@hed-hog/core 0.0.374 → 0.0.376

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/README.md CHANGED
@@ -1,190 +1,191 @@
1
- ```markdown
2
1
  # @hed-hog/core
3
2
 
4
- > **Licença**: MIT (Open Source) — o Core é o único módulo Open Source do HedHog Framework; os demais módulos são Enterprise e requerem licença comercial. Consulte [LICENSE.md](./LICENSE.md).
3
+ > **License**: MIT (Open Source) — Core is the only Open Source module of the HedHog Framework; the other modules are Enterprise and require a commercial license. See [LICENSE.md](./LICENSE.md).
5
4
 
6
- ## 1. Visão geral do módulo
5
+ Learn more about the HedHog Framework at **[hedhog.com](https://hedhog.com)**.
7
6
 
8
- O módulo `@hed-hog/core` é o núcleo do monorepo HedHog, responsável por fornecer funcionalidades centrais essenciais para o sistema, incluindo autenticação, inteligência artificial, dashboard, sistema e gerenciamento de usuários, roles, permissões e componentes de interface. Ele integra diversos submódulos que tratam desde a segurança e autenticação até a gestão de dashboards customizáveis e agentes de IA.
7
+ ## 1. Module overview
9
8
 
10
- ## 2. Escopo e responsabilidades
9
+ The `@hed-hog/core` module is the core of the HedHog monorepo, responsible for providing essential core functionality for the system, including authentication, artificial intelligence, dashboard, system information, and management of users, roles, permissions and UI components. It integrates several submodules covering everything from security and authentication to management of customizable dashboards and AI agents.
11
10
 
12
- - Gerenciamento de autenticação e autorização, incluindo MFA, WebAuthn, recuperação de senha, sessões e login social via OAuth (Google, Facebook, GitHub, Microsoft, Microsoft Entra ID, Apple, LinkedIn) com hub de callback multi-app.
13
- - Serviços de inteligência artificial para chat e agentes AI com suporte a OpenAI e Gemini.
14
- - Gestão de dashboards, componentes, roles e usuários associados.
15
- - Informações do sistema operacional, hardware, banco de dados e módulos instalados.
16
- - Validação e manipulação de dados via DTOs e integração com Prisma ORM.
17
- - Suporte a internacionalização e paginação.
11
+ ## 2. Scope and responsibilities
12
+
13
+ - Authentication and authorization management, including MFA, WebAuthn, password recovery, sessions, and social login via OAuth (Google, Facebook, GitHub, Microsoft, Microsoft Entra ID, Apple, LinkedIn) with a multi-app callback hub.
14
+ - Artificial intelligence services for chat and AI agents with support for OpenAI and Gemini.
15
+ - Management of dashboards, components, roles, and associated users.
16
+ - Operating system, hardware, database, and installed-module information.
17
+ - Data validation and handling via DTOs and integration with the Prisma ORM.
18
+ - Internationalization and pagination support.
18
19
 
19
20
  ## 3. Endpoints
20
21
 
21
- ### Módulo AI (`/ai`)
22
-
23
- | Método | Path | Autenticação | Descrição | Parâmetros / Query / Body | Resposta | Erros Comuns |
24
- |--------|-------------------------|--------------|--------------------------------------------------|-------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|-------------------------------------|
25
- | POST | `/ai/chat` | Autenticada | Realiza chat com IA, podendo enviar arquivo. | Body: `ChatDTO` (message: string, provider?: 'openai'|'gemini', model?: string, systemPrompt?: string, file_id?: number)<br>File: opcional | `{ provider: string, model: string, content: string }` | 400: Chave API não configurada |
26
- | POST | `/ai/agent` | Autenticada | Cria um agente AI. | Body: `CreateAgentDTO` (slug: string, provider?: 'openai'|'gemini', model?: string, instructions?: string) | Objeto agente criado ou existente | 400: Slug já existe |
27
- | GET | `/ai/agent` | Autenticada | Lista agentes AI com paginação. | Query: paginação (page?: number, pageSize?: number, search?: string) | Paginação com lista de agentes | - |
28
- | GET | `/ai/agent/id/:agentId` | Autenticada | Obtém agente AI por ID. | Path param: `agentId` (int) | Objeto agente | 404: Agente não encontrado |
29
- | GET | `/ai/agent/:slug` | Autenticada | Obtém agente AI por slug. | Path param: `slug` (string) | Objeto agente | 404: Agente não encontrado |
30
- | PATCH | `/ai/agent/:agentId` | Autenticada | Atualiza agente AI. | Path param: `agentId` (int)<br>Body: `UpdateAgentDTO` (slug?: string, provider?: 'openai'|'gemini', model?: string, instructions?: string) | Objeto agente atualizado | 404: Agente não encontrado<br>400: Slug duplicado |
31
- | DELETE | `/ai/agent` | Autenticada | Deleta agentes AI em lote. | Body: `DeleteDTO` (ids: number[]) | `{ count: number }` | 404: Um ou mais agentes não encontrados |
32
- | POST | `/ai/agent/:slug/chat` | Autenticada | Chat com agente AI específico, com arquivo opcional. | Path param: `slug` (string)<br>Body: `ChatAgentDTO` (message: string, file_id?: number)<br>File: opcional | `{ slug: string, provider: string, model: string, content: string }` | 404: Agente não encontrado |
33
-
34
- ### Módulo Auth (`/auth`)
35
-
36
- | Método | Path | Autenticação | Descrição | Parâmetros / Body | Resposta | Erros Comuns |
37
- |--------|-------------------------------------|--------------|--------------------------------------------------|--------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|-------------------------------------|
38
- | GET | `/auth/verify` | Autenticada | Verifica usuário autenticado. | - | Dados do usuário autenticado | - |
39
- | GET | `/auth/roles` | Pública | Retorna roles do usuário (se autenticado). | - | `{ roles: string[] }` | - |
40
- | POST | `/auth/refresh` | Pública | Atualiza token de acesso usando refresh token. | Body: `{ refreshToken?: string }`<br>Cookies: `rt` opcional | `{ accessToken: string, refreshToken?: string }` | 400: Refresh token não fornecido |
41
- | POST | `/auth/login` | Pública | Login com email e senha. | Body: `LoginDTO` (email: string, password: string, refreshToken?: boolean) | Tokens de acesso e refresh ou MFA requerida | 400: Acesso negado |
42
- | POST | `/auth/login-email-verification` | Pública | Login via verificação por email e código. | Body: `LoginEmailVerificationDTO` (token: string, code: string) | Tokens de acesso e refresh | 400: Código inválido ou desafio não encontrado |
43
- | POST | `/auth/login-email-verification-resend` | Pública | Reenvia código de verificação por email. | Body: `LoginEmailVerificationResendDTO` (token: string) | Novo token para verificação | 400: Token inválido ou expirado |
44
- | POST | `/auth/signup` | Pública | Cadastro com email e senha. | Body: `CreateWithEmailAndPasswordDTO` | Usuário criado | - |
45
- | POST | `/auth/login-code` | Pública | Login com código MFA. | Body: `LoginWithCodeDTO` (token: string, code: string, methodType?: 'totp'|'email'|'recovery') | Tokens de acesso e refresh | 400: Código MFA inválido |
46
- | POST | `/auth/login-recovery-code` | Pública | Login com código de recuperação MFA. | Body: `LoginWithRecoveryCodeDTO` (token: string, code: string) | Tokens de acesso e refresh | 400: Código de recuperação inválido|
47
- | POST | `/auth/resend-mfa-code` | Pública | Reenvia código MFA por email. | Body: `ResendMfaCodeDTO` (token: string) | `{ success: true, hasEmailMfa: true }` | 400: Nenhum método MFA por email |
48
- | POST | `/auth/webauthn/generate` | Pública | Gera opções para autenticação WebAuthn. | Body: `{ mfaToken: string }` | Opções WebAuthn | 400: WebAuthn não configurado |
49
- | POST | `/auth/webauthn/verify` | Pública | Verifica autenticação WebAuthn. | Body: `{ mfaToken: string, assertionResponse: any }` | Tokens de acesso e refresh | 400: Verificação falhou |
50
- | POST | `/auth/forgot` | Pública | Solicita recuperação de senha via email. | Body: `ForgetDTO` (email: string) | `{ success: true }` | - |
51
- | POST | `/auth/logout` | Pública | Logout e invalida refresh token. | Body: `{ refreshToken?: string }`<br>Cookies: `rt` opcional | `{ success: true }` | 400: Refresh token não fornecido |
52
- | POST | `/auth/forgot-reset` | Pública | Reseta senha via código de recuperação. | Body: `ResetDTO` (password: string, code: string) | Tokens de acesso e refresh | 400: Código inválido ou expirado |
53
-
54
- ### Módulo OAuth (`/oauth`)
55
-
56
- > **Padrão de hub multi-app**: cada provider precisa de **apenas uma URL de callback registrada** (`${url}/callback/:provider`, sem sufixo de fluxo — GitHub usa `${api-url}/oauth/github/callback`, e Apple usa `${api-url}/oauth/apple/callback`, ambos por só aceitarem uma callback URL). O fluxo (`login`/`register`/`connect`) e o app que iniciou a autenticação (ex.: `training`) viajam assinados no parâmetro `state` (`hhweb.<app>.<flow>.<assinatura>`, HMAC via `SecurityService`), nunca no path. O app configurado na setting `url` funciona como **hub**: ao receber o callback do provider, ele decide localmente (fluxo próprio) ou reencaminha o browser para a página de callback do app que iniciou o fluxo, resolvendo a origem via setting `app-urls`. Providers suportados: Google, Facebook, GitHub, Microsoft, Microsoft Entra ID, Apple (Sign in with Apple) e LinkedIn.
57
-
58
- | Método | Path | Autenticação | Descrição | Parâmetros / Query / Body | Resposta | Erros Comuns |
59
- |--------|----------------------------------|--------------|------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------|------------------------------------------------------------------------|----------------------------------------------------------------------|
60
- | GET | `/oauth/github/callback` | Pública | Bounce exclusivo do GitHub (única callback URL aceita pelo provider); repassa o `code` para o frontend correto. | Query: `code`, `state?` | Redirect 302 para `${origem}/callback/github/<flow>?code=...&state=...` | - |
61
- | POST | `/oauth/apple/callback` | Pública | Bounce exclusivo da Apple: ela exige `response_mode=form_post` quando `scope` é solicitado, então POSTa `code`/`state` em vez de um redirect GET. Converte de volta para um redirect GET, igual ao bounce do GitHub. | Body: `code`, `state?` | Redirect 302 para `${origem}/callback/apple/<flow>?code=...&state=...` | - |
62
- | GET | `/oauth/:provider/login` | Pública | Inicia o fluxo de login, redirecionando para a tela de autorização do provider. | Path: `provider`<br>Query: `redirectApp?` (chave em `app-urls` do app iniciador) | Redirect 302 para a URL de autorização do provider | 400: provider não habilitado ou não suportado |
63
- | GET | `/oauth/:provider/register` | Pública | Inicia o fluxo de cadastro via OAuth. | Path: `provider`<br>Query: `redirectApp?` | Redirect 302 para a URL de autorização do provider | 400: provider não habilitado ou não suportado |
64
- | GET | `/oauth/:provider/connect` | Pública | Inicia o fluxo de vinculação de conta a um usuário já autenticado no app de destino. | Path: `provider`<br>Query: `redirectApp?` | Redirect 302 para a URL de autorização do provider | 400: provider não habilitado ou não suportado |
65
- | GET | `/oauth/:provider/mobile/auth-url` | Pública | Retorna a URL de autorização para apps nativos (Electron/React Native) interceptarem o redirect. | Path: `provider`<br>Query: `redirectUri` (custom scheme do app nativo) | `{ authUrl: string }` | 400: redirect URI inválida ou provider não habilitado |
66
- | GET | `/oauth/:provider/callback/login` | Pública | Troca o `code` por tokens de acesso após o hub reencaminhar para a página de login do app. | Path: `provider`<br>Query: `code`, `state?`, `redirectUri?` | `{ accessToken: string, refreshToken?: string }` + cookie `rt` (httpOnly) | 400: code ausente, origem inválida ou callback já processado<br>409: callback em processamento<br>503: falha no provider |
67
- | GET | `/oauth/:provider/callback/register` | Pública | Troca o `code` por tokens após cadastro via OAuth. | Path: `provider`<br>Query: `code` | `{ accessToken: string, refreshToken?: string }` + cookie `rt` | 400/409/503 — mesmos casos do callback de login |
68
- | GET | `/oauth/:provider/callback/connect` | Autenticada | Vincula a conta do provider ao usuário autenticado. | Path: `provider`<br>Query: `code` | `{ accessToken: string, refreshToken?: string }` + cookie `rt` | 400/409/503 — mesmos casos do callback de login |
69
- | DELETE | `/oauth/:provider` | Autenticada | Desvincula a conta do provider do usuário. | Path: `provider`<br>Body: `{ email: string }` | Resultado da desconexão | - |
70
-
71
- ### Módulo System (`/system`)
72
-
73
- | Método | Path | Autenticação | Descrição | Parâmetros / Body | Resposta | Erros Comuns |
74
- |--------|------------|--------------|---------------------------------|-------------------|------------------------------------------------------------------------------------------|--------------|
75
- | GET | `/system` | Autenticada | Retorna informações do sistema. | - | Informações detalhadas do sistema operacional, hardware, banco de dados, módulos e usuários | - |
76
-
77
- ### Módulo Dashboard Core (`/dashboard-core`)
78
-
79
- | Método | Path | Autenticação | Descrição | Parâmetros / Body | Resposta | Erros Comuns |
80
- |--------|---------------------------|--------------|--------------------------------------------------|---------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|-------------------------------------|
81
- | GET | `/dashboard-core/home` | Autenticada | Retorna dashboard home do usuário. | - | Objeto dashboard ou null | 400: Usuário não encontrado |
82
- | GET | `/dashboard-core/stats/overview/users` | Autenticada | Estatísticas de usuários. | - | Estatísticas agregadas de usuários e sessões | - |
83
- | GET | `/dashboard-core/stats/overview/mails` | Autenticada | Estatísticas de emails enviados. | - | Estatísticas agregadas de emails enviados | - |
84
- | GET | `/dashboard-core/stats/overview/system` | Autenticada | Estatísticas do sistema (menus e rotas). | - | Estatísticas agregadas do sistema | - |
85
- | GET | `/dashboard-core/config/overview` | Autenticada | Visão geral das configurações do sistema. | - | Objeto com configurações | - |
86
- | GET | `/dashboard-core/widgets/me` | Autenticada | Dados dos widgets para o usuário. | - | Dados agregados para widgets do usuário | - |
87
- | GET | `/dashboard-core/user-dashboards` | Autenticada | Lista dashboards do usuário. | - | Lista de dashboards associados ao usuário | - |
88
- | GET | `/dashboard-core/templates` | Autenticada | Lista templates disponíveis para dashboards. | - | Lista de templates | - |
89
- | POST | `/dashboard-core/dashboard` | Autenticada | Cria dashboard para usuário. | Body: `{ name?: string; slug?: string; icon?: string | null; templateSlug?: string }` | Dashboard criado | - |
90
- | PATCH | `/dashboard-core/dashboard/order` | Autenticada | Reordena dashboards do usuário. | Body: `{ slugs?: string[] }` | Dashboard reordenado | - |
91
- | PATCH | `/dashboard-core/dashboard/:slug` | Autenticada | Renomeia dashboard do usuário. | Path param: `slug` (string)<br>Body: `{ name?: string; icon?: string | null }` | Dashboard atualizado | - |
92
- | POST | `/dashboard-core/dashboard/:slug/home` | Autenticada | Define dashboard como home do usuário. | Path param: `slug` (string) | Sucesso | - |
93
- | GET | `/dashboard-core/dashboard/:slug/shares` | Autenticada | Lista compartilhamentos do dashboard. | Path param: `slug` (string) | Lista de usuários com acesso | - |
94
- | GET | `/dashboard-core/shareable-users/:slug` | Autenticada | Lista usuários para compartilhar dashboard. | Path param: `slug` (string)<br>Query: search?: string, page?: string, pageSize?: string | Paginação de usuários | - |
95
- | POST | `/dashboard-core/dashboard/:slug/share` | Autenticada | Compartilha dashboard com usuários. | Path param: `slug` (string)<br>Body: `{ userId?: number; userIds?: number[] }` | Sucesso | - |
96
- | DELETE | `/dashboard-core/dashboard/:slug/share/:sharedUserId` | Autenticada | Revoga compartilhamento de dashboard. | Path params: `slug` (string), `sharedUserId` (int) | Sucesso | - |
97
- | DELETE | `/dashboard-core/dashboard/:slug` | Autenticada | Remove dashboard do usuário. | Path param: `slug` (string) | Sucesso | - |
98
- | GET | `/dashboard-core/access/:slug` | Autenticada | Verifica acesso do usuário a dashboard. | Path param: `slug` (string) | `{ hasAccess: boolean, dashboard: object|null }` | - |
99
- | GET | `/dashboard-core/layout/:slug` | Autenticada | Obtém layout do usuário para dashboard. | Path param: `slug` (string) | Array de widgets com posições e dimensões | - |
100
- | POST | `/dashboard-core/layout/:slug` | Autenticada | Salva layout do usuário para dashboard. | Body: `{ layout: Array<{ i: string; x: number; y: number; w: number; h: number }> }` | `{ success: true }` | 403: Acesso negado |
101
- | POST | `/dashboard-core/widget/:slug` | Autenticada | Adiciona widget ao dashboard do usuário. | Body: `{ componentSlug: string }` | Dados do widget adicionado | 403: Acesso negado |
102
- | DELETE | `/dashboard-core/widget/:slug/:widgetId` | Autenticada | Remove widget do dashboard do usuário. | Path params: `slug` (string), `widgetId` (string) | Erro "Not implemented yet" | - |
103
- | GET | `/dashboard-core/:slug` | Autenticada | Obtém itens do dashboard por slug. | Path param: `slug` (string), Query param: `locale?: string` | Lista de itens do dashboard | - |
104
-
105
- ### Módulo Dashboard (`/dashboard`)
106
-
107
- | Método | Path | Autenticação | Descrição | Parâmetros / Body | Resposta | Erros Comuns |
108
- |--------|----------------|--------------|-------------------------------|-------------------------------------------------------|------------------------------|----------------------------|
109
- | GET | `/dashboard` | Autenticada | Lista dashboards com paginação | Query: paginação (page?: number, pageSize?: number, search?: string) | Paginação com dashboards | - |
110
- | GET | `/dashboard/:id` | Autenticada | Obtém dashboard por ID | Path param: `id` (int) | Objeto dashboard | 404: Dashboard não encontrado |
111
- | POST | `/dashboard` | Autenticada | Cria dashboard | Body: `CreateDashboardDTO` (slug: string, locale: Record<string, { name: string }>) | Dashboard criado | - |
112
- | PATCH | `/dashboard/:id` | Autenticada | Atualiza dashboard | Path param: `id` (int), Body: `UpdateDashboardDTO` | Dashboard atualizado | 404: Dashboard não encontrado |
113
- | DELETE | `/dashboard/:id` | Autenticada | Deleta dashboard | Path param: `id` (int) | `{ success: true }` | 404: Dashboard não encontrado |
114
-
115
- ### Módulo Dashboard Component (`/dashboard-component`)
116
-
117
- | Método | Path | Autenticação | Descrição | Parâmetros / Body | Resposta | Erros Comuns |
118
- |--------|------------------------|--------------|-------------------------------|----------------------------------------------------------------|------------------------------|-------------------------------|
119
- | GET | `/dashboard-component` | Autenticada | Lista componentes com paginação | Query: paginação (page?: number, pageSize?: number, search?: string) | Paginação com componentes | - |
120
- | GET | `/dashboard-component/user` | Autenticada | Lista componentes por roles do usuário | Query: paginação (page?: number, pageSize?: number, search?: string), User ID via token | Paginação com componentes | - |
121
- | GET | `/dashboard-component/:id` | Autenticada | Obtém componente por ID | Path param: `id` (int) | Objeto componente | 404: Componente não encontrado |
122
- | POST | `/dashboard-component` | Autenticada | Cria componente | Body: `CreateDashboardComponentDTO` | Componente criado | - |
123
- | PATCH | `/dashboard-component/:id` | Autenticada | Atualiza componente | Path param: `id` (int), Body: `UpdateDashboardComponentDTO` | Componente atualizado | 404: Componente não encontrado |
124
- | DELETE | `/dashboard-component/:id` | Autenticada | Deleta componente | Path param: `id` (int) | `{ success: true }` | 404: Componente não encontrado |
125
- | POST | `/dashboard-component/:id/preview` | Autenticada | Salva preview de componente (imagem) | Path param: `id` (int), File: imagem (image/*) | `{ success: true, componentId: number, slug: string, library_slug: string, fileName: string, relativeUrl: string }` | 400: Arquivo inválido<br>403: Apenas em dev |
126
-
127
- ### Módulo Dashboard Component Role (`/dashboard-component-role`)
128
-
129
- | Método | Path | Autenticação | Descrição | Parâmetros / Body | Resposta | Erros Comuns |
130
- |--------|-------------------------------|--------------|-----------------------------------|----------------------------------------------------------------|------------------------------|-------------------------------|
131
- | GET | `/dashboard-component-role` | Autenticada | Lista relações com paginação ou por componente | Query: paginação (page?: number, pageSize?: number), Query param: componentId?: number | Paginação ou lista de relações | - |
132
- | POST | `/dashboard-component-role` | Autenticada | Cria relação componente-role | Body: `CreateDashboardComponentRoleDTO` | Relação criada | Erro se relação já existe |
133
- | POST | `/dashboard-component-role/batch` | Autenticada | Cria relações em lote | Body: `CreateDashboardComponentRoleBatchDTO` | { success: boolean, created: number, skipped: number, message: string } | - |
134
- | DELETE | `/dashboard-component-role/:id` | Autenticada | Deleta relação por ID | Path param: `id` (int) | `{ success: true }` | 404: Relação não encontrada |
135
- | DELETE | `/dashboard-component-role/component/:componentId/role/:roleId` | Autenticada | Deleta relação por componente e role | Path params: `componentId` (int), `roleId` (int) | `{ success: true }` | 404: Relação não encontrada |
136
-
137
- ### Módulo Dashboard Item (`/dashboard-item`)
138
-
139
- | Método | Path | Autenticação | Descrição | Parâmetros / Body | Resposta | Erros Comuns |
140
- |--------|--------------------|--------------|-------------------------------|----------------------------------------------------------------|------------------------------|-------------------------------|
141
- | GET | `/dashboard-item` | Autenticada | Lista itens com paginação e filtro por dashboard | Query: paginação (page?: number, pageSize?: number), Query param: dashboardId?: number | Paginação com itens | - |
142
- | POST | `/dashboard-item` | Autenticada | Cria item | Body: `CreateDashboardItemDTO` | Item criado | - |
143
- | DELETE | `/dashboard-item/:id` | Autenticada | Deleta item por ID | Path param: `id` (int) | `{ success: true }` | 404: Item não encontrado |
144
-
145
- ### Módulo Dashboard Role (`/dashboard-role`)
146
-
147
- | Método | Path | Autenticação | Descrição | Parâmetros / Body | Resposta | Erros Comuns |
148
- |--------|--------------------|--------------|-----------------------------------|----------------------------------------------------------------|------------------------------|-------------------------------|
149
- | GET | `/dashboard-role` | Autenticada | Lista relações com paginação ou por dashboard | Query: paginação (page?: number, pageSize?: number), Query param: dashboardId?: number | Paginação ou lista de relações | - |
150
- | POST | `/dashboard-role` | Autenticada | Cria relação dashboard-role | Body: `CreateDashboardRoleDTO` | Relação criada | Erro se relação já existe |
151
- | POST | `/dashboard-role/batch` | Autenticada | Cria relações em lote | Body: `CreateDashboardRoleBatchDTO` | { success: boolean, created: number, skipped: number, message: string } | - |
152
- | DELETE | `/dashboard-role/:id` | Autenticada | Deleta relação por ID | Path param: `id` (int) | `{ success: true }` | 404: Relação não encontrada |
153
- | DELETE | `/dashboard-role/dashboard/:dashboardId/role/:roleId` | Autenticada | Deleta relação por dashboard e role | Path params: `dashboardId` (int), `roleId` (int) | `{ success: true }` | 404: Relação não encontrada |
154
-
155
- ### Módulo Dashboard User (`/dashboard-user`)
156
-
157
- | Método | Path | Autenticação | Descrição | Parâmetros / Body | Resposta | Erros Comuns |
158
- |--------|--------------------|--------------|-------------------------------|----------------------------------------------------------------|------------------------------|-------------------------------|
159
- | GET | `/dashboard-user` | Autenticada | Lista relações com paginação | Query: paginação (page?: number, pageSize?: number) | Paginação com relações | - |
160
- | GET | `/dashboard-user/:id` | Autenticada | Obtém relação por ID | Path param: `id` (int) | Objeto relação | - |
161
- | POST | `/dashboard-user` | Autenticada | Cria relação | Body: `CreateDTO` (dashboard_id: number, user_id: number) | Relação criada | - |
162
- | PATCH | `/dashboard-user/:id` | Autenticada | Atualiza relação | Path param: `id` (int), Body: `UpdateDTO` | Relação atualizada | - |
163
- | DELETE | `/dashboard-user` | Autenticada | Deleta relações em lote | Body: `DeleteDTO` (ids: number[]) | `{ count: number }` | 400: Nenhum id fornecido |
164
-
165
- ## 4. Regras de autenticação e autorização
166
-
167
- - A maioria dos endpoints requer autenticação via token JWT.
168
- - Endpoints públicos são indicados explicitamente.
169
- - Controle de acesso baseado em roles e permissões.
170
- - MFA (Multi-Factor Authentication) suportado via TOTP, email e códigos de recuperação.
171
- - WebAuthn suportado para autenticação forte.
172
- - Refresh tokens são gerenciados via cookies HTTP-only ou no corpo da requisição.
173
- - Operações sensíveis (criação, atualização, deleção) requerem autenticação e permissões adequadas.
174
-
175
- ## 5. Estruturas de request/response
176
-
177
- ### DTOs principais do módulo AI
22
+ ### AI Module (`/ai`)
23
+
24
+ | Method | Path | Auth | Description | Parameters / Query / Body | Response | Common Errors |
25
+ |--------|-------------------------|--------------|-----------------------------------------------------|-------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|-------------------------------------|
26
+ | POST | `/ai/chat` | Authenticated | Chats with AI, optionally sending a file. | Body: `ChatDTO` (message: string, provider?: 'openai'|'gemini', model?: string, systemPrompt?: string, file_id?: number)<br>File: optional | `{ provider: string, model: string, content: string }` | 400: API key not configured |
27
+ | POST | `/ai/agent` | Authenticated | Creates an AI agent. | Body: `CreateAgentDTO` (slug: string, provider?: 'openai'|'gemini', model?: string, instructions?: string) | Created or existing agent object | 400: Slug already exists |
28
+ | GET | `/ai/agent` | Authenticated | Lists AI agents with pagination. | Query: pagination (page?: number, pageSize?: number, search?: string) | Paginated list of agents | - |
29
+ | GET | `/ai/agent/id/:agentId` | Authenticated | Gets an AI agent by ID. | Path param: `agentId` (int) | Agent object | 404: Agent not found |
30
+ | GET | `/ai/agent/:slug` | Authenticated | Gets an AI agent by slug. | Path param: `slug` (string) | Agent object | 404: Agent not found |
31
+ | PATCH | `/ai/agent/:agentId` | Authenticated | Updates an AI agent. | Path param: `agentId` (int)<br>Body: `UpdateAgentDTO` (slug?: string, provider?: 'openai'|'gemini', model?: string, instructions?: string) | Updated agent object | 404: Agent not found<br>400: Duplicate slug |
32
+ | DELETE | `/ai/agent` | Authenticated | Bulk-deletes AI agents. | Body: `DeleteDTO` (ids: number[]) | `{ count: number }` | 404: One or more agents not found |
33
+ | POST | `/ai/agent/:slug/chat` | Authenticated | Chats with a specific AI agent, with an optional file. | Path param: `slug` (string)<br>Body: `ChatAgentDTO` (message: string, file_id?: number)<br>File: optional | `{ slug: string, provider: string, model: string, content: string }` | 404: Agent not found |
34
+
35
+ ### Auth Module (`/auth`)
36
+
37
+ | Method | Path | Auth | Description | Parameters / Body | Response | Common Errors |
38
+ |--------|-------------------------------------|--------------|-----------------------------------------------------|--------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|-------------------------------------|
39
+ | GET | `/auth/verify` | Authenticated | Verifies the authenticated user. | - | Authenticated user data | - |
40
+ | GET | `/auth/roles` | Public | Returns the user's roles (if authenticated). | - | `{ roles: string[] }` | - |
41
+ | POST | `/auth/refresh` | Public | Refreshes the access token using the refresh token. | Body: `{ refreshToken?: string }`<br>Cookies: `rt` optional | `{ accessToken: string, refreshToken?: string }` | 400: Refresh token not provided |
42
+ | POST | `/auth/login` | Public | Login with email and password. | Body: `LoginDTO` (email: string, password: string, refreshToken?: boolean) | Access and refresh tokens, or MFA required | 400: Access denied |
43
+ | POST | `/auth/login-email-verification` | Public | Login via email verification and code. | Body: `LoginEmailVerificationDTO` (token: string, code: string) | Access and refresh tokens | 400: Invalid code or challenge not found |
44
+ | POST | `/auth/login-email-verification-resend` | Public | Resends the email verification code. | Body: `LoginEmailVerificationResendDTO` (token: string) | New token for verification | 400: Invalid or expired token |
45
+ | POST | `/auth/signup` | Public | Sign-up with email and password. | Body: `CreateWithEmailAndPasswordDTO` | User created | - |
46
+ | POST | `/auth/login-code` | Public | Login with an MFA code. | Body: `LoginWithCodeDTO` (token: string, code: string, methodType?: 'totp'|'email'|'recovery') | Access and refresh tokens | 400: Invalid MFA code |
47
+ | POST | `/auth/login-recovery-code` | Public | Login with an MFA recovery code. | Body: `LoginWithRecoveryCodeDTO` (token: string, code: string) | Access and refresh tokens | 400: Invalid recovery code|
48
+ | POST | `/auth/resend-mfa-code` | Public | Resends the MFA code by email. | Body: `ResendMfaCodeDTO` (token: string) | `{ success: true, hasEmailMfa: true }` | 400: No email MFA method configured |
49
+ | POST | `/auth/webauthn/generate` | Public | Generates options for WebAuthn authentication. | Body: `{ mfaToken: string }` | WebAuthn options | 400: WebAuthn not configured |
50
+ | POST | `/auth/webauthn/verify` | Public | Verifies WebAuthn authentication. | Body: `{ mfaToken: string, assertionResponse: any }` | Access and refresh tokens | 400: Verification failed |
51
+ | POST | `/auth/forgot` | Public | Requests password recovery via email. | Body: `ForgetDTO` (email: string) | `{ success: true }` | - |
52
+ | POST | `/auth/logout` | Public | Logs out and invalidates the refresh token. | Body: `{ refreshToken?: string }`<br>Cookies: `rt` optional | `{ success: true }` | 400: Refresh token not provided |
53
+ | POST | `/auth/forgot-reset` | Public | Resets the password using a recovery code. | Body: `ResetDTO` (password: string, code: string) | Access and refresh tokens | 400: Invalid or expired code |
54
+
55
+ ### OAuth Module (`/oauth`)
56
+
57
+ > **Multi-app hub pattern**: each provider only needs **one registered callback URL** (`${url}/callback/:provider`, with no flow suffix — GitHub uses `${api-url}/oauth/github/callback`, and Apple uses `${api-url}/oauth/apple/callback`, both because they only accept a single callback URL). The flow (`login`/`register`/`connect`) and the app that started the authentication (e.g. `training`) travel signed in the `state` parameter (`hhweb.<app>.<flow>.<signature>`, HMAC via `SecurityService`), never in the path. The app configured in the `url` setting acts as the **hub**: when it receives the callback from the provider, it either handles the flow locally or forwards the browser to the callback page of the app that started the flow, resolving the origin via the `app-urls` setting. Supported providers: Google, Facebook, GitHub, Microsoft, Microsoft Entra ID, Apple (Sign in with Apple), and LinkedIn.
58
+
59
+ | Method | Path | Auth | Description | Parameters / Query / Body | Response | Common Errors |
60
+ |--------|----------------------------------|--------------|--------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------|------------------------------------------------------------------------|----------------------------------------------------------------------|
61
+ | GET | `/oauth/github/callback` | Public | Exclusive GitHub bounce (the only callback URL accepted by the provider); forwards the `code` to the correct frontend. | Query: `code`, `state?` | 302 redirect to `${origin}/callback/github/<flow>?code=...&state=...` | - |
62
+ | POST | `/oauth/apple/callback` | Public | Exclusive Apple bounce: Apple requires `response_mode=form_post` when `scope` is requested, so it POSTs `code`/`state` instead of a GET redirect. This is converted back to a GET redirect, the same as the GitHub bounce. | Body: `code`, `state?` | 302 redirect to `${origin}/callback/apple/<flow>?code=...&state=...` | - |
63
+ | GET | `/oauth/:provider/login` | Public | Starts the login flow, redirecting to the provider's authorization screen. | Path: `provider`<br>Query: `redirectApp?` (key in the initiating app's `app-urls`) | 302 redirect to the provider's authorization URL | 400: provider not enabled or not supported |
64
+ | GET | `/oauth/:provider/register` | Public | Starts the sign-up flow via OAuth. | Path: `provider`<br>Query: `redirectApp?` | 302 redirect to the provider's authorization URL | 400: provider not enabled or not supported |
65
+ | GET | `/oauth/:provider/connect` | Public | Starts the flow to link an account to a user already authenticated in the target app. | Path: `provider`<br>Query: `redirectApp?` | 302 redirect to the provider's authorization URL | 400: provider not enabled or not supported |
66
+ | GET | `/oauth/:provider/mobile/auth-url` | Public | Returns the authorization URL for native apps (Electron/React Native) to intercept the redirect. | Path: `provider`<br>Query: `redirectUri` (native app's custom scheme) | `{ authUrl: string }` | 400: invalid redirect URI or provider not enabled |
67
+ | GET | `/oauth/:provider/callback/login` | Public | Exchanges the `code` for access tokens after the hub forwards to the app's login page. | Path: `provider`<br>Query: `code`, `state?`, `redirectUri?` | `{ accessToken: string, refreshToken?: string }` + `rt` cookie (httpOnly) | 400: missing code, invalid origin, or callback already processed<br>409: callback being processed<br>503: provider failure |
68
+ | GET | `/oauth/:provider/callback/register` | Public | Exchanges the `code` for tokens after signing up via OAuth. | Path: `provider`<br>Query: `code` | `{ accessToken: string, refreshToken?: string }` + `rt` cookie | 400/409/503 — same cases as the login callback |
69
+ | GET | `/oauth/:provider/callback/connect` | Authenticated | Links the provider account to the authenticated user. | Path: `provider`<br>Query: `code` | `{ accessToken: string, refreshToken?: string }` + `rt` cookie | 400/409/503 — same cases as the login callback |
70
+ | DELETE | `/oauth/:provider` | Authenticated | Unlinks the provider account from the user. | Path: `provider`<br>Body: `{ email: string }` | Disconnection result | - |
71
+
72
+ ### System Module (`/system`)
73
+
74
+ | Method | Path | Auth | Description | Parameters / Body | Response | Common Errors |
75
+ |--------|------------|--------------|---------------------------------|-------------------|--------------------------------------------------------------------------------------------|--------------|
76
+ | GET | `/system` | Authenticated | Returns system information. | - | Detailed information about the operating system, hardware, database, modules, and users | - |
77
+
78
+ ### Dashboard Core Module (`/dashboard-core`)
79
+
80
+ | Method | Path | Auth | Description | Parameters / Body | Response | Common Errors |
81
+ |--------|---------------------------|--------------|-----------------------------------------------------|---------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------|-------------------------------------|
82
+ | GET | `/dashboard-core/home` | Authenticated | Returns the user's home dashboard. | - | Dashboard object or null | 400: User not found |
83
+ | GET | `/dashboard-core/stats/overview/users` | Authenticated | User statistics. | - | Aggregated user and session statistics | - |
84
+ | GET | `/dashboard-core/stats/overview/mails` | Authenticated | Sent-email statistics. | - | Aggregated statistics on sent emails | - |
85
+ | GET | `/dashboard-core/stats/overview/system` | Authenticated | System statistics (menus and routes). | - | Aggregated system statistics | - |
86
+ | GET | `/dashboard-core/config/overview` | Authenticated | Overview of the system settings. | - | Object with configuration | - |
87
+ | GET | `/dashboard-core/widgets/me` | Authenticated | Widget data for the user. | - | Aggregated data for the user's widgets | - |
88
+ | GET | `/dashboard-core/user-dashboards` | Authenticated | Lists the user's dashboards. | - | List of dashboards associated with the user | - |
89
+ | GET | `/dashboard-core/templates` | Authenticated | Lists templates available for dashboards. | - | List of templates | - |
90
+ | POST | `/dashboard-core/dashboard` | Authenticated | Creates a dashboard for the user. | Body: `{ name?: string; slug?: string; icon?: string | null; templateSlug?: string }` | Created dashboard | - |
91
+ | PATCH | `/dashboard-core/dashboard/order` | Authenticated | Reorders the user's dashboards. | Body: `{ slugs?: string[] }` | Reordered dashboard | - |
92
+ | PATCH | `/dashboard-core/dashboard/:slug` | Authenticated | Renames the user's dashboard. | Path param: `slug` (string)<br>Body: `{ name?: string; icon?: string | null }` | Updated dashboard | - |
93
+ | POST | `/dashboard-core/dashboard/:slug/home` | Authenticated | Sets the dashboard as the user's home dashboard. | Path param: `slug` (string) | Success | - |
94
+ | GET | `/dashboard-core/dashboard/:slug/shares` | Authenticated | Lists the dashboard's shares. | Path param: `slug` (string) | List of users with access | - |
95
+ | GET | `/dashboard-core/shareable-users/:slug` | Authenticated | Lists users the dashboard can be shared with. | Path param: `slug` (string)<br>Query: search?: string, page?: string, pageSize?: string | Paginated list of users | - |
96
+ | POST | `/dashboard-core/dashboard/:slug/share` | Authenticated | Shares the dashboard with users. | Path param: `slug` (string)<br>Body: `{ userId?: number; userIds?: number[] }` | Success | - |
97
+ | DELETE | `/dashboard-core/dashboard/:slug/share/:sharedUserId` | Authenticated | Revokes a dashboard share. | Path params: `slug` (string), `sharedUserId` (int) | Success | - |
98
+ | DELETE | `/dashboard-core/dashboard/:slug` | Authenticated | Removes the dashboard from the user. | Path param: `slug` (string) | Success | - |
99
+ | GET | `/dashboard-core/access/:slug` | Authenticated | Checks the user's access to the dashboard. | Path param: `slug` (string) | `{ hasAccess: boolean, dashboard: object|null }` | - |
100
+ | GET | `/dashboard-core/layout/:slug` | Authenticated | Gets the user's layout for the dashboard. | Path param: `slug` (string) | Array of widgets with positions and sizes | - |
101
+ | POST | `/dashboard-core/layout/:slug` | Authenticated | Saves the user's layout for the dashboard. | Body: `{ layout: Array<{ i: string; x: number; y: number; w: number; h: number }> }` | `{ success: true }` | 403: Access denied |
102
+ | POST | `/dashboard-core/widget/:slug` | Authenticated | Adds a widget to the user's dashboard. | Body: `{ componentSlug: string }` | Data for the added widget | 403: Access denied |
103
+ | DELETE | `/dashboard-core/widget/:slug/:widgetId` | Authenticated | Removes a widget from the user's dashboard. | Path params: `slug` (string), `widgetId` (string) | "Not implemented yet" error | - |
104
+ | GET | `/dashboard-core/:slug` | Authenticated | Gets dashboard items by slug. | Path param: `slug` (string), Query param: `locale?: string` | List of dashboard items | - |
105
+
106
+ ### Dashboard Module (`/dashboard`)
107
+
108
+ | Method | Path | Auth | Description | Parameters / Body | Response | Common Errors |
109
+ |--------|----------------|--------------|------------------------------------|---------------------------------------------------------|------------------------------|----------------------------|
110
+ | GET | `/dashboard` | Authenticated | Lists dashboards with pagination | Query: pagination (page?: number, pageSize?: number, search?: string) | Paginated list of dashboards | - |
111
+ | GET | `/dashboard/:id` | Authenticated | Gets a dashboard by ID | Path param: `id` (int) | Dashboard object | 404: Dashboard not found |
112
+ | POST | `/dashboard` | Authenticated | Creates a dashboard | Body: `CreateDashboardDTO` (slug: string, locale: Record<string, { name: string }>) | Created dashboard | - |
113
+ | PATCH | `/dashboard/:id` | Authenticated | Updates a dashboard | Path param: `id` (int), Body: `UpdateDashboardDTO` | Updated dashboard | 404: Dashboard not found |
114
+ | DELETE | `/dashboard/:id` | Authenticated | Deletes a dashboard | Path param: `id` (int) | `{ success: true }` | 404: Dashboard not found |
115
+
116
+ ### Dashboard Component Module (`/dashboard-component`)
117
+
118
+ | Method | Path | Auth | Description | Parameters / Body | Response | Common Errors |
119
+ |--------|------------------------|--------------|------------------------------------|------------------------------------------------------------------|------------------------------|-------------------------------|
120
+ | GET | `/dashboard-component` | Authenticated | Lists components with pagination | Query: pagination (page?: number, pageSize?: number, search?: string) | Paginated list of components | - |
121
+ | GET | `/dashboard-component/user` | Authenticated | Lists components by the user's roles | Query: pagination (page?: number, pageSize?: number, search?: string), User ID via token | Paginated list of components | - |
122
+ | GET | `/dashboard-component/:id` | Authenticated | Gets a component by ID | Path param: `id` (int) | Component object | 404: Component not found |
123
+ | POST | `/dashboard-component` | Authenticated | Creates a component | Body: `CreateDashboardComponentDTO` | Created component | - |
124
+ | PATCH | `/dashboard-component/:id` | Authenticated | Updates a component | Path param: `id` (int), Body: `UpdateDashboardComponentDTO` | Updated component | 404: Component not found |
125
+ | DELETE | `/dashboard-component/:id` | Authenticated | Deletes a component | Path param: `id` (int) | `{ success: true }` | 404: Component not found |
126
+ | POST | `/dashboard-component/:id/preview` | Authenticated | Saves a component preview (image) | Path param: `id` (int), File: image (image/*) | `{ success: true, componentId: number, slug: string, library_slug: string, fileName: string, relativeUrl: string }` | 400: Invalid file<br>403: Dev environment only |
127
+
128
+ ### Dashboard Component Role Module (`/dashboard-component-role`)
129
+
130
+ | Method | Path | Auth | Description | Parameters / Body | Response | Common Errors |
131
+ |--------|-------------------------------|--------------|---------------------------------------|------------------------------------------------------------------|------------------------------|-------------------------------|
132
+ | GET | `/dashboard-component-role` | Authenticated | Lists relations with pagination or by component | Query: pagination (page?: number, pageSize?: number), Query param: componentId?: number | Paginated or list of relations | - |
133
+ | POST | `/dashboard-component-role` | Authenticated | Creates a component-role relation | Body: `CreateDashboardComponentRoleDTO` | Created relation | Error if the relation already exists |
134
+ | POST | `/dashboard-component-role/batch` | Authenticated | Bulk-creates relations | Body: `CreateDashboardComponentRoleBatchDTO` | { success: boolean, created: number, skipped: number, message: string } | - |
135
+ | DELETE | `/dashboard-component-role/:id` | Authenticated | Deletes a relation by ID | Path param: `id` (int) | `{ success: true }` | 404: Relation not found |
136
+ | DELETE | `/dashboard-component-role/component/:componentId/role/:roleId` | Authenticated | Deletes a relation by component and role | Path params: `componentId` (int), `roleId` (int) | `{ success: true }` | 404: Relation not found |
137
+
138
+ ### Dashboard Item Module (`/dashboard-item`)
139
+
140
+ | Method | Path | Auth | Description | Parameters / Body | Response | Common Errors |
141
+ |--------|--------------------|--------------|------------------------------------|------------------------------------------------------------------|------------------------------|-------------------------------|
142
+ | GET | `/dashboard-item` | Authenticated | Lists items with pagination and filter by dashboard | Query: pagination (page?: number, pageSize?: number), Query param: dashboardId?: number | Paginated list of items | - |
143
+ | POST | `/dashboard-item` | Authenticated | Creates an item | Body: `CreateDashboardItemDTO` | Created item | - |
144
+ | DELETE | `/dashboard-item/:id` | Authenticated | Deletes an item by ID | Path param: `id` (int) | `{ success: true }` | 404: Item not found |
145
+
146
+ ### Dashboard Role Module (`/dashboard-role`)
147
+
148
+ | Method | Path | Auth | Description | Parameters / Body | Response | Common Errors |
149
+ |--------|--------------------|--------------|---------------------------------------|------------------------------------------------------------------|------------------------------|-------------------------------|
150
+ | GET | `/dashboard-role` | Authenticated | Lists relations with pagination or by dashboard | Query: pagination (page?: number, pageSize?: number), Query param: dashboardId?: number | Paginated or list of relations | - |
151
+ | POST | `/dashboard-role` | Authenticated | Creates a dashboard-role relation | Body: `CreateDashboardRoleDTO` | Created relation | Error if the relation already exists |
152
+ | POST | `/dashboard-role/batch` | Authenticated | Bulk-creates relations | Body: `CreateDashboardRoleBatchDTO` | { success: boolean, created: number, skipped: number, message: string } | - |
153
+ | DELETE | `/dashboard-role/:id` | Authenticated | Deletes a relation by ID | Path param: `id` (int) | `{ success: true }` | 404: Relation not found |
154
+ | DELETE | `/dashboard-role/dashboard/:dashboardId/role/:roleId` | Authenticated | Deletes a relation by dashboard and role | Path params: `dashboardId` (int), `roleId` (int) | `{ success: true }` | 404: Relation not found |
155
+
156
+ ### Dashboard User Module (`/dashboard-user`)
157
+
158
+ | Method | Path | Auth | Description | Parameters / Body | Response | Common Errors |
159
+ |--------|--------------------|--------------|------------------------------------|------------------------------------------------------------------|------------------------------|-------------------------------|
160
+ | GET | `/dashboard-user` | Authenticated | Lists relations with pagination | Query: pagination (page?: number, pageSize?: number) | Paginated list of relations | - |
161
+ | GET | `/dashboard-user/:id` | Authenticated | Gets a relation by ID | Path param: `id` (int) | Relation object | - |
162
+ | POST | `/dashboard-user` | Authenticated | Creates a relation | Body: `CreateDTO` (dashboard_id: number, user_id: number) | Created relation | - |
163
+ | PATCH | `/dashboard-user/:id` | Authenticated | Updates a relation | Path param: `id` (int), Body: `UpdateDTO` | Updated relation | - |
164
+ | DELETE | `/dashboard-user` | Authenticated | Bulk-deletes relations | Body: `DeleteDTO` (ids: number[]) | `{ count: number }` | 400: No id provided |
165
+
166
+ ## 4. Authentication and authorization rules
167
+
168
+ - Most endpoints require authentication via a JWT token.
169
+ - Public endpoints are explicitly marked.
170
+ - Access control is based on roles and permissions.
171
+ - MFA (Multi-Factor Authentication) is supported via TOTP, email, and recovery codes.
172
+ - WebAuthn is supported for strong authentication.
173
+ - Refresh tokens are managed via HTTP-only cookies or in the request body.
174
+ - Sensitive operations (create, update, delete) require authentication and the appropriate permissions.
175
+
176
+ ## 5. Request/response structures
177
+
178
+ ### Main DTOs of the AI module
178
179
 
179
180
  - **ChatDTO**
180
181
 
181
182
  ```ts
