@adonis-agora/authkit-server 0.46.0 → 0.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/commands/clients_create.js +4 -4
- package/build/commands/configure.js +3 -2
- package/build/commands/doctor.js +8 -5
- package/build/commands/eject.js +1 -1
- package/build/commands/expire_scan.js +7 -5
- package/build/commands/import_users.js +1 -1
- package/build/commands/keys_rotate.js +7 -4
- package/build/host/views/account/apps.edge +5 -5
- package/build/host/views/account/login.edge +1 -1
- package/build/host/views/account/mfa.edge +7 -7
- package/build/host/views/account/orgs.edge +5 -5
- package/build/host/views/account/security.edge +10 -10
- package/build/host/views/account/tokens.edge +5 -5
- package/build/host/views/otp-unlock.edge +2 -2
- package/build/index.d.ts +7 -7
- package/build/index.js +5 -5
- package/build/providers/authkit_server_provider.d.ts +2 -2
- package/build/providers/authkit_server_provider.js +26 -10
- package/build/services/booted_app.d.ts +8 -0
- package/build/services/booted_app.js +27 -0
- package/build/services/main.d.ts +8 -1
- package/build/services/main.js +10 -2
- package/build/src/accounts/lucid_account_store.d.ts +3 -3
- package/build/src/accounts/lucid_account_store.js +6 -8
- package/build/src/accounts/lucid_store/core.js +1 -1
- package/build/src/accounts/lucid_store/mfa.js +1 -1
- package/build/src/accounts/lucid_store/organizations.js +2 -4
- package/build/src/accounts/lucid_store/password_hygiene.js +1 -4
- package/build/src/accounts/lucid_store/shared.d.ts +1 -1
- package/build/src/accounts/lucid_stores.d.ts +3 -3
- package/build/src/accounts/lucid_stores.js +2 -2
- package/build/src/adapters/database_adapter.js +3 -1
- package/build/src/adapters/factory.js +1 -1
- package/build/src/adapters/redis_adapter.js +2 -1
- package/build/src/commands/expire_scan_command.js +19 -5
- package/build/src/commands/settings_commands.js +22 -5
- package/build/src/define_config.d.ts +20 -20
- package/build/src/define_config.js +62 -67
- package/build/src/doctor/checks.js +45 -20
- package/build/src/events/dispatcher.d.ts +1 -1
- package/build/src/events/dispatcher.js +10 -10
- package/build/src/host/account_api/account_api_controller.js +95 -39
- package/build/src/host/account_deletion_ops.d.ts +4 -4
- package/build/src/host/account_deletion_ops.js +5 -5
- package/build/src/host/account_deletion_service.d.ts +2 -2
- package/build/src/host/account_deletion_service.js +2 -2
- package/build/src/host/account_export_service.d.ts +1 -1
- package/build/src/host/account_export_service.js +4 -6
- package/build/src/host/account_home.d.ts +0 -8
- package/build/src/host/account_home.js +6 -4
- package/build/src/host/account_login_url.d.ts +44 -0
- package/build/src/host/account_login_url.js +58 -0
- package/build/src/host/account_paths.d.ts +120 -0
- package/build/src/host/account_paths.js +161 -0
- package/build/src/host/admin_api/admin_orgs_service.d.ts +2 -2
- package/build/src/host/admin_api/admin_orgs_service.js +3 -2
- package/build/src/host/admin_api/admin_users_service.d.ts +12 -12
- package/build/src/host/admin_api/admin_users_service.js +20 -20
- package/build/src/host/admin_api/api_clients_controller.js +2 -2
- package/build/src/host/admin_api/api_misc_controller.js +2 -2
- package/build/src/host/admin_api/api_orgs_controller.js +3 -3
- package/build/src/host/admin_api/api_settings_controller.js +11 -5
- package/build/src/host/admin_api/api_users_controller.d.ts +2 -2
- package/build/src/host/admin_api/api_users_controller.js +37 -38
- package/build/src/host/admin_api/dto.d.ts +4 -4
- package/build/src/host/admin_clients_service.js +5 -4
- package/build/src/host/admin_console/admin_shell_controller.js +4 -2
- package/build/src/host/admin_console/console_audit_controller.js +1 -1
- package/build/src/host/admin_console/console_clients_controller.js +5 -3
- package/build/src/host/admin_console/console_impersonation_controller.js +1 -1
- package/build/src/host/admin_console/console_keys_controller.js +2 -4
- package/build/src/host/admin_console/console_orgs_controller.js +3 -3
- package/build/src/host/admin_console/console_roles_controller.js +30 -12
- package/build/src/host/admin_console/console_sessions_controller.js +3 -3
- package/build/src/host/admin_console/console_settings_controller.js +11 -5
- package/build/src/host/admin_console/console_users_controller.d.ts +2 -2
- package/build/src/host/admin_console/console_users_controller.js +46 -47
- package/build/src/host/admin_prefix.js +2 -2
- package/build/src/host/admin_sessions_service.d.ts +3 -1
- package/build/src/host/admin_sessions_service.js +36 -6
- package/build/src/host/adonis_auth_sync.d.ts +1 -1
- package/build/src/host/adonis_auth_user_provider.d.ts +1 -1
- package/build/src/host/app_key.js +1 -1
- package/build/src/host/avatar_storage.js +1 -3
- package/build/src/host/config_locks.js +1 -3
- package/build/src/host/console_session.d.ts +4 -0
- package/build/src/host/console_session.js +9 -2
- package/build/src/host/controllers/account_apps_controller.js +3 -2
- package/build/src/host/controllers/account_confirm_controller.js +2 -6
- package/build/src/host/controllers/account_mfa_controller.js +63 -12
- package/build/src/host/controllers/account_orgs_controller.js +53 -30
- package/build/src/host/controllers/account_security_controller.d.ts +2 -2
- package/build/src/host/controllers/account_security_controller.js +116 -116
- package/build/src/host/controllers/account_session_controller.js +7 -5
- package/build/src/host/controllers/account_tokens_controller.js +14 -2
- package/build/src/host/controllers/interaction_controller.js +59 -22
- package/build/src/host/controllers/pat_introspection_controller.js +10 -0
- package/build/src/host/controllers/registration_controller.js +28 -13
- package/build/src/host/default_mailer.js +11 -7
- package/build/src/host/durable/account_deletion_workflow.d.ts +2 -2
- package/build/src/host/durable/account_deletion_workflow.js +14 -14
- package/build/src/host/durable/account_export_workflow.d.ts +2 -2
- package/build/src/host/durable/account_export_workflow.js +12 -12
- package/build/src/host/durable/index.d.ts +5 -5
- package/build/src/host/durable/index.js +6 -6
- package/build/src/host/i18n.d.ts +1376 -1376
- package/build/src/host/i18n.js +1380 -1380
- package/build/src/host/impersonation.js +1 -7
- package/build/src/host/key_rotation_actions.d.ts +1 -1
- package/build/src/host/login_attempt.js +35 -6
- package/build/src/host/login_notify.js +1 -1
- package/build/src/host/middleware/account_auth.js +3 -1
- package/build/src/host/otp_lockout.js +1 -3
- package/build/src/host/rate_limit.js +1 -1
- package/build/src/host/register_auth_host.d.ts +96 -2
- package/build/src/host/register_auth_host.js +196 -91
- package/build/src/host/renderers/edge_renderer.js +19 -1
- package/build/src/host/renderers/inertia_renderer.d.ts +63 -0
- package/build/src/host/renderers/inertia_renderer.js +63 -0
- package/build/src/host/runtime_settings.js +1 -1
- package/build/src/host/runtime_toggles.d.ts +3 -3
- package/build/src/host/runtime_toggles.js +69 -24
- package/build/src/host/security_notice_service.d.ts +1 -1
- package/build/src/host/security_notice_service.js +1 -1
- package/build/src/host/session_context.js +1 -1
- package/build/src/host/sudo/index.d.ts +3 -3
- package/build/src/host/sudo/index.js +3 -3
- package/build/src/host/sudo/methods/magic_link.js +6 -5
- package/build/src/host/sudo/methods/passkey.js +7 -4
- package/build/src/host/sudo/methods/password.js +8 -3
- package/build/src/host/sudo/runtime.js +6 -13
- package/build/src/host/sudo_mode.js +3 -2
- package/build/src/host/ui-dist/assets/index-BQoTDiLV.js +137 -0
- package/build/src/host/ui-dist/index.html +1 -1
- package/build/src/keys/jwks_manager.js +1 -1
- package/build/src/keys/keystore.js +1 -1
- package/build/src/keys/keystore_manager.d.ts +2 -2
- package/build/src/keys/keystore_manager.js +2 -2
- package/build/src/keys/keystore_vault.js +8 -4
- package/build/src/mixins/with_audit_log.d.ts +1 -1
- package/build/src/mixins/with_auth_user.d.ts +1 -1
- package/build/src/mixins/with_auth_user.js +1 -1
- package/build/src/mixins/with_credentials.d.ts +1 -1
- package/build/src/mixins/with_mfa.d.ts +1 -1
- package/build/src/mixins/with_personal_access_token.d.ts +1 -1
- package/build/src/mixins/with_provider_identity.d.ts +1 -1
- package/build/src/mixins/with_webauthn_credential.d.ts +1 -1
- package/build/src/observability/diagnostics_bridge.js +2 -2
- package/build/src/observability/metrics_service.js +1 -1
- package/build/src/observability/telescope/data_providers.d.ts +1 -1
- package/build/src/observability/telescope/data_providers.js +29 -43
- package/build/src/observability/telescope/extension.d.ts +1 -1
- package/build/src/observability/telescope/extension.js +58 -58
- package/build/src/observability/telescope/index.d.ts +2 -2
- package/build/src/observability/telescope/index.js +2 -2
- package/build/src/observability/wire_provider_events.d.ts +1 -1
- package/build/src/observability/wire_provider_events.js +11 -11
- package/build/src/password/common_passwords.js +1 -1
- package/build/src/password/password_manager.d.ts +1 -1
- package/build/src/password/password_manager.js +4 -4
- package/build/src/pat/lucid_pat_store.js +2 -6
- package/build/src/pat/pat_tokens.js +1 -1
- package/build/src/provider/interaction_actions.js +1 -1
- package/build/src/provider/key_rotation_scheduler.js +3 -1
- package/build/src/provider/logout_sources.js +2 -8
- package/build/src/provider/oidc_service.d.ts +2 -2
- package/build/src/provider/oidc_service.js +22 -14
- package/build/src/provider/single_flight_lock.js +1 -3
- package/build/src/provider/token_exchange.d.ts +1 -1
- package/build/src/schema/ensure.js +3 -1
- package/build/stubs/ui/react/components/auth_shell.tsx +19 -19
- package/build/stubs/ui/react/pages/account/login.tsx +6 -7
- package/build/stubs/ui/react/pages/account/mfa.tsx +16 -15
- package/build/stubs/ui/react/pages/account/tokens.tsx +27 -17
- package/build/stubs/ui/react/pages/consent.tsx +12 -12
- package/build/stubs/ui/react/pages/forgot.tsx +7 -7
- package/build/stubs/ui/react/pages/login.tsx +22 -24
- package/build/stubs/ui/react/pages/mfa-challenge.tsx +9 -10
- package/build/stubs/ui/react/pages/reset.tsx +10 -10
- package/build/stubs/ui/react/pages/signup.tsx +30 -16
- package/build/stubs/ui/react/pages/verify-email.tsx +6 -6
- package/package.json +1 -1
- package/stubs/main.ts +2 -2
- package/stubs/ui/react/components/auth_shell.tsx +19 -19
- package/stubs/ui/react/pages/account/login.tsx +6 -7
- package/stubs/ui/react/pages/account/mfa.tsx +16 -15
- package/stubs/ui/react/pages/account/tokens.tsx +27 -17
- package/stubs/ui/react/pages/consent.tsx +12 -12
- package/stubs/ui/react/pages/forgot.tsx +7 -7
- package/stubs/ui/react/pages/login.tsx +22 -24
- package/stubs/ui/react/pages/mfa-challenge.tsx +9 -10
- package/stubs/ui/react/pages/reset.tsx +10 -10
- package/stubs/ui/react/pages/signup.tsx +30 -16
- package/stubs/ui/react/pages/verify-email.tsx +6 -6
- package/build/src/host/ui-dist/assets/index-BmI_J87g.js +0 -137
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prefixo e segmentos de tela do console de conta (`/account/*`) — singleton de
|
|
3
|
+
* processo, configurável/localizável.
|
|
4
|
+
*
|
|
5
|
+
* Definidos em tempo de registro das rotas (`registerAuthHost`, via a opção
|
|
6
|
+
* `accountRoutes`) e lidos em runtime por TODOS os pontos que constroem uma URL
|
|
7
|
+
* do console de conta: o registro de rotas, os redirects dos controllers, as
|
|
8
|
+
* URLs absolutas dos e-mails transacionais, os guards de sudo e as views Edge
|
|
9
|
+
* (injetados como prop global `accountPaths` pelo renderer).
|
|
10
|
+
*
|
|
11
|
+
* Por que módulo singleton e não binding do container? Mesma razão do
|
|
12
|
+
* `admin_prefix` e do `account_login_url`: os guards e o registro de rotas são
|
|
13
|
+
* closures construídas em tempo de registro, ANTES da primeira request, quando
|
|
14
|
+
* o container do AdonisJS ainda não existe. Um módulo ESM é inicializado uma vez
|
|
15
|
+
* por processo — ideal para configuração imutável de boot.
|
|
16
|
+
*
|
|
17
|
+
* ── O que é configurável e o que NÃO é ──────────────────────────────────────
|
|
18
|
+
* Configuráveis: o PREFIXO (`/account` → `/conta`) e o SEGMENTO de cada TELA
|
|
19
|
+
* navegável (`security` → `seguranca`, `confirm` → `confirmar`, ...). São paths
|
|
20
|
+
* que o usuário vê na barra de endereço e que um host pode querer no idioma dele.
|
|
21
|
+
*
|
|
22
|
+
* NÃO configuráveis (de propósito): os action-subpaths dos POSTs de dentro de
|
|
23
|
+
* cada tela — `/password`, `/email`, `/enroll`, `/confirm`, `/disable`,
|
|
24
|
+
* `/passkeys/options`, `/passkeys/verify`, `/delete`, `/export`, o `/magic-link`
|
|
25
|
+
* e o `/passkey` das rotas de sudo, etc. São endpoints de formulário invisíveis
|
|
26
|
+
* ao usuário (ele nunca digita `/account/security/password`), servem apenas de
|
|
27
|
+
* alvo de `<form action>`/`fetch`, e mantê-los fixos evita multiplicar a
|
|
28
|
+
* superfície de configuração (e a de bugs) sem nenhum ganho de UX. Renomear a
|
|
29
|
+
* tela já cobre o caso real "quero /conta/seguranca". Idem para o segmento fixo
|
|
30
|
+
* `api` da JSON API (`{prefix}/api/*`): ele segue o prefixo mas nunca é
|
|
31
|
+
* traduzido — é contrato de máquina (authkit-react), não de humano.
|
|
32
|
+
*/
|
|
33
|
+
/**
|
|
34
|
+
* Segmentos default de cada TELA navegável do console de conta. A chave é o
|
|
35
|
+
* identificador estável usado no código (`accountPath('security')`); o valor é
|
|
36
|
+
* o segmento de URL, sobrescrevível por `setAccountPaths`.
|
|
37
|
+
*/
|
|
38
|
+
declare const DEFAULT_ACCOUNT_SEGMENTS: {
|
|
39
|
+
readonly login: "login";
|
|
40
|
+
readonly logout: "logout";
|
|
41
|
+
readonly security: "security";
|
|
42
|
+
readonly mfa: "mfa";
|
|
43
|
+
readonly confirm: "confirm";
|
|
44
|
+
readonly tokens: "tokens";
|
|
45
|
+
readonly apps: "apps";
|
|
46
|
+
readonly orgs: "orgs";
|
|
47
|
+
readonly emailConfirm: "email/confirm";
|
|
48
|
+
};
|
|
49
|
+
/** Chave de tela do console de conta (`login`, `security`, `confirm`, ...). */
|
|
50
|
+
export type AccountPathKey = keyof typeof DEFAULT_ACCOUNT_SEGMENTS;
|
|
51
|
+
/** Overrides aceitos por `setAccountPaths` / a opção `accountRoutes`. */
|
|
52
|
+
export interface AccountPathsOptions {
|
|
53
|
+
/** Prefixo base do console (ex.: `'/conta'`). Normalizado automaticamente. */
|
|
54
|
+
prefix?: string;
|
|
55
|
+
/** Segmentos de tela a sobrescrever (ex.: `{ security: 'seguranca' }`). */
|
|
56
|
+
paths?: Partial<Record<AccountPathKey, string>>;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Normaliza o prefixo: começa com '/', sem trailing slash. Mesma semântica de
|
|
60
|
+
* `normalizeAdminPrefix`. `'/'` → `'/'` (raiz) é improvável mas seguro.
|
|
61
|
+
*/
|
|
62
|
+
export declare function normalizeAccountPrefix(raw: string): string;
|
|
63
|
+
/**
|
|
64
|
+
* Define o prefixo e/ou os segmentos de tela do console de conta para este
|
|
65
|
+
* processo. Chamado UMA VEZ por `registerAuthHost` no boot da aplicação.
|
|
66
|
+
*
|
|
67
|
+
* Só sobrescreve o que foi passado — chamada sem `prefix` mantém o prefixo
|
|
68
|
+
* atual, e segmentos ausentes de `paths` mantêm o default. Valores
|
|
69
|
+
* vazios/whitespace são ignorados (caem no default), nunca produzem um path
|
|
70
|
+
* quebrado.
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* setAccountPaths({ prefix: '/conta', paths: { security: 'seguranca', confirm: 'confirmar' } })
|
|
74
|
+
* accountPath('security') // → '/conta/seguranca'
|
|
75
|
+
* accountPath('confirm') // → '/conta/confirmar'
|
|
76
|
+
*/
|
|
77
|
+
export declare function setAccountPaths(opts: AccountPathsOptions | undefined): void;
|
|
78
|
+
/**
|
|
79
|
+
* Junta o prefixo do console de conta com um subpath arbitrário, tratando o
|
|
80
|
+
* prefixo raiz (`'/'`) como `''` na composição: sem isso o resultado seria
|
|
81
|
+
* `'//<sub>'`, que num `<form action>`/`fetch()` é URL protocol-relative (o
|
|
82
|
+
* browser lê `<sub>` como HOST, não como path).
|
|
83
|
+
*
|
|
84
|
+
* Base canônica de `accountPath()` e de QUALQUER outra composição
|
|
85
|
+
* prefixo+subpath fora das TELAS fixas (ex.: o `{prefix}/api` da JSON API em
|
|
86
|
+
* `register_auth_host`). Novas composições devem passar por aqui em vez de
|
|
87
|
+
* reimplementar `${accountPrefix()}/algo` — é exatamente essa duplicação que
|
|
88
|
+
* reproduz o bug do `//` quando o prefixo normaliza pra raiz.
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* joinAccountPath('api') // default → '/account/api'
|
|
92
|
+
*/
|
|
93
|
+
export declare function joinAccountPath(sub: string): string;
|
|
94
|
+
/**
|
|
95
|
+
* Retorna o path completo de uma TELA do console de conta (prefixo + segmento).
|
|
96
|
+
* Para os action-subpaths (POSTs de dentro da tela) concatene o subpath fixo:
|
|
97
|
+
* `accountPath('security') + '/password'`.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* accountPath('security') // default → '/account/security'
|
|
101
|
+
* accountPath('confirm') // default → '/account/confirm'
|
|
102
|
+
*/
|
|
103
|
+
export declare function accountPath(key: AccountPathKey): string;
|
|
104
|
+
/**
|
|
105
|
+
* Retorna o prefixo BRUTO do console de conta (default `'/account'`; pode ser
|
|
106
|
+
* `'/'` na raiz). Para compor `{prefix}/algo` (ex.: a JSON API, cujo segmento
|
|
107
|
+
* `api` é fixo — contrato de máquina), use `joinAccountPath('algo')`: ela
|
|
108
|
+
* colapsa a raiz e evita o `//`. Este getter existe para os casos (ex.: os
|
|
109
|
+
* asserts de teste) que precisam do valor cru do prefixo.
|
|
110
|
+
*/
|
|
111
|
+
export declare function accountPrefix(): string;
|
|
112
|
+
/**
|
|
113
|
+
* Mapa `{ chave → path completo }` de TODAS as telas. Injetado como prop global
|
|
114
|
+
* `accountPaths` nas views Edge, para que os `<form action>`/`fetch()` respeitem
|
|
115
|
+
* os overrides sem cada view conhecer o prefixo.
|
|
116
|
+
*/
|
|
117
|
+
export declare function accountPathsMap(): Record<AccountPathKey, string>;
|
|
118
|
+
/** Restaura os defaults — uso em testes (isola o singleton entre casos). */
|
|
119
|
+
export declare function resetAccountPaths(): void;
|
|
120
|
+
export {};
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prefixo e segmentos de tela do console de conta (`/account/*`) — singleton de
|
|
3
|
+
* processo, configurável/localizável.
|
|
4
|
+
*
|
|
5
|
+
* Definidos em tempo de registro das rotas (`registerAuthHost`, via a opção
|
|
6
|
+
* `accountRoutes`) e lidos em runtime por TODOS os pontos que constroem uma URL
|
|
7
|
+
* do console de conta: o registro de rotas, os redirects dos controllers, as
|
|
8
|
+
* URLs absolutas dos e-mails transacionais, os guards de sudo e as views Edge
|
|
9
|
+
* (injetados como prop global `accountPaths` pelo renderer).
|
|
10
|
+
*
|
|
11
|
+
* Por que módulo singleton e não binding do container? Mesma razão do
|
|
12
|
+
* `admin_prefix` e do `account_login_url`: os guards e o registro de rotas são
|
|
13
|
+
* closures construídas em tempo de registro, ANTES da primeira request, quando
|
|
14
|
+
* o container do AdonisJS ainda não existe. Um módulo ESM é inicializado uma vez
|
|
15
|
+
* por processo — ideal para configuração imutável de boot.
|
|
16
|
+
*
|
|
17
|
+
* ── O que é configurável e o que NÃO é ──────────────────────────────────────
|
|
18
|
+
* Configuráveis: o PREFIXO (`/account` → `/conta`) e o SEGMENTO de cada TELA
|
|
19
|
+
* navegável (`security` → `seguranca`, `confirm` → `confirmar`, ...). São paths
|
|
20
|
+
* que o usuário vê na barra de endereço e que um host pode querer no idioma dele.
|
|
21
|
+
*
|
|
22
|
+
* NÃO configuráveis (de propósito): os action-subpaths dos POSTs de dentro de
|
|
23
|
+
* cada tela — `/password`, `/email`, `/enroll`, `/confirm`, `/disable`,
|
|
24
|
+
* `/passkeys/options`, `/passkeys/verify`, `/delete`, `/export`, o `/magic-link`
|
|
25
|
+
* e o `/passkey` das rotas de sudo, etc. São endpoints de formulário invisíveis
|
|
26
|
+
* ao usuário (ele nunca digita `/account/security/password`), servem apenas de
|
|
27
|
+
* alvo de `<form action>`/`fetch`, e mantê-los fixos evita multiplicar a
|
|
28
|
+
* superfície de configuração (e a de bugs) sem nenhum ganho de UX. Renomear a
|
|
29
|
+
* tela já cobre o caso real "quero /conta/seguranca". Idem para o segmento fixo
|
|
30
|
+
* `api` da JSON API (`{prefix}/api/*`): ele segue o prefixo mas nunca é
|
|
31
|
+
* traduzido — é contrato de máquina (authkit-react), não de humano.
|
|
32
|
+
*/
|
|
33
|
+
/** Prefixo default do console de conta. */
|
|
34
|
+
const DEFAULT_ACCOUNT_PREFIX = '/account';
|
|
35
|
+
/**
|
|
36
|
+
* Segmentos default de cada TELA navegável do console de conta. A chave é o
|
|
37
|
+
* identificador estável usado no código (`accountPath('security')`); o valor é
|
|
38
|
+
* o segmento de URL, sobrescrevível por `setAccountPaths`.
|
|
39
|
+
*/
|
|
40
|
+
const DEFAULT_ACCOUNT_SEGMENTS = {
|
|
41
|
+
login: 'login',
|
|
42
|
+
logout: 'logout',
|
|
43
|
+
security: 'security',
|
|
44
|
+
mfa: 'mfa',
|
|
45
|
+
confirm: 'confirm',
|
|
46
|
+
tokens: 'tokens',
|
|
47
|
+
apps: 'apps',
|
|
48
|
+
orgs: 'orgs',
|
|
49
|
+
emailConfirm: 'email/confirm',
|
|
50
|
+
};
|
|
51
|
+
let _prefix = DEFAULT_ACCOUNT_PREFIX;
|
|
52
|
+
let _segments = { ...DEFAULT_ACCOUNT_SEGMENTS };
|
|
53
|
+
/**
|
|
54
|
+
* Normaliza o prefixo: começa com '/', sem trailing slash. Mesma semântica de
|
|
55
|
+
* `normalizeAdminPrefix`. `'/'` → `'/'` (raiz) é improvável mas seguro.
|
|
56
|
+
*/
|
|
57
|
+
export function normalizeAccountPrefix(raw) {
|
|
58
|
+
let p = raw.trim();
|
|
59
|
+
if (!p.startsWith('/'))
|
|
60
|
+
p = `/${p}`;
|
|
61
|
+
p = p.replace(/\/+$/, '');
|
|
62
|
+
return p || '/';
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Normaliza um segmento de tela: sem barras nas pontas (mas preserva barras
|
|
66
|
+
* internas, ex.: `'email/confirm'`). Valor vazio é ignorado pelo caller.
|
|
67
|
+
*/
|
|
68
|
+
function normalizeSegment(raw) {
|
|
69
|
+
return raw.trim().replace(/^\/+/, '').replace(/\/+$/, '');
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Define o prefixo e/ou os segmentos de tela do console de conta para este
|
|
73
|
+
* processo. Chamado UMA VEZ por `registerAuthHost` no boot da aplicação.
|
|
74
|
+
*
|
|
75
|
+
* Só sobrescreve o que foi passado — chamada sem `prefix` mantém o prefixo
|
|
76
|
+
* atual, e segmentos ausentes de `paths` mantêm o default. Valores
|
|
77
|
+
* vazios/whitespace são ignorados (caem no default), nunca produzem um path
|
|
78
|
+
* quebrado.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* setAccountPaths({ prefix: '/conta', paths: { security: 'seguranca', confirm: 'confirmar' } })
|
|
82
|
+
* accountPath('security') // → '/conta/seguranca'
|
|
83
|
+
* accountPath('confirm') // → '/conta/confirmar'
|
|
84
|
+
*/
|
|
85
|
+
export function setAccountPaths(opts) {
|
|
86
|
+
if (!opts)
|
|
87
|
+
return;
|
|
88
|
+
if (opts.prefix !== undefined) {
|
|
89
|
+
const trimmed = opts.prefix.trim();
|
|
90
|
+
if (trimmed)
|
|
91
|
+
_prefix = normalizeAccountPrefix(trimmed);
|
|
92
|
+
}
|
|
93
|
+
if (opts.paths) {
|
|
94
|
+
for (const key of Object.keys(opts.paths)) {
|
|
95
|
+
const value = opts.paths[key];
|
|
96
|
+
if (value === undefined)
|
|
97
|
+
continue;
|
|
98
|
+
const seg = normalizeSegment(value);
|
|
99
|
+
if (seg)
|
|
100
|
+
_segments[key] = seg;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Junta o prefixo do console de conta com um subpath arbitrário, tratando o
|
|
106
|
+
* prefixo raiz (`'/'`) como `''` na composição: sem isso o resultado seria
|
|
107
|
+
* `'//<sub>'`, que num `<form action>`/`fetch()` é URL protocol-relative (o
|
|
108
|
+
* browser lê `<sub>` como HOST, não como path).
|
|
109
|
+
*
|
|
110
|
+
* Base canônica de `accountPath()` e de QUALQUER outra composição
|
|
111
|
+
* prefixo+subpath fora das TELAS fixas (ex.: o `{prefix}/api` da JSON API em
|
|
112
|
+
* `register_auth_host`). Novas composições devem passar por aqui em vez de
|
|
113
|
+
* reimplementar `${accountPrefix()}/algo` — é exatamente essa duplicação que
|
|
114
|
+
* reproduz o bug do `//` quando o prefixo normaliza pra raiz.
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* joinAccountPath('api') // default → '/account/api'
|
|
118
|
+
*/
|
|
119
|
+
export function joinAccountPath(sub) {
|
|
120
|
+
const base = _prefix === '/' ? '' : _prefix;
|
|
121
|
+
return `${base}/${sub}`;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Retorna o path completo de uma TELA do console de conta (prefixo + segmento).
|
|
125
|
+
* Para os action-subpaths (POSTs de dentro da tela) concatene o subpath fixo:
|
|
126
|
+
* `accountPath('security') + '/password'`.
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* accountPath('security') // default → '/account/security'
|
|
130
|
+
* accountPath('confirm') // default → '/account/confirm'
|
|
131
|
+
*/
|
|
132
|
+
export function accountPath(key) {
|
|
133
|
+
return joinAccountPath(_segments[key]);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Retorna o prefixo BRUTO do console de conta (default `'/account'`; pode ser
|
|
137
|
+
* `'/'` na raiz). Para compor `{prefix}/algo` (ex.: a JSON API, cujo segmento
|
|
138
|
+
* `api` é fixo — contrato de máquina), use `joinAccountPath('algo')`: ela
|
|
139
|
+
* colapsa a raiz e evita o `//`. Este getter existe para os casos (ex.: os
|
|
140
|
+
* asserts de teste) que precisam do valor cru do prefixo.
|
|
141
|
+
*/
|
|
142
|
+
export function accountPrefix() {
|
|
143
|
+
return _prefix;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Mapa `{ chave → path completo }` de TODAS as telas. Injetado como prop global
|
|
147
|
+
* `accountPaths` nas views Edge, para que os `<form action>`/`fetch()` respeitem
|
|
148
|
+
* os overrides sem cada view conhecer o prefixo.
|
|
149
|
+
*/
|
|
150
|
+
export function accountPathsMap() {
|
|
151
|
+
const out = {};
|
|
152
|
+
for (const key of Object.keys(_segments)) {
|
|
153
|
+
out[key] = accountPath(key);
|
|
154
|
+
}
|
|
155
|
+
return out;
|
|
156
|
+
}
|
|
157
|
+
/** Restaura os defaults — uso em testes (isola o singleton entre casos). */
|
|
158
|
+
export function resetAccountPaths() {
|
|
159
|
+
_prefix = DEFAULT_ACCOUNT_PREFIX;
|
|
160
|
+
_segments = { ...DEFAULT_ACCOUNT_SEGMENTS };
|
|
161
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import type { OrgInvitation, OrgMember, OrgSummary } from '../../accounts/account_store.js';
|
|
1
2
|
import type { ResolvedServerConfig } from '../../define_config.js';
|
|
2
|
-
import type { OrgSummary, OrgMember, OrgInvitation } from '../../accounts/account_store.js';
|
|
3
|
-
import type { AdminActor } from './admin_users_service.js';
|
|
4
3
|
import type { SettingsCapability } from '../runtime_settings.js';
|
|
4
|
+
import type { AdminActor } from './admin_users_service.js';
|
|
5
5
|
export interface OrgWithMemberCount extends OrgSummary {
|
|
6
6
|
memberCount: number;
|
|
7
7
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { supportsOrganizations } from '../../accounts/account_store.js';
|
|
2
|
-
import {
|
|
2
|
+
import { accountPath } from '../account_paths.js';
|
|
3
|
+
import { isRoleInCatalog, resolveRoleCatalogList } from '../runtime_toggles.js';
|
|
3
4
|
/**
|
|
4
5
|
* Lógica de gestão de organizações compartilhada entre o console admin (HTML)
|
|
5
6
|
* e a Admin REST API (JSON). Usada pelos `AdminOrgsController` e `ApiOrgsController`.
|
|
@@ -267,7 +268,7 @@ export class AdminOrgsService {
|
|
|
267
268
|
});
|
|
268
269
|
// Dispara mail hook (best-effort)
|
|
269
270
|
if (this.cfg.mail?.onOrgInvitation) {
|
|
270
|
-
const acceptUrl = `${origin}
|
|
271
|
+
const acceptUrl = `${origin}${accountPath('orgs')}/invitations/${token}/accept`;
|
|
271
272
|
try {
|
|
272
273
|
await this.cfg.mail.onOrgInvitation({
|
|
273
274
|
email: input.email,
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import type { HttpContext } from
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
4
|
-
import type {
|
|
5
|
-
import { type DeletionResult } from
|
|
6
|
-
import type { SettingsCapability } from
|
|
1
|
+
import type { HttpContext } from '@adonisjs/core/http';
|
|
2
|
+
import type { AuthAccount } from '../../accounts/account_store.js';
|
|
3
|
+
import type { ResolvedServerConfig } from '../../define_config.js';
|
|
4
|
+
import type { OidcService } from '../../provider/oidc_service.js';
|
|
5
|
+
import { type DeletionResult } from '../account_deletion_service.js';
|
|
6
|
+
import type { SettingsCapability } from '../runtime_settings.js';
|
|
7
7
|
/** Quem disparou a operação (para auditoria). `admin-api` quando via REST API. */
|
|
8
8
|
export interface AdminActor {
|
|
9
9
|
actorId: string | null;
|
|
10
10
|
ip: string | null;
|
|
11
11
|
/** Marca metadata da auditoria — 'admin-api' nas escritas via REST, 'admin' no console HTML. */
|
|
12
|
-
source?:
|
|
12
|
+
source?: 'admin-api' | 'admin';
|
|
13
13
|
}
|
|
14
14
|
/** Resultado da deleção via admin: false quando o store não suporta hard delete. */
|
|
15
15
|
export type DeleteUserResult = {
|
|
16
16
|
ok: false;
|
|
17
|
-
reason:
|
|
17
|
+
reason: 'not_found' | 'unsupported';
|
|
18
18
|
} | {
|
|
19
19
|
ok: true;
|
|
20
20
|
result: DeletionResult;
|
|
@@ -34,7 +34,7 @@ export type EnqueueDeletion = (input: {
|
|
|
34
34
|
actor: {
|
|
35
35
|
actorId: string | null;
|
|
36
36
|
ip: string | null;
|
|
37
|
-
source:
|
|
37
|
+
source: 'admin' | 'admin-api';
|
|
38
38
|
};
|
|
39
39
|
}) => Promise<string>;
|
|
40
40
|
export interface CreateUserInput {
|
|
@@ -50,10 +50,10 @@ export type CreateUserResult = {
|
|
|
50
50
|
invited: boolean;
|
|
51
51
|
} | {
|
|
52
52
|
ok: false;
|
|
53
|
-
reason:
|
|
53
|
+
reason: 'email_taken';
|
|
54
54
|
} | {
|
|
55
55
|
ok: false;
|
|
56
|
-
reason:
|
|
56
|
+
reason: 'password_policy';
|
|
57
57
|
/** Chave i18n da regra violada + params para interpolar. */
|
|
58
58
|
messageKey: string;
|
|
59
59
|
messageParams?: Record<string, string | number>;
|
|
@@ -141,7 +141,7 @@ export declare class AdminUsersService {
|
|
|
141
141
|
*
|
|
142
142
|
* @returns código i18n/erro quando a operação deve ser bloqueada; `null` quando OK.
|
|
143
143
|
*/
|
|
144
|
-
guardGlobalRolesChange(targetId: string, newRoles: string[], actorId: string | null): Promise<
|
|
144
|
+
guardGlobalRolesChange(targetId: string, newRoles: string[], actorId: string | null): Promise<'last_admin' | 'cannot_self_demote' | null>;
|
|
145
145
|
/** Atualiza nome/avatar (capacidade opcional). Retorna a conta ou null. */
|
|
146
146
|
updateProfile(accountId: string, patch: {
|
|
147
147
|
name?: string | null;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { randomBytes } from
|
|
2
|
-
import { supportsAccountDeletion, supportsAccountStatus,
|
|
3
|
-
import {
|
|
4
|
-
import { AccountDeletionService
|
|
5
|
-
import {
|
|
6
|
-
import { resolveEffectiveRolesCatalog } from
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
import { supportsAccountDeletion, supportsAccountStatus, supportsCountByGlobalRole, supportsProfile, } from '../../accounts/account_store.js';
|
|
3
|
+
import { PasswordPolicyError } from '../../password/password_manager.js';
|
|
4
|
+
import { AccountDeletionService } from '../account_deletion_service.js';
|
|
5
|
+
import { sendPasswordResetEmail } from '../default_mailer.js';
|
|
6
|
+
import { resolveEffectiveRolesCatalog } from '../runtime_toggles.js';
|
|
7
7
|
/**
|
|
8
8
|
* Lógica de gestão de usuários compartilhada entre o console admin (B6, HTML) e a
|
|
9
9
|
* Admin REST API (R6, JSON). Encapsula o fluxo "create + invite", reset de senha,
|
|
@@ -24,9 +24,9 @@ export class AdminUsersService {
|
|
|
24
24
|
const store = this.cfg.accountStore;
|
|
25
25
|
const existing = await store.findByEmail(input.email);
|
|
26
26
|
if (existing)
|
|
27
|
-
return { ok: false, reason:
|
|
27
|
+
return { ok: false, reason: 'email_taken' };
|
|
28
28
|
const hasPassword = !!input.password;
|
|
29
|
-
const initialPassword = input.password ?? randomBytes(24).toString(
|
|
29
|
+
const initialPassword = input.password ?? randomBytes(24).toString('hex');
|
|
30
30
|
let account;
|
|
31
31
|
try {
|
|
32
32
|
account = await store.create({
|
|
@@ -40,7 +40,7 @@ export class AdminUsersService {
|
|
|
40
40
|
if (error instanceof PasswordPolicyError) {
|
|
41
41
|
return {
|
|
42
42
|
ok: false,
|
|
43
|
-
reason:
|
|
43
|
+
reason: 'password_policy',
|
|
44
44
|
messageKey: error.key,
|
|
45
45
|
messageParams: error.params,
|
|
46
46
|
};
|
|
@@ -48,7 +48,7 @@ export class AdminUsersService {
|
|
|
48
48
|
throw error;
|
|
49
49
|
}
|
|
50
50
|
await this.cfg.audit?.record({
|
|
51
|
-
type:
|
|
51
|
+
type: 'user.created',
|
|
52
52
|
accountId: account.id,
|
|
53
53
|
email: input.email,
|
|
54
54
|
actorId: actor.actorId,
|
|
@@ -70,7 +70,7 @@ export class AdminUsersService {
|
|
|
70
70
|
return null;
|
|
71
71
|
await this.sendResetEmail(ctx, account.email);
|
|
72
72
|
await this.cfg.audit?.record({
|
|
73
|
-
type:
|
|
73
|
+
type: 'user.password_reset_sent',
|
|
74
74
|
accountId,
|
|
75
75
|
email: account.email,
|
|
76
76
|
actorId: actor.actorId,
|
|
@@ -92,7 +92,7 @@ export class AdminUsersService {
|
|
|
92
92
|
else
|
|
93
93
|
await store.enableAccount(accountId);
|
|
94
94
|
await this.cfg.audit?.record({
|
|
95
|
-
type: disable ?
|
|
95
|
+
type: disable ? 'user.disabled' : 'user.enabled',
|
|
96
96
|
accountId,
|
|
97
97
|
actorId: actor.actorId,
|
|
98
98
|
ip: actor.ip,
|
|
@@ -123,7 +123,7 @@ export class AdminUsersService {
|
|
|
123
123
|
// Roles que o usuário não tinha E que não estão no catálogo = inválidas.
|
|
124
124
|
for (const role of roles) {
|
|
125
125
|
if (!catalogNames.has(role) && !currentRoles.has(role)) {
|
|
126
|
-
return
|
|
126
|
+
return 'admin.roles.unknown_role';
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
129
|
}
|
|
@@ -137,7 +137,7 @@ export class AdminUsersService {
|
|
|
137
137
|
/** Roles globais que conferem acesso de admin (default ['ADMIN']). */
|
|
138
138
|
adminRoles() {
|
|
139
139
|
const roles = this.cfg.admin?.roles;
|
|
140
|
-
return roles && roles.length > 0 ? roles : [
|
|
140
|
+
return roles && roles.length > 0 ? roles : ['ADMIN'];
|
|
141
141
|
}
|
|
142
142
|
/** Um conjunto de roles contém ao menos uma role de admin? */
|
|
143
143
|
hasAdminRole(roles) {
|
|
@@ -226,11 +226,11 @@ export class AdminUsersService {
|
|
|
226
226
|
return null;
|
|
227
227
|
// Auto-rebaixamento: o ator removendo a própria role de admin.
|
|
228
228
|
if (actorId && actorId === targetId)
|
|
229
|
-
return
|
|
229
|
+
return 'cannot_self_demote';
|
|
230
230
|
// Último admin: se o target é o único admin, a remoção causaria lockout.
|
|
231
231
|
const admins = await this.countAdmins();
|
|
232
232
|
if (admins <= 1)
|
|
233
|
-
return
|
|
233
|
+
return 'last_admin';
|
|
234
234
|
return null;
|
|
235
235
|
}
|
|
236
236
|
/** Atualiza nome/avatar (capacidade opcional). Retorna a conta ou null. */
|
|
@@ -251,15 +251,15 @@ export class AdminUsersService {
|
|
|
251
251
|
async delete(oidc, accountId, actor, enqueue) {
|
|
252
252
|
const store = this.cfg.accountStore;
|
|
253
253
|
if (!supportsAccountDeletion(store))
|
|
254
|
-
return { ok: false, reason:
|
|
254
|
+
return { ok: false, reason: 'unsupported' };
|
|
255
255
|
const account = await store.findById(accountId);
|
|
256
256
|
if (!account)
|
|
257
|
-
return { ok: false, reason:
|
|
258
|
-
const source = actor.source ===
|
|
257
|
+
return { ok: false, reason: 'not_found' };
|
|
258
|
+
const source = actor.source === 'admin' ? 'admin' : 'admin-api';
|
|
259
259
|
// Trilha administrativa ANTES do cascade — assim esta linha também é
|
|
260
260
|
// anonimizada na etapa de anonimização do audit (não reintroduz PII).
|
|
261
261
|
await this.cfg.audit?.record({
|
|
262
|
-
type:
|
|
262
|
+
type: 'user.deleted',
|
|
263
263
|
accountId,
|
|
264
264
|
email: account.email,
|
|
265
265
|
actorId: actor.actorId,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import '../augmentations.js';
|
|
2
2
|
import { AdminClientsService } from '../admin_clients_service.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { clientCreateInput, clientInputValidator, clientPartialInput, } from '../admin_validators.js';
|
|
4
|
+
import { apiError, clientDto, createdClientDto } from './dto.js';
|
|
5
5
|
/** Resolve o serviço (== OidcService) + o AdminClientsService. */
|
|
6
6
|
async function clientsService(ctx) {
|
|
7
7
|
const service = await ctx.containerResolver.make('authkit.server');
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import '../augmentations.js';
|
|
2
|
-
import { TokenVerifyService } from './token_verify_service.js';
|
|
3
2
|
import { AdminSessionsService } from '../admin_sessions_service.js';
|
|
4
3
|
import { computeAdminStats } from '../admin_stats_service.js';
|
|
5
|
-
import { auditDto, apiError } from './dto.js';
|
|
6
4
|
import { tokenVerifyValidator } from '../admin_validators.js';
|
|
5
|
+
import { apiError, auditDto } from './dto.js';
|
|
6
|
+
import { TokenVerifyService } from './token_verify_service.js';
|
|
7
7
|
/**
|
|
8
8
|
* Endpoints utilitários da Admin REST API: log de auditoria (`GET /audit`) e
|
|
9
9
|
* introspecção genérica de token (`POST /tokens/verify`).
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import '../augmentations.js';
|
|
2
|
-
import {
|
|
3
|
-
import { orgDto, orgDetailDto, orgInvitationDto, apiError } from './dto.js';
|
|
2
|
+
import { orgAddMemberValidator, orgCreateValidator, orgInvitationValidator, orgMemberRoleValidator, orgUpdateValidator, } from '../admin_validators.js';
|
|
4
3
|
import { resolveRuntimeSettings } from '../runtime_settings.js';
|
|
5
|
-
import {
|
|
4
|
+
import { AdminOrgsService } from './admin_orgs_service.js';
|
|
5
|
+
import { apiError, orgDetailDto, orgDto, orgInvitationDto } from './dto.js';
|
|
6
6
|
/** Lê a config + monta o actor `admin-api` para auditoria. */
|
|
7
7
|
async function ctxBits(ctx) {
|
|
8
8
|
const service = await ctx.containerResolver.make('authkit.server');
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import '../augmentations.js';
|
|
2
|
+
import { SettingLockedError, isSettingLocked, lockedSettingKeys } from '../config_locks.js';
|
|
2
3
|
import { resolveRuntimeSettings } from '../runtime_settings.js';
|
|
3
|
-
import {
|
|
4
|
-
import { isSettingLocked, lockedSettingKeys, SettingLockedError } from '../config_locks.js';
|
|
4
|
+
import { apiError, settingDto } from './dto.js';
|
|
5
5
|
/** 423 Locked: a setting foi travada via defineConfig (não editável em runtime). */
|
|
6
6
|
function lockedResponse(ctx, err) {
|
|
7
7
|
return ctx.response
|
|
@@ -16,8 +16,8 @@ function notSupported(ctx) {
|
|
|
16
16
|
function resolveOrgId(ctx) {
|
|
17
17
|
const qs = ctx.request.qs();
|
|
18
18
|
if ('organizationId' in qs) {
|
|
19
|
-
const v = qs
|
|
20
|
-
return v
|
|
19
|
+
const v = qs.organizationId;
|
|
20
|
+
return v?.trim() ? v.trim() : null;
|
|
21
21
|
}
|
|
22
22
|
return null;
|
|
23
23
|
}
|
|
@@ -99,7 +99,13 @@ export default class ApiSettingsController {
|
|
|
99
99
|
metadata: { key, value: body.value, organizationId: orgId },
|
|
100
100
|
});
|
|
101
101
|
const saved = await svc.getSetting(key, orgId);
|
|
102
|
-
return settingDto({
|
|
102
|
+
return settingDto({
|
|
103
|
+
key,
|
|
104
|
+
organizationId: orgId,
|
|
105
|
+
value: saved,
|
|
106
|
+
updatedAt: new Date(),
|
|
107
|
+
updatedBy: null,
|
|
108
|
+
});
|
|
103
109
|
}
|
|
104
110
|
/** DELETE /settings/:key */
|
|
105
111
|
async destroy(ctx) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type { HttpContext } from
|
|
1
|
+
import '../augmentations.js';
|
|
2
|
+
import type { HttpContext } from '@adonisjs/core/http';
|
|
3
3
|
/**
|
|
4
4
|
* Recurso de usuários da Admin REST API (R6). JSON puro (camelCase), erros no
|
|
5
5
|
* envelope `{ error: { code, message } }`. Toda escrita audita com `actor: 'admin-api'`.
|