feat(19.7-01): account contributions, prompted record actions and backend subject events

- pact.HasAccountContributions compiled at Activate; GET /account/contributions
  and POST /account/contributions/{name}/actions/{action} behind interactiveOnly
  (AccountContributionView, AccountContributionActionView, AccountActionResultView)
- RecordAction.prompt (RecordActionPrompt) for a pact.AdminRecordAction with a
  Prompt; the record action route accepts payload only for such an action
  (trimmed JSON string, non-empty, capped at max_length) and hands it to Run as
  Value through CRUDService.RecordActionWithValue; a prompt without a label
  fails boot
- fireSubjectChanged: pact.MCPSubjectChanged for revoked tokens, Administrators
  saves that changed role, permissions or superuser, Roles saves that changed
  grants, deactivation and deletion
- phrases backend::lang.form.action_prompt_required/_too_long (en, pl)
- smoke tests TestAccountContributionsSmoke and TestRecordActionPromptSmoke;
  TestRecordActionPromptRun on Postgres; route inventories, CSRF walk and
  OpenAPI conformance cases extended for the two routes
- regenerated admin/openapi/admin.json and admin/src/api/schema.d.ts; cabana
  README, docs/backend/{admin-controllers,users-and-permissions}.md
This commit is contained in:
Jakub Zych
2026-10-22 16:31:29 +02:00
parent 6887abf575
commit 9f14cf603c
28 changed files with 1431 additions and 20 deletions

View File

@@ -330,6 +330,67 @@ func AdminAccountTokenCreate() {}
// @Router /account/tokens/{id} [delete]
func AdminAccountTokenRevoke() {}
// AccountContributionView is one section a plugin contributes to My account
// (pact.AccountContribution), for the signed-in admin: translated label,
// description and status, and the actions they may run that apply to them.
type AccountContributionView struct {
Name string `json:"name"`
Label string `json:"label"`
Description string `json:"description"`
// Status is the section's line for the signed-in admin; "" when the
// section has none.
Status string `json:"status"`
Actions []AccountContributionActionView `json:"actions"`
}
// AccountContributionActionView is one button of a contributed section: the
// name posted to .../actions/{action}, its translated label, an optional
// confirmation text and whether the confirm dialog takes the danger variant.
type AccountContributionActionView struct {
Name string `json:"name"`
Label string `json:"label"`
Confirm string `json:"confirm"`
Danger bool `json:"danger"`
}
// AccountActionResultView is an account action's answer: a translated
// message for the toast and an optional URL the admin opens in a new tab.
type AccountActionResultView struct {
Message string `json:"message"`
URL string `json:"url"`
}
// AdminAccountContributions documents GET /account/contributions.
//
// @Summary List My account contributions
// @Description Lists the sections plugins contribute to the signed-in admin's My account page: label, description, the status for this admin and the actions they may run that apply to them. Empty when no plugin contributes. Personal API tokens are refused (403 interactive_only).
// @Tags admin
// @Produce json
// @Security BackendBearer
// @Success 200 {object} Envelope[[]AccountContributionView]
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Router /account/contributions [get]
func AdminAccountContributions() {}
// AdminAccountContributionAction documents POST /account/contributions/{name}/actions/{action}.
//
// @Summary Run a My account contribution action
// @Description Runs one action of a contributed section for the signed-in admin. The action's permissions are checked (403) and its Applies rule again (409 when it no longer applies); the answer is the action's message and an optional URL to open. Personal API tokens are refused (403 interactive_only).
// @Tags admin
// @Produce json
// @Security BackendBearer
// @Param name path string true "Section name"
// @Param action path string true "Action name"
// @Success 200 {object} Envelope[AccountActionResultView]
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 404 {object} ErrorEnvelope
// @Failure 409 {object} ErrorEnvelope
// @Failure 422 {object} ErrorEnvelope
// @Router /account/contributions/{name}/actions/{action} [post]
func AdminAccountContributionAction() {}
// AdminAccountTokenScopes documents GET /account/tokens/scopes.
//
// @Summary List available API token scopes