182
183
  {
183
- message: string; // obrigatório
184
- provider?: 'openai' | 'gemini'; // opcional, padrão 'openai'
185
- model?: string; // opcional
186
- systemPrompt?: string; // opcional
187
- file_id?: number; // opcional
184
+ message: string; // required
185
+ provider?: 'openai' | 'gemini'; // optional, default 'openai'
186
+ model?: string; // optional
187
+ systemPrompt?: string; // optional
188
+ file_id?: number; // optional
188
189
  }
189
190
  ```
190
191
 
@@ -192,8 +193,8 @@ O módulo `@hed-hog/core` é o núcleo do monorepo HedHog, responsável por forn
192
193
 
193
194
  ```ts
194
195
  {
195
- message: string; // obrigatório
196
- file_id?: number; // opcional
196
+ message: string; // required
197
+ file_id?: number; // optional
197
198
  }
198
199
  ```
199
200
 
@@ -201,10 +202,10 @@ O módulo `@hed-hog/core` é o núcleo do monorepo HedHog, responsável por forn
201
202
 
202
203
  ```ts
203
204
  {
204
- slug: string; // obrigatório
205
- provider?: 'openai' | 'gemini'; // opcional, padrão 'openai'
206
- model?: string; // opcional
207
- instructions?: string; // opcional
205
+ slug: string; // required
206
+ provider?: 'openai' | 'gemini'; // optional, default 'openai'
207
+ model?: string; // optional
208
+ instructions?: string; // optional
208
209
  }
