feat(16.3-01): account token API with OpenAPI and phrase keys

- Add list/create/revoke/scopes handlers, validation, and soft revoke
- Regenerate admin OpenAPI and TS types; inventory and conformance cases
- Add server-side api_tokens phrase keys (en/pl)

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-11 01:51:11 +02:00
parent 183583e0e9
commit ffdb5d00ca
13 changed files with 1430 additions and 49 deletions

View File

@@ -7,7 +7,7 @@ package cabana
// @securityDefinitions.apikey BackendBearer
// @in header
// @name Authorization
// @description Backend admin bearer token. Send "Bearer {access_token}".
// @description Backend admin access token or personal API token (g15_…). Send "Bearer {token}". Account and token management routes refuse personal API tokens.
// Admin API annotations. scripts/check-admin-openapi.sh reads them with swag
// to produce admin/openapi/admin.json, the document the SPA's TypeScript types
@@ -204,7 +204,7 @@ func AdminMe() {}
// AdminAccountUpdate documents PUT /account.
//
// @Summary Update my account
// @Description Saves the signed-in admin's own first name, last name, email and subtitle; the row is the token subject, never a request value. Only the AdminAccountRequest keys are accepted: any other key (login, role_id, is_superuser, permissions, is_activated, id) is a 422 on that key and nothing is written. The email must be valid, 6 to 255 characters, and not equal (ignoring case) the email or login of another admin. A non-empty password needs current_password, at least 4 characters, at most 72 bytes and an equal password_confirmation; it ends every other session, and a cookie session receives a fresh cookie.
// @Description Saves the signed-in admin's own first name, last name, email and subtitle; the row is the token subject, never a request value. Only the AdminAccountRequest keys are accepted: any other key (login, role_id, is_superuser, permissions, is_activated, id) is a 422 on that key and nothing is written. The email must be valid, 6 to 255 characters, and not equal (ignoring case) the email or login of another admin. A non-empty password needs current_password, at least 4 characters, at most 72 bytes and an equal password_confirmation; it ends every other session, and a cookie session receives a fresh cookie. Personal API tokens are refused (403 interactive_only).
// @Tags admin
// @Accept json
// @Produce json
@@ -218,6 +218,64 @@ func AdminMe() {}
// @Router /account [put]
func AdminAccountUpdate() {}
// AdminAccountTokens documents GET /account/tokens.
//
// @Summary List my API tokens
// @Description Lists the signed-in admin's personal API tokens, newest first. Revoked tokens are hidden unless revoked=1. Personal API tokens are refused (403 interactive_only).
// @Tags admin
// @Produce json
// @Security BackendBearer
// @Param revoked query string false "Include revoked tokens when 1 or true"
// @Success 200 {object} Envelope[[]AdminAPIToken]
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Router /account/tokens [get]
func AdminAccountTokens() {}
// AdminAccountTokenCreate documents POST /account/tokens.
//
// @Summary Create an API token
// @Description Creates a personal API token for the signed-in admin. The plaintext token is returned exactly once in the 201 body. Personal API tokens are refused (403 interactive_only).
// @Tags admin
// @Accept json
// @Produce json
// @Security BackendBearer
// @Param body body AdminAPITokenRequest true "Name, expiry and scopes"
// @Success 201 {object} Envelope[AdminAPITokenCreated]
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 422 {object} ErrorEnvelope
// @Router /account/tokens [post]
func AdminAccountTokenCreate() {}
// AdminAccountTokenRevoke documents DELETE /account/tokens/{id}.
//
// @Summary Revoke an API token
// @Description Soft-revokes one of the signed-in admin's active personal API tokens. Personal API tokens are refused (403 interactive_only).
// @Tags admin
// @Produce json
// @Security BackendBearer
// @Param id path int true "Token id"
// @Success 200 {object} Envelope[AdminAPIToken]
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Failure 404 {object} ErrorEnvelope
// @Router /account/tokens/{id} [delete]
func AdminAccountTokenRevoke() {}
// AdminAccountTokenScopes documents GET /account/tokens/scopes.
//
// @Summary List available API token scopes
// @Description Lists framework and plugin scopes the signed-in admin may pick when creating a token. Personal API tokens are refused (403 interactive_only).
// @Tags admin
// @Produce json
// @Security BackendBearer
// @Success 200 {object} Envelope[[]AdminAPITokenScope]
// @Failure 401 {object} ErrorEnvelope
// @Failure 403 {object} ErrorEnvelope
// @Router /account/tokens/scopes [get]
func AdminAccountTokenScopes() {}
// AdminAccountAvatarUpload documents POST /account/avatar.
//
// @Summary Upload my avatar