feat(10.1-01): run registered widget actions through a cabana-owned route

- pact: AdminClientAssets, AdminAction, AdminActionInput, AdminActionResult,
  HasAdminActions and AdminPartialData contracts
- fields.yaml type: widget with widget, action and fill keys; boot checks the
  plugin tag prefix, the registered action and writable scalar fill fields
- POST .../widgets/{field} behind requireAjax, controller and action
  permissions, scoped non-locking record read and a server-side fill filter
- typed OpenAPI operation, inventories and an acme conformance case
This commit is contained in:
Jakub Zych
2026-09-28 23:35:00 +02:00
parent 9b98d8409f
commit f9281949a6
19 changed files with 876 additions and 10 deletions

View File

@@ -218,6 +218,11 @@ func (s *service) mount(r pact.Router) {
constrainController(g)
g.Post("/{vendor}/{plugin}/{controller}/bulk-delete", requireAjax(s.bulkDelete))
constrainController(g)
// Runtime extension actions (Phase 10.1): cabana owns these routes, so
// CSRF, auth and record scoping never depend on plugin code.
g.Post("/{vendor}/{plugin}/{controller}/widgets/{field}", requireAjax(s.widgetAction))
constrainController(g)
g.Where("field", "[A-Za-z_][A-Za-z0-9_]*")
g.Get("/{vendor}/{plugin}/{controller}/{id}", s.show)
constrainController(g)
g.Put("/{vendor}/{plugin}/{controller}/{id}", requireAjax(s.update))