209
210
  ```
210
211
 
@@ -223,19 +224,19 @@ O módulo `@hed-hog/core` é o núcleo do monorepo HedHog, responsável por forn
223
224
 
224
225
  ```ts
225
226
  {
226
- ids: number[]; // array de IDs para deleção, mínimo 1 item
227
+ ids: number[]; // array of IDs to delete, minimum 1 item
227
228
  }
228
229
  ```
229
230
 
230
- ### DTOs principais do módulo Auth
231
+ ### Main DTOs of the Auth module
231
232
 
232
233
  - **LoginDTO**
233
234
 
234
235
  ```ts
235
236
  {
236
- email: string; // obrigatório, email válido
237
- password: string; // obrigatório, senha forte mínima 6 caracteres
238
- refreshToken?: boolean; // opcional, padrão false
237
+ email: string; // required, valid email
238
+ password: string; // required, strong password, minimum 6 characters
239
+ refreshToken?: boolean; // optional, default false
239
240
  }
240
241
  ```
241
242
 
@@ -243,8 +244,8 @@ O módulo `@hed-hog/core` é o núcleo do monorepo HedHog, responsável por forn
243
244
 
244
245
  ```ts
245
246
  {
246
- token: string; // obrigatório
247
- code: string; // obrigatório, código PIN
247
+ token: string; // required
248
+ code: string; // required, PIN code
248
249
  }
249
250
  ```
250
251
 
@@ -252,7 +253,7 @@ O módulo `@hed-hog/core` é o núcleo do monorepo HedHog, responsável por forn
252
253
 
253
254
  ```ts
254
255
  {
255
- token: string; // obrigatório
256
+ token: string; // required
256
257
  }
257
258
  ```
258
259
 
@@ -260,9 +261,9 @@ O módulo `@hed-hog/core` é o núcleo do monorepo HedHog, responsável por forn
260
261
 
261
262
  ```ts
262
263
  {
263
- code: string; // obrigatório
264
- token: string; // obrigatório, JWT
265
- methodType?: 'totp' | 'email' | 'recovery'; // opcional
264
+ code: string; // required
265
+ token: string; // required, JWT
266
+ methodType?: 'totp' | 'email' | 'recovery'; // optional
266
267
  }
267
268
  ```
268
269
 
@@ -270,8 +271,8 @@ O módulo `@hed-hog/core` é o núcleo do monorepo HedHog, responsável por forn
270
271
 
271
272
  ```ts
272
273
  {
273
- code: string; // obrigatório
274
- token: string; // obrigatório, JWT
274
+ code: string; // required
275
+ token: string; // required, JWT
275
276
  }
276
277
  ```
277
278
 
@@ -279,7 +280,7 @@ O módulo `@hed-hog/core` é o núcleo do monorepo HedHog, responsável por forn
279
280
 
280
281
  ```ts
281
282
  {
282
- email: string; // obrigatório, email válido
283
+ email: string; // required, valid email
283
284
  }
284
285
  ```
285
286
 
@@ -287,19 +288,19 @@ O módulo `@hed-hog/core` é o núcleo do monorepo HedHog, responsável por forn
287
288
 
288
289
  ```ts
289
290
  {
290
- password: string; // obrigatório, mínimo 8 caracteres
291
- code: string; // obrigatório
291
+ password: string; // required, minimum 8 characters
292
+ code: string; // required
292
293
  }
293
294
  ```
294
295
 
295
- ### DTOs principais do módulo Dashboard
296
+ ### Main DTOs of the Dashboard module
296
297
 
297
298
  - **CreateDashboardDTO**
298
299
 
299
300
  ```ts
300
301
  {
301
- slug: string; // obrigatório
302
- locale: Record<string, { name: string }>; // obrigatório
302
+ slug: string; // required
303
+ locale: Record<string, { name: string }>; // required
303
304
  }
304
305
  ```
305
306
 
@@ -449,70 +450,70 @@ O módulo `@hed-hog/core` é o núcleo do monorepo HedHog, responsável por forn
449
450
  }
450
451
  ```
451
452
 
452
- ## 6. Erros comuns
453
+ ## 6. Common errors
453
454
 
454
455
  - **400 Bad Request**
455
456
 
456
- - Chave API não configurada para OpenAI ou Gemini.
457
- - Slug de agente AI já existente.
458
- - Código de verificação inválido ou expirado.
459
- - Refresh token não fornecido.
460
- - Acesso negado por credenciais inválidas.
461
- - MFA não configurado ou código inválido.
462
- - Tentativa de deletar itens inexistentes.
463
- - Requisição inválida para criação de relações duplicadas.
464
- - Arquivo inválido para preview de componente.
465
- - Operação de preview disponível somente em ambiente de desenvolvimento.
466
- - OAuth: provider não habilitado (`oauth-<provider>-enabled` desligado) ou não suportado.
467
- - OAuth: perfil de integração não configurado (`oauth-<provider>-profile-id` vazio) ou não encontrado.
468
- - OAuth: código de autorização ausente, redirect URI de mobile inválida, origem do callback inválida (origin binding do hub) ou callback já processado.
457
+ - API key not configured for OpenAI or Gemini.
458
+ - AI agent slug already exists.
459
+ - Invalid or expired verification code.
460
+ - Refresh token not provided.
461
+ - Access denied due to invalid credentials.
462
+ - MFA not configured or invalid code.
463
+ - Attempt to delete non-existent items.
464
+ - Invalid request to create duplicate relations.
465
+ - Invalid file for component preview.
466
+ - Preview operation available only in the development environment.
467
+ - OAuth: provider not enabled (`oauth-<provider>-enabled` off) or not supported.
468
+ - OAuth: integration profile not configured (`oauth-<provider>-profile-id` empty) or not found.
469
+ - OAuth: missing authorization code, invalid mobile redirect URI, invalid callback origin (hub origin binding), or callback already processed.
469
470
 
470
471
  - **404 Not Found**
471
472
 
472
- - Agente AI não encontrado por ID ou slug.
473
- - Dashboard, componente ou relação não encontrada.
474
- - Desafio (challenge) para verificação não encontrado ou expirado.
473
+ - AI agent not found by ID or slug.
474
+ - Dashboard, component, or relation not found.
475
+ - Verification challenge not found or expired.
475
476
 
476
477
  - **403 Forbidden**
477
478
 
478
- - Acesso negado a dashboards ou recursos protegidos.
479
- - Tentativa de salvar preview fora do ambiente de desenvolvimento.
479
+ - Access denied to dashboards or protected resources.
480
+ - Attempt to save a preview outside the development environment.
480
481
 
481
482
  - **409 Conflict**
482
483
 
483
- - OAuth: callback já em processamento (lock de idempotência por código).
484
+ - OAuth: callback already being processed (idempotency lock by code).
484
485
 
485
486
  - **503 Service Unavailable**
486
487
 
487
- - OAuth: falha na comunicação com o provider (upstream).
488
+ - OAuth: failure communicating with the provider (upstream).
488
489
 
489
- ## 7. Banco de dados (tabelas YAML)
490
+ ## 7. Database (YAML tables)
490
491
 
491
492
  ### ai_agent
492
493
 
493
494
  ```yaml
494
- finalidade: Armazena agentes de inteligência artificial configurados no sistema.
495
- colunas:
495
+ purpose: Stores AI agents configured in the system.
496
+ columns:
496
497
  - id: integer, PK, auto-increment
497
- - slug: string, único, não nulo
498
- - provider: enum('openai', 'gemini'), não nulo
498
+ - slug: string, unique, not null
499
+ - provider: enum('openai', 'gemini'), not null
499
500
  - model: string, nullable
500
501
  - instructions: string, nullable
501
502
  - external_agent_id: string, nullable
502
- - created_at: timestamp, não nulo, default NOW()
503
- - updated_at: timestamp, não nulo, default NOW()
503
+ - created_at: timestamp, not null, default NOW()
504
+ - updated_at: timestamp, not null, default NOW()
504
505
  defaults:
505
506
  - created_at: NOW()
506
507
  - updated_at: NOW()
507
- nulabilidade:
508
+ nullability:
508
509
  - model: nullable
509
510
  - instructions: nullable
510
511
  - external_agent_id: nullable
511
- integridade:
512
- - slug único
513
- indices:
512
+ integrity:
513
+ - unique slug
514
+ indexes:
514
515
  - id (PK)
515
- - slug (único)
516
+ - slug (unique)
516
517
  enums:
517
518
  - provider: ['openai', 'gemini']
518
519
  ```
@@ -520,36 +521,36 @@ enums:
520
521
  ### user
521
522
 
522
523
  ```yaml
523
- finalidade: Armazena usuários do sistema.
524
- colunas:
524
+ purpose: Stores the system's users.
525
+ columns:
525
526
  - id: integer, PK, auto-increment
526
- - name: string, não nulo
527
+ - name: string, not null
527
528
  - photo_id: integer, nullable
528
529
  - last_login_at: timestamp, nullable
529
- - created_at: timestamp, não nulo, default NOW()
530
- - updated_at: timestamp, não nulo, default NOW()
531
- integridade:
532
- - PK em id
533
- indices:
530
+ - created_at: timestamp, not null, default NOW()
531
+ - updated_at: timestamp, not null, default NOW()
532
+ integrity:
533
+ - PK on id
534
+ indexes:
534
535
  - id (PK)
535
536
  ```
536
537
 
537
538
  ### user_mfa
538
539
 
539
540
  ```yaml
540
- finalidade: Armazena métodos de autenticação multifator dos usuários.
541
- colunas:
541
+ purpose: Stores users' multi-factor authentication methods.
542
+ columns:
542
543
  - id: integer, PK, auto-increment
543
- - user_id: integer, FK para user.id, não nulo
544
- - name: string, não nulo
545
- - type: enum('totp', 'email', 'webauthn'), não nulo
544
+ - user_id: integer, FK to user.id, not null
545
+ - name: string, not null
546
+ - type: enum('totp', 'email', 'webauthn'), not null
546
547
  - verified_at: timestamp, nullable
547
548
  - suspended_until: timestamp, nullable
548
- - created_at: timestamp, não nulo, default NOW()
549
- - updated_at: timestamp, não nulo, default NOW()
550
- integridade:
551
- - FK user_id referencia user.id
552
- indices:
549
+ - created_at: timestamp, not null, default NOW()
550
+ - updated_at: timestamp, not null, default NOW()
551
+ integrity:
552
+ - FK user_id references user.id
553
+ indexes:
553
554
  - id (PK)
554
555
  enums:
555
556
  - type: ['totp', 'email', 'webauthn']
@@ -558,241 +559,241 @@ enums:
558
559
  ### user_identifier
559
560
 
560
561
  ```yaml
561
- finalidade: Identificadores do usuário, como emails.
562
- colunas:
562
+ purpose: User identifiers, such as emails.
563
+ columns:
563
564
  - id: integer, PK, auto-increment
564
- - user_id: integer, FK para user.id, não nulo
565
- - type: string, não nulo (ex: 'email')
566
- - value: string, não nulo
565
+ - user_id: integer, FK to user.id, not null
566
+ - type: string, not null (e.g.: 'email')
567
+ - value: string, not null
567
568
  - verified_at: timestamp, nullable
568
- - enabled: boolean, não nulo, default true
569
- - created_at: timestamp, não nulo, default NOW()
570
- - updated_at: timestamp, não nulo, default NOW()
571
- integridade:
572
- - FK user_id referencia user.id
573
- indices:
569
+ - enabled: boolean, not null, default true
570
+ - created_at: timestamp, not null, default NOW()
571
+ - updated_at: timestamp, not null, default NOW()
572
+ integrity:
573
+ - FK user_id references user.id
574
+ indexes:
574
575
  - id (PK)
575
576
  ```
576
577
 
577
578
  ### user_session
578
579
 
579
580
  ```yaml
580
- finalidade: Sessões de usuários autenticados.
581
- colunas:
581
+ purpose: Sessions of authenticated users.
582
+ columns:
582
583
  - id: integer, PK, auto-increment
583
- - user_id: integer, FK para user.id, não nulo
584
- - token: string, não nulo
584
+ - user_id: integer, FK to user.id, not null
585
+ - token: string, not null
585
586
  - ip_address: string, nullable
586
587
  - user_agent: string, nullable
587
- - created_at: timestamp, não nulo, default NOW()
588
- - expires_at: timestamp, não nulo
588
+ - created_at: timestamp, not null, default NOW()
589
+ - expires_at: timestamp, not null
589
590
  - revoked_at: timestamp, nullable
590
- integridade:
591
- - FK user_id referencia user.id
592
- indices:
591
+ integrity:
592
+ - FK user_id references user.id
593
+ indexes:
593
594
  - id (PK)
594
595
  ```
595
596
 
596
597
  ### user_activity
597
598
 
598
599
  ```yaml
599
- finalidade: Registro de atividades dos usuários.
600
- colunas:
600
+ purpose: Log of user activities.
601
+ columns:
601
602
  - id: integer, PK, auto-increment
602
- - user_id: integer, FK para user.id, não nulo
603
- - action: string, não nulo
604
- - created_at: timestamp, não nulo, default NOW()
605
- integridade:
606
- - FK user_id referencia user.id
607
- indices:
603
+ - user_id: integer, FK to user.id, not null
604
+ - action: string, not null
605
+ - created_at: timestamp, not null, default NOW()
606
+ integrity:
607
+ - FK user_id references user.id
608
+ indexes:
608
609
  - id (PK)
609
610
  ```
610
611
 
611
612
  ### role
612
613
 
613
614
  ```yaml
614
- finalidade: Papéis (roles) do sistema para controle de acesso.
615
- colunas:
615
+ purpose: System roles for access control.
616
+ columns:
616
617
  - id: integer, PK, auto-increment
617
- - slug: string, único, não nulo
618
- - created_at: timestamp, não nulo, default NOW()
619
- - updated_at: timestamp, não nulo, default NOW()
620
- integridade:
621
- - slug único
622
- indices:
618
+ - slug: string, unique, not null
619
+ - created_at: timestamp, not null, default NOW()
620
+ - updated_at: timestamp, not null, default NOW()
621
+ integrity:
622
+ - unique slug
623
+ indexes:
623
624
  - id (PK)
624
- - slug (único)
625
+ - slug (unique)
625
626
  ```
626
627
 
627
628
  ### role_user
628
629
 
629
630
  ```yaml
630
- finalidade: Relação muitos-para-muitos entre usuários e roles.
631
- colunas:
631
+ purpose: Many-to-many relation between users and roles.
632
+ columns:
632
633
  - id: integer, PK, auto-increment
633
- - user_id: integer, FK para user.id, não nulo
634
- - role_id: integer, FK para role.id, não nulo
635
- integridade:
636
- - FK user_id referencia user.id
637
- - FK role_id referencia role.id
638
- indices:
634
+ - user_id: integer, FK to user.id, not null
635
+ - role_id: integer, FK to role.id, not null
636
+ integrity:
637
+ - FK user_id references user.id
638
+ - FK role_id references role.id
639
+ indexes:
639
640
  - id (PK)
640
641
  ```
641
642
 
642
643
  ### dashboard
643
644
 
644
645
  ```yaml
645
- finalidade: Dashboards configuráveis do sistema.
646
- colunas:
646
+ purpose: The system's configurable dashboards.
647
+ columns:
647
648
  - id: integer, PK, auto-increment
648
- - slug: string, único, não nulo
649
- - created_at: timestamp, não nulo, default NOW()
650
- - updated_at: timestamp, não nulo, default NOW()
651
- integridade:
652
- - slug único
653
- indices:
649
+ - slug: string, unique, not null
650
+ - created_at: timestamp, not null, default NOW()
651
+ - updated_at: timestamp, not null, default NOW()
652
+ integrity:
653
+ - unique slug
654
+ indexes:
654
655
  - id (PK)
655
- - slug (único)
656
+ - slug (unique)
656
657
  ```
657
658
 
658
659
  ### dashboard_component
659
660
 
660
661
  ```yaml
661
- finalidade: Componentes que podem ser usados em dashboards.
662
- colunas:
662
+ purpose: Components that can be used in dashboards.
663
+ columns:
663
664
  - id: integer, PK, auto-increment
664
- - slug: string, único, não nulo
665
+ - slug: string, unique, not null
665
666
  - library_slug: string, nullable
666
667
  - min_width: integer, nullable
667
668
  - max_width: integer, nullable
668
669
  - min_height: integer, nullable
669
670
  - max_height: integer, nullable
670
- - width: integer, não nulo
671
- - height: integer, não nulo
672
- - is_resizable: boolean, não nulo, default true
673
- - created_at: timestamp, não nulo, default NOW()
674
- - updated_at: timestamp, não nulo, default NOW()
675
- integridade:
676
- - slug único
677
- indices:
671
+ - width: integer, not null
672
+ - height: integer, not null
673
+ - is_resizable: boolean, not null, default true
674
+ - created_at: timestamp, not null, default NOW()
675
+ - updated_at: timestamp, not null, default NOW()
676
+ integrity:
677
+ - unique slug
678
+ indexes:
678
679
  - id (PK)
679
- - slug (único)
680
+ - slug (unique)
680
681
  ```
681
682
 
682
683
  ### dashboard_role
683
684
 
684
685
  ```yaml
685
- finalidade: Relação entre dashboards e roles para controle de acesso.
686
- colunas:
686
+ purpose: Relation between dashboards and roles for access control.
687
+ columns:
687
688
  - id: integer, PK, auto-increment
688
- - dashboard_id: integer, FK para dashboard.id, não nulo
689
- - role_id: integer, FK para role.id, não nulo
690
- integridade:
691
- - FK dashboard_id referencia dashboard.id
692
- - FK role_id referencia role.id
693
- indices:
689
+ - dashboard_id: integer, FK to dashboard.id, not null
690
+ - role_id: integer, FK to role.id, not null
691
+ integrity:
692
+ - FK dashboard_id references dashboard.id
693
+ - FK role_id references role.id
694
+ indexes:
694
695
  - id (PK)
695
696
  ```
696
697
 
697
698
  ### dashboard_user
698
699
 
699
700
  ```yaml
700
- finalidade: Relação entre dashboards e usuários.
701
- colunas:
701
+ purpose: Relation between dashboards and users.
702
+ columns:
702
703
  - id: integer, PK, auto-increment
703
- - dashboard_id: integer, FK para dashboard.id, não nulo
704
- - user_id: integer, FK para user.id, não nulo
705
- - is_home: boolean, não nulo, default false
706
- integridade:
707
- - FK dashboard_id referencia dashboard.id
708
- - FK user_id referencia user.id
709
- indices:
704
+ - dashboard_id: integer, FK to dashboard.id, not null
705
+ - user_id: integer, FK to user.id, not null
706
+ - is_home: boolean, not null, default false
707
+ integrity:
708
+ - FK dashboard_id references dashboard.id
709
+ - FK user_id references user.id
710
+ indexes:
710
711
  - id (PK)
711
712
  ```
712
713
 
713
714
  ### dashboard_item
714
715
 
715
716
  ```yaml
716
- finalidade: Itens (widgets) dentro de dashboards.
717
- colunas:
717
+ purpose: Items (widgets) within dashboards.
718
+ columns:
718
719
  - id: integer, PK, auto-increment
719
- - dashboard_id: integer, FK para dashboard.id, não nulo
720
- - component_id: integer, FK para dashboard_component.id, não nulo
721
- - width: integer, não nulo
722
- - height: integer, não nulo
723
- - x_axis: integer, não nulo
724
- - y_axis: integer, não nulo
725
- integridade:
726
- - FK dashboard_id referencia dashboard.id
727
- - FK component_id referencia dashboard_component.id
728
- indices:
720
+ - dashboard_id: integer, FK to dashboard.id, not null
721
+ - component_id: integer, FK to dashboard_component.id, not null
722
+ - width: integer, not null
723
+ - height: integer, not null
724
+ - x_axis: integer, not null
725
+ - y_axis: integer, not null
726
+ integrity:
727
+ - FK dashboard_id references dashboard.id
728
+ - FK component_id references dashboard_component.id
729
+ indexes:
729
730
  - id (PK)
730
731
  ```
731
732
 
732
733
  ### dashboard_component_role
733
734
 
734
735
  ```yaml
735
- finalidade: Relação entre componentes de dashboard e roles.
736
- colunas:
736
+ purpose: Relation between dashboard components and roles.
737
+ columns:
737
738
  - id: integer, PK, auto-increment
738
- - component_id: integer, FK para dashboard_component.id, não nulo
739
- - role_id: integer, FK para role.id, não nulo
740
- integridade:
741
- - FK component_id referencia dashboard_component.id
742
- - FK role_id referencia role.id
743
- indices:
739
+ - component_id: integer, FK to dashboard_component.id, not null
740
+ - role_id: integer, FK to role.id, not null
741
+ integrity:
742
+ - FK component_id references dashboard_component.id
743
+ - FK role_id references role.id
744
+ indexes:
744
745
  - id (PK)
745
746
  ```
746
747
 
747
748
  ### oauth_mobile_state_token
748
749
 
749
750
  ```yaml
750
- finalidade: Estado assinado de uso único para o fluxo OAuth iniciado por apps nativos (mobile/desktop), vinculando o redirect URI do app ao provider e ao fluxo.
751
- colunas:
751
+ purpose: Single-use signed state for the OAuth flow started by native apps (mobile/desktop), binding the app's redirect URI to the provider and the flow.
752
+ columns:
752
753
  - id: bigint, PK, auto-increment
753
- - token_hash: string, único, não nulo
754
- - provider: string, não nulo
755
- - redirect_uri: string, não nulo (custom scheme do app nativo)
756
- - flow_type: string, não nulo ('login' | 'register' | 'connect')
757
- - expires_at: timestamptz, não nulo
758
- - consumed_at: timestamptz, nullable (marca consumo único)
759
- - created_at: timestamptz, não nulo, default NOW()
760
- integridade:
761
- - token_hash único
762
- indices:
754
+ - token_hash: string, unique, not null
755
+ - provider: string, not null
756
+ - redirect_uri: string, not null (native app's custom scheme)
757
+ - flow_type: string, not null ('login' | 'register' | 'connect')
758
+ - expires_at: timestamptz, not null
759
+ - consumed_at: timestamptz, nullable (marks single use)
760
+ - created_at: timestamptz, not null, default NOW()
761
+ integrity:
762
+ - unique token_hash
763
+ indexes:
763
764
  - id (PK)
764
765
  - expires_at
765
766
  - (provider, redirect_uri)
766
767
  ```
767
768
 
768
- > Usada apenas pelo fluxo mobile (`GET /oauth/:provider/mobile/auth-url`). O hub multi-app web (`state` assinado `hhweb.<app>.<flow>.<sig>`) é stateless — a assinatura HMAC é verificada sem persistência em banco.
769
+ > Used only by the mobile flow (`GET /oauth/:provider/mobile/auth-url`). The multi-app web hub (signed `state`, `hhweb.<app>.<flow>.<sig>`) is stateless — the HMAC signature is verified without database persistence.
769
770
 
770
- ## 8. Regras de negócio relevantes
771
+ ## 8. Relevant business rules
771
772
 
772
- - Agentes AI podem ser criados, atualizados e deletados, com integração direta com APIs OpenAI e Gemini.
773
- - Chat com IA suporta anexos de arquivos, com extração de texto para PDFs e arquivos de texto.
774
- - MFA obrigatório pode ser configurado, com suporte a email, TOTP e WebAuthn.
775
- - Tokens de acesso e refresh são gerenciados com segurança, incluindo cookies HTTP-only.
776
- - Dashboards são personalizados por usuário, com controle de acesso baseado em roles.
777
- - Layouts de dashboards e widgets podem ser salvos e recuperados por usuário.
778
- - Sistema coleta estatísticas de uso, sessões, emails enviados e segurança da conta.
779
- - Validações rigorosas são aplicadas via DTOs e classes de validação.
780
- - Operações de criação em batch evitam duplicações e retornam contagem de criados e ignorados.
781
- - A remoção de widgets do dashboard do usuário ainda não está implementada.
782
- - Preview de componentes de dashboard é permitido somente em ambiente de desenvolvimento e aceita apenas imagens.
783
- - MFA via email envia códigos para múltiplos emails associados ao usuário.
784
- - WebAuthn é suportado para autenticação forte com geração e verificação de desafios.
785
- - **OAuth — uma URL de callback por provider**: o fluxo (login/registro/conexão) e o app iniciador viajam assinados no `state`, nunca no path; cada provider é habilitado/desabilitado individualmente via setting `oauth-<provider>-enabled`, sem remover as credenciais configuradas (`oauth-<provider>-profile-id`, apontando para um `integration_profile`).
786
- - **OAuth — hub multi-app**: o app da setting `url` (ex.: admin) recebe o callback do provider e, se o `state` indicar outro app iniciador, reencaminha o browser para a página de callback correspondente desse app, resolvendo a origem via `app-urls`. A troca do `code` por tokens só é aceita quando o header `Origin`/`Referer` da requisição bate com a origem assinada no `state` (origin binding), prevenendo interceptação do código entre apps.
787
- - **OAuth — auto-vinculação por e-mail**: no login, se não existir `user_account` para o provider mas já existir um `user_identifier` de e-mail habilitado igual ao do perfil OAuth, o fluxo vira uma conexão (`connect`) automática à conta existente em vez de criar um novo usuário; só cria conta nova (`register`) quando nenhum usuário corresponde ao e-mail.
788
- - **OAuth — roles no cadastro**: novos usuários registrados via OAuth recebem as roles configuradas na setting `oauth-role-assignment`.
789
- - **OAuth — mobile**: apps nativos usam `/oauth/:provider/mobile/auth-url` com estado assinado e persistido em `oauth_mobile_state_token` (TTL de 10 minutos, consumo único).
790
- - **OAuth — Apple (Sign in with Apple)**: sem `client_secret` estático — o `private_key` (chave EC `.p8`) assina, a cada troca de código, um JWT `client_secret` (ES256, `iss`=team ID, `sub`=Services ID, `kid`=key ID, TTL curto). Como escopos exigem `response_mode=form_post`, o callback é `POST /oauth/apple/callback` (única URL, no backend), convertido para o mesmo redirect GET dos demais providers. A identidade vem do `id_token` (JWT decodificado, sem verificação de assinatura — entregue diretamente pela Apple na troca servidor-a-servidor); o nome só é enviado por Apple na primeira autorização e não é capturado, então o nome do usuário cai para o local-part do e-mail.
791
- - **OAuth — LinkedIn**: usa "Sign In with LinkedIn using OpenID Connect" (`scope=openid profile email`), com identidade obtida em uma única chamada a `GET /v2/userinfo` (claims OIDC padrão), sem o par de chamadas legado `/v2/me` + `/v2/emailAddress`.
773
+ - AI agents can be created, updated, and deleted, with direct integration with the OpenAI and Gemini APIs.
774
+ - AI chat supports file attachments, with text extraction for PDFs and text files.
775
+ - Mandatory MFA can be configured, with support for email, TOTP, and WebAuthn.
776
+ - Access and refresh tokens are managed securely, including HTTP-only cookies.
777
+ - Dashboards are personalized per user, with role-based access control.
778
+ - Dashboard and widget layouts can be saved and retrieved per user.
779
+ - The system collects usage statistics, sessions, sent emails, and account security data.
780
+ - Strict validations are applied via DTOs and validation classes.
781
+ - Batch create operations avoid duplication and return counts of created and skipped items.
782
+ - Removing widgets from a user's dashboard is not yet implemented.
783
+ - Dashboard component previews are allowed only in the development environment and accept images only.
784
+ - Email MFA sends codes to all email addresses associated with the user.
785
+ - WebAuthn is supported for strong authentication with challenge generation and verification.
786
+ - **OAuth — one callback URL per provider**: the flow (login/register/connect) and the initiating app travel signed in the `state`, never in the path; each provider is enabled/disabled individually via the `oauth-<provider>-enabled` setting, without removing the configured credentials (`oauth-<provider>-profile-id`, pointing to an `integration_profile`).
787
+ - **OAuth — multi-app hub**: the app in the `url` setting (e.g. admin) receives the callback from the provider and, if the `state` indicates a different initiating app, forwards the browser to that app's corresponding callback page, resolving the origin via `app-urls`. Exchanging the `code` for tokens is only accepted when the request's `Origin`/`Referer` header matches the origin signed in the `state` (origin binding), preventing code interception between apps.
788
+ - **OAuth — email auto-linking**: on login, if no `user_account` exists for the provider but an enabled email `user_identifier` matching the OAuth profile's email already exists, the flow becomes an automatic connection (`connect`) to the existing account instead of creating a new user; a new account (`register`) is only created when no user matches the email.
789
+ - **OAuth — roles on sign-up**: new users registered via OAuth receive the roles configured in the `oauth-role-assignment` setting.
790
+ - **OAuth — mobile**: native apps use `/oauth/:provider/mobile/auth-url` with a signed state persisted in `oauth_mobile_state_token` (10-minute TTL, single use).
791
+ - **OAuth — Apple (Sign in with Apple)**: no static `client_secret` — the `private_key` (EC `.p8` key) signs a `client_secret` JWT (ES256, `iss`=team ID, `sub`=Services ID, `kid`=key ID, short TTL) on each code exchange. Since scopes require `response_mode=form_post`, the callback is `POST /oauth/apple/callback` (a single URL, on the backend), converted to the same GET redirect used by the other providers. Identity comes from the `id_token` (decoded JWT, without signature verification — delivered directly by Apple in the server-to-server exchange); the name is only sent by Apple on the first authorization and is not captured, so the user's name falls back to the local part of the email.
792
+ - **OAuth — LinkedIn**: uses "Sign In with LinkedIn using OpenID Connect" (`scope=openid profile email`), with identity obtained in a single call to `GET /v2/userinfo` (standard OIDC claims), without the legacy pair of calls `/v2/me` + `/v2/emailAddress`.
792
793
 
793
- ## 9. Guia rápido de uso (exemplos)
794
+ ## 9. Quick usage guide (examples)
794
795
 
795
- ### Criar agente AI
796
+ ### Create an AI agent
796
797
 
797
798
  ```http
798
799
  POST /ai/agent
@@ -800,64 +801,64 @@ Authorization: Bearer <token>
800
801
  Content-Type: application/json
801
802
 
802
803
  {
803
- "slug": "meu-agente",
804
+ "slug": "my-agent",
804
805
  "provider": "openai",
805
806
  "model": "gpt-4o-mini",
806
- "instructions": "Seja um assistente amigável."
807
+ "instructions": "Be a friendly assistant."
807
808
  }
808
809
  ```
809
810
 
810
- Resposta:
811
+ Response:
811
812
 
812
813
  ```json
813
814
  {
814
815
  "id": 1,
815
- "slug": "meu-agente",
816
+ "slug": "my-agent",
816
817
  "provider": "openai",
817
818
  "model": "gpt-4o-mini",
818
- "instructions": "Seja um assistente amigável.",
819
+ "instructions": "Be a friendly assistant.",
819
820
  "external_agent_id": "abc123",
820
821
  "created_at": "2024-06-01T12:00:00Z",
821
822
  "updated_at": "2024-06-01T12:00:00Z"
822
823
  }
823
824
  ```
824
825
 
825
- ### Chat com agente AI
826
+ ### Chat with an AI agent
826
827
 
827
828
  ```http
828
- POST /ai/agent/meu-agente/chat
829
+ POST /ai/agent/my-agent/chat
829
830
  Authorization: Bearer <token>
830
831
  Content-Type: multipart/form-data
831
832
 
832
833
  Form-data:
833
- - message: "Olá, como você está?"
834
- - file: (arquivo opcional)
834
+ - message: "Hi, how are you?"
835
+ - file: (optional file)
835
836
  ```
836
837
 
837
- Resposta:
838
+ Response:
838
839
 
839
840
  ```json
840
841
  {
841
- "slug": "meu-agente",
842
+ "slug": "my-agent",
842
843
  "provider": "openai",
843
844
  "model": "gpt-4o-mini",
844
- "content": "Olá! Estou bem, obrigado por perguntar."
845
+ "content": "Hello! I'm doing well, thanks for asking."
845
846
  }
846
847
  ```
847
848
 
848
- ### Login com email e senha
849
+ ### Login with email and password
849
850
 
850
851
  ```http
851
852
  POST /auth/login
852
853
  Content-Type: application/json
853
854
 
854
855
  {
855
- "email": "usuario@exemplo.com",
856
- "password": "senhaSegura123"
856
+ "email": "user@example.com",
857
+ "password": "strongPassword123"
857
858
  }
858
859
  ```
859
860
 
860
- Resposta:
861
+ Response:
861
862
 
862
863
  ```json
863
864
  {
@@ -866,21 +867,21 @@ Resposta:
866
867
  }
867
868
  ```
868
869
 
869
- ### Login OAuth iniciado por outro app (hub multi-app)
870
+ ### OAuth login started by another app (multi-app hub)
870
871
 
871
- Um app diferente do hub (ex.: `training`, chave `training` na setting `app-urls`) inicia o login redirecionando o browser para:
872
+ An app other than the hub (e.g. `training`, key `training` in the `app-urls` setting) starts the login by redirecting the browser to:
872
873
 
873
874
  ```http
874
875
  GET /oauth/google/login?redirectApp=training
875
876
  ```
876
877
 
877
- O backend responde com um `redirect` 302 para a URL de autorização do Google, contendo `state=hhweb.training.login.<assinatura>`. Após o consentimento, o Google redireciona para a única callback registrada (`${url}/callback/google`, o hub). O hub lê o `state`, resolve `training` via `app-urls` e reencaminha o browser para `${origem-do-training}/callback/google/login?code=...&state=...`. O app `training` então troca o código:
878
+ The backend responds with a 302 `redirect` to Google's authorization URL, containing `state=hhweb.training.login.<signature>`. After consent, Google redirects to the single registered callback (`${url}/callback/google`, the hub). The hub reads the `state`, resolves `training` via `app-urls`, and forwards the browser to `${training-origin}/callback/google/login?code=...&state=...`. The `training` app then exchanges the code:
878
879
 
879
880
  ```http
880
- GET /oauth/google/callback/login?code=<code>&state=hhweb.training.login.<assinatura>
881
+ GET /oauth/google/callback/login?code=<code>&state=hhweb.training.login.<signature>
881
882
  ```
882
883
 
883
- Resposta:
884
+ Response:
884
885
 
885
886
  ```json
886
887
  {
@@ -888,16 +889,16 @@ Resposta:
888
889
  }
889
890
  ```
890
891
 
891
- O `refreshToken` é definido no cookie httpOnly `rt` (não retorna no corpo, exceto quando a chamada informa `redirectUri`, usado pelo fluxo mobile).
892
+ The `refreshToken` is set in the httpOnly `rt` cookie (it is not returned in the body, except when the call provides `redirectUri`, used by the mobile flow).
892
893
 
893
- ### Obter informações do sistema
894
+ ### Get system information
894
895
 
895
896
  ```http
896
897
  GET /system
897
898
  Authorization: Bearer <token>
898
899
  ```
899
900
 
900
- Resposta (exemplo resumido):
901
+ Response (abbreviated example):
901
902
 
902
903
  ```json
903
904
  {
@@ -950,6 +951,4 @@ Resposta (exemplo resumido):
950
951
 
951
952
  ---
952
953
 
953
- Este README documenta o módulo `@hed-hog/core` com base no código-fonte e definições atuais, fornecendo uma visão técnica detalhada para desenvolvedores e integradores do sistema.
954
-
955
- ```
954
+ This README documents the `@hed-hog/core` module based on the current source code and definitions, providing a detailed technical overview for developers and integrators of the system.