@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 +414 -415
- package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +102 -0
- package/hedhog/frontend/app/integration/profiles/page.tsx.ejs +14 -1268
- package/hedhog/frontend/e2e/core.spec.ts.ejs +28 -0
- package/hedhog/frontend/messages/en.json +3 -1
- package/hedhog/frontend/messages/pt.json +3 -1
- package/package.json +4 -4
- package/src/integration/README.md +2 -0
package/README.md
CHANGED
|
@@ -1,190 +1,191 @@
|
|
|
1
|
-
```markdown
|
|
2
1
|
# @hed-hog/core
|
|
3
2
|
|
|
4
|
-
> **
|
|
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
|
-
|
|
5
|
+
Learn more about the HedHog Framework at **[hedhog.com](https://hedhog.com)**.
|
|
7
6
|
|
|
8
|
-
|
|
7
|
+
## 1. Module overview
|
|
9
8
|
|
|
10
|
-
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
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
|
-
###
|
|
22
|
-
|
|
23
|
-
|
|
|
24
|
-
|
|
25
|
-
| POST | `/ai/chat` |
|
|
26
|
-
| POST | `/ai/agent` |
|
|
27
|
-
| GET | `/ai/agent` |
|
|
28
|
-
| GET | `/ai/agent/id/:agentId` |
|
|
29
|
-
| GET | `/ai/agent/:slug` |
|
|
30
|
-
| PATCH | `/ai/agent/:agentId` |
|
|
31
|
-
| DELETE | `/ai/agent` |
|
|
32
|
-
| POST | `/ai/agent/:slug/chat` |
|
|
33
|
-
|
|
34
|
-
###
|
|
35
|
-
|
|
36
|
-
|
|
|
37
|
-
|
|
38
|
-
| GET | `/auth/verify` |
|
|
39
|
-
| GET | `/auth/roles` |
|
|
40
|
-
| POST | `/auth/refresh` |
|
|
41
|
-
| POST | `/auth/login` |
|
|
42
|
-
| POST | `/auth/login-email-verification` |
|
|
43
|
-
| POST | `/auth/login-email-verification-resend` |
|
|
44
|
-
| POST | `/auth/signup` |
|
|
45
|
-
| POST | `/auth/login-code` |
|
|
46
|
-
| POST | `/auth/login-recovery-code` |
|
|
47
|
-
| POST | `/auth/resend-mfa-code` |
|
|
48
|
-
| POST | `/auth/webauthn/generate` |
|
|
49
|
-
| POST | `/auth/webauthn/verify` |
|
|
50
|
-
| POST | `/auth/forgot` |
|
|
51
|
-
| POST | `/auth/logout` |
|
|
52
|
-
| POST | `/auth/forgot-reset` |
|
|
53
|
-
|
|
54
|
-
###
|
|
55
|
-
|
|
56
|
-
> **
|
|
57
|
-
|
|
58
|
-
|
|
|
59
|
-
|
|
60
|
-
| GET | `/oauth/github/callback` |
|
|
61
|
-
| POST | `/oauth/apple/callback` |
|
|
62
|
-
| GET | `/oauth/:provider/login` |
|
|
63
|
-
| GET | `/oauth/:provider/register` |
|
|
64
|
-
| GET | `/oauth/:provider/connect` |
|
|
65
|
-
| GET | `/oauth/:provider/mobile/auth-url` |
|
|
66
|
-
| GET | `/oauth/:provider/callback/login` |
|
|
67
|
-
| GET | `/oauth/:provider/callback/register` |
|
|
68
|
-
| GET | `/oauth/:provider/callback/connect` |
|
|
69
|
-
| DELETE | `/oauth/:provider` |
|
|
70
|
-
|
|
71
|
-
###
|
|
72
|
-
|
|
73
|
-
|
|
|
74
|
-
|
|
75
|
-
| GET | `/system` |
|
|
76
|
-
|
|
77
|
-
###
|
|
78
|
-
|
|
79
|
-
|
|
|
80
|
-
|
|
81
|
-
| GET | `/dashboard-core/home` |
|
|
82
|
-
| GET | `/dashboard-core/stats/overview/users` |
|
|
83
|
-
| GET | `/dashboard-core/stats/overview/mails` |
|
|
84
|
-
| GET | `/dashboard-core/stats/overview/system` |
|
|
85
|
-
| GET | `/dashboard-core/config/overview` |
|
|
86
|
-
| GET | `/dashboard-core/widgets/me` |
|
|
87
|
-
| GET | `/dashboard-core/user-dashboards` |
|
|
88
|
-
| GET | `/dashboard-core/templates` |
|
|
89
|
-
| POST | `/dashboard-core/dashboard` |
|
|
90
|
-
| PATCH | `/dashboard-core/dashboard/order` |
|
|
91
|
-
| PATCH | `/dashboard-core/dashboard/:slug` |
|
|
92
|
-
| POST | `/dashboard-core/dashboard/:slug/home` |
|
|
93
|
-
| GET | `/dashboard-core/dashboard/:slug/shares` |
|
|
94
|
-
| GET | `/dashboard-core/shareable-users/:slug` |
|
|
95
|
-
| POST | `/dashboard-core/dashboard/:slug/share` |
|
|
96
|
-
| DELETE | `/dashboard-core/dashboard/:slug/share/:sharedUserId` |
|
|
97
|
-
| DELETE | `/dashboard-core/dashboard/:slug` |
|
|
98
|
-
| GET | `/dashboard-core/access/:slug` |
|
|
99
|
-
| GET | `/dashboard-core/layout/:slug` |
|
|
100
|
-
| POST | `/dashboard-core/layout/:slug` |
|
|
101
|
-
| POST | `/dashboard-core/widget/:slug` |
|
|
102
|
-
| DELETE | `/dashboard-core/widget/:slug/:widgetId` |
|
|
103
|
-
| GET | `/dashboard-core/:slug` |
|
|
104
|
-
|
|
105
|
-
###
|
|
106
|
-
|
|
107
|
-
|
|
|
108
|
-
|
|
109
|
-
| GET | `/dashboard` |
|
|
110
|
-
| GET | `/dashboard/:id` |
|
|
111
|
-
| POST | `/dashboard` |
|
|
112
|
-
| PATCH | `/dashboard/:id` |
|
|
113
|
-
| DELETE | `/dashboard/:id` |
|
|
114
|
-
|
|
115
|
-
###
|
|
116
|
-
|
|
117
|
-
|
|
|
118
|
-
|
|
119
|
-
| GET | `/dashboard-component` |
|
|
120
|
-
| GET | `/dashboard-component/user` |
|
|
121
|
-
| GET | `/dashboard-component/:id` |
|
|
122
|
-
| POST | `/dashboard-component` |
|
|
123
|
-
| PATCH | `/dashboard-component/:id` |
|
|
124
|
-
| DELETE | `/dashboard-component/:id` |
|
|
125
|
-
| POST | `/dashboard-component/:id/preview` |
|
|
126
|
-
|
|
127
|
-
###
|
|
128
|
-
|
|
129
|
-
|
|
|
130
|
-
|
|
131
|
-
| GET | `/dashboard-component-role` |
|
|
132
|
-
| POST | `/dashboard-component-role` |
|
|
133
|
-
| POST | `/dashboard-component-role/batch` |
|
|
134
|
-
| DELETE | `/dashboard-component-role/:id` |
|
|
135
|
-
| DELETE | `/dashboard-component-role/component/:componentId/role/:roleId` |
|
|
136
|
-
|
|
137
|
-
###
|
|
138
|
-
|
|
139
|
-
|
|
|
140
|
-
|
|
141
|
-
| GET | `/dashboard-item` |
|
|
142
|
-
| POST | `/dashboard-item` |
|
|
143
|
-
| DELETE | `/dashboard-item/:id` |
|
|
144
|
-
|
|
145
|
-
###
|
|
146
|
-
|
|
147
|
-
|
|
|
148
|
-
|
|
149
|
-
| GET | `/dashboard-role` |
|
|
150
|
-
| POST | `/dashboard-role` |
|
|
151
|
-
| POST | `/dashboard-role/batch` |
|
|
152
|
-
| DELETE | `/dashboard-role/:id` |
|
|
153
|
-
| DELETE | `/dashboard-role/dashboard/:dashboardId/role/:roleId` |
|
|
154
|
-
|
|
155
|
-
###
|
|
156
|
-
|
|
157
|
-
|
|
|
158
|
-
|
|
159
|
-
| GET | `/dashboard-user` |
|
|
160
|
-
| GET | `/dashboard-user/:id` |
|
|
161
|
-
| POST | `/dashboard-user` |
|
|
162
|
-
| PATCH | `/dashboard-user/:id` |
|
|
163
|
-
| DELETE | `/dashboard-user` |
|
|
164
|
-
|
|
165
|
-
## 4.
|
|
166
|
-
|
|
167
|
-
-
|
|
168
|
-
-
|
|
169
|
-
-
|
|
170
|
-
- MFA (Multi-Factor Authentication)
|
|
171
|
-
- WebAuthn
|
|
172
|
-
- Refresh tokens
|
|
173
|
-
-
|
|
174
|
-
|
|
175
|
-
## 5.
|
|
176
|
-
|
|
177
|
-
### DTOs
|
|
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; //
|
|
184
|
-
provider?: 'openai' | 'gemini'; //
|
|
185
|
-
model?: string; //
|
|
186
|
-
systemPrompt?: string; //
|
|
187
|
-
file_id?: number; //
|
|
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; //
|
|
196
|
-
file_id?: number; //
|
|
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; //
|
|
205
|
-
provider?: 'openai' | 'gemini'; //
|
|
206
|
-
model?: string; //
|
|
207
|
-
instructions?: string; //
|
|
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
|
|
227
|
+
ids: number[]; // array of IDs to delete, minimum 1 item
|
|
227
228
|
}
|
|
228
229
|
```
|
|
229
230
|
|
|
230
|
-
### DTOs
|
|
231
|
+
### Main DTOs of the Auth module
|
|
231
232
|
|
|
232
233
|
- **LoginDTO**
|
|
233
234
|
|
|
234
235
|
```ts
|
|
235
236
|
{
|
|
236
|
-
email: string; //
|
|
237
|
-
password: string; //
|
|
238
|
-
refreshToken?: boolean; //
|
|
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; //
|
|
247
|
-
code: string; //
|
|
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; //
|
|
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; //
|
|
264
|
-
token: string; //
|
|
265
|
-
methodType?: 'totp' | 'email' | 'recovery'; //
|
|
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; //
|
|
274
|
-
token: string; //
|
|
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; //
|
|
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; //
|
|
291
|
-
code: string; //
|
|
291
|
+
password: string; // required, minimum 8 characters
|
|
292
|
+
code: string; // required
|
|
292
293
|
}
|
|
293
294
|
```
|
|
294
295
|
|
|
295
|
-
### DTOs
|
|
296
|
+
### Main DTOs of the Dashboard module
|
|
296
297
|
|
|
297
298
|
- **CreateDashboardDTO**
|
|
298
299
|
|
|
299
300
|
```ts
|
|
300
301
|
{
|
|
301
|
-
slug: string; //
|
|
302
|
-
locale: Record<string, { name: string }>; //
|
|
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.
|
|
453
|
+
## 6. Common errors
|
|
453
454
|
|
|
454
455
|
- **400 Bad Request**
|
|
455
456
|
|
|
456
|
-
-
|
|
457
|
-
-
|
|
458
|
-
-
|
|
459
|
-
- Refresh token
|
|
460
|
-
-
|
|
461
|
-
- MFA
|
|
462
|
-
-
|
|
463
|
-
-
|
|
464
|
-
-
|
|
465
|
-
-
|
|
466
|
-
- OAuth: provider
|
|
467
|
-
- OAuth:
|
|
468
|
-
- OAuth:
|
|
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
|
-
-
|
|
473
|
-
- Dashboard,
|
|
474
|
-
-
|
|
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
|
-
-
|
|
479
|
-
-
|
|
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
|
|
484
|
+
- OAuth: callback already being processed (idempotency lock by code).
|
|
484
485
|
|
|
485
486
|
- **503 Service Unavailable**
|
|
486
487
|
|
|
487
|
-
- OAuth:
|
|
488
|
+
- OAuth: failure communicating with the provider (upstream).
|
|
488
489
|
|
|
489
|
-
## 7.
|
|
490
|
+
## 7. Database (YAML tables)
|
|
490
491
|
|
|
491
492
|
### ai_agent
|
|
492
493
|
|
|
493
494
|
```yaml
|
|
494
|
-
|
|
495
|
-
|
|
495
|
+
purpose: Stores AI agents configured in the system.
|
|
496
|
+
columns:
|
|
496
497
|
- id: integer, PK, auto-increment
|
|
497
|
-
- slug: string,
|
|
498
|
-
- provider: enum('openai', 'gemini'),
|
|
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,
|
|
503
|
-
- updated_at: timestamp,
|
|
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
|
-
|
|
508
|
+
nullability:
|
|
508
509
|
- model: nullable
|
|
509
510
|
- instructions: nullable
|
|
510
511
|
- external_agent_id: nullable
|
|
511
|
-
|
|
512
|
-
- slug
|
|
513
|
-
|
|
512
|
+
integrity:
|
|
513
|
+
- unique slug
|
|
514
|
+
indexes:
|
|
514
515
|
- id (PK)
|
|
515
|
-
- slug (
|
|
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
|
-
|
|
524
|
-
|
|
524
|
+
purpose: Stores the system's users.
|
|
525
|
+
columns:
|
|
525
526
|
- id: integer, PK, auto-increment
|
|
526
|
-
- name: string,
|
|
527
|
+
- name: string, not null
|
|
527
528
|
- photo_id: integer, nullable
|
|
528
529
|
- last_login_at: timestamp, nullable
|
|
529
|
-
- created_at: timestamp,
|
|
530
|
-
- updated_at: timestamp,
|
|
531
|
-
|
|
532
|
-
- PK
|
|
533
|
-
|
|
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
|
-
|
|
541
|
-
|
|
541
|
+
purpose: Stores users' multi-factor authentication methods.
|
|
542
|
+
columns:
|
|
542
543
|
- id: integer, PK, auto-increment
|
|
543
|
-
- user_id: integer, FK
|
|
544
|
-
- name: string,
|
|
545
|
-
- type: enum('totp', 'email', 'webauthn'),
|
|
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,
|
|
549
|
-
- updated_at: timestamp,
|
|
550
|
-
|
|
551
|
-
- FK user_id
|
|
552
|
-
|
|
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
|
-
|
|
562
|
-
|
|
562
|
+
purpose: User identifiers, such as emails.
|
|
563
|
+
columns:
|
|
563
564
|
- id: integer, PK, auto-increment
|
|
564
|
-
- user_id: integer, FK
|
|
565
|
-
- type: string,
|
|
566
|
-
- value: string,
|
|
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,
|
|
569
|
-
- created_at: timestamp,
|
|
570
|
-
- updated_at: timestamp,
|
|
571
|
-
|
|
572
|
-
- FK user_id
|
|
573
|
-
|
|
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
|
-
|
|
581
|
-
|
|
581
|
+
purpose: Sessions of authenticated users.
|
|
582
|
+
columns:
|
|
582
583
|
- id: integer, PK, auto-increment
|
|
583
|
-
- user_id: integer, FK
|
|
584
|
-
- token: string,
|
|
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,
|
|
588
|
-
- expires_at: timestamp,
|
|
588
|
+
- created_at: timestamp, not null, default NOW()
|
|
589
|
+
- expires_at: timestamp, not null
|
|
589
590
|
- revoked_at: timestamp, nullable
|
|
590
|
-
|
|
591
|
-
- FK user_id
|
|
592
|
-
|
|
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
|
-
|
|
600
|
-
|
|
600
|
+
purpose: Log of user activities.
|
|
601
|
+
columns:
|
|
601
602
|
- id: integer, PK, auto-increment
|
|
602
|
-
- user_id: integer, FK
|
|
603
|
-
- action: string,
|
|
604
|
-
- created_at: timestamp,
|
|
605
|
-
|
|
606
|
-
- FK user_id
|
|
607
|
-
|
|
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
|
-
|
|
615
|
-
|
|
615
|
+
purpose: System roles for access control.
|
|
616
|
+
columns:
|
|
616
617
|
- id: integer, PK, auto-increment
|
|
617
|
-
- slug: string,
|
|
618
|
-
- created_at: timestamp,
|
|
619
|
-
- updated_at: timestamp,
|
|
620
|
-
|
|
621
|
-
- slug
|
|
622
|
-
|
|
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 (
|
|
625
|
+
- slug (unique)
|
|
625
626
|
```
|
|
626
627
|
|
|
627
628
|
### role_user
|
|
628
629
|
|
|
629
630
|
```yaml
|
|
630
|
-
|
|
631
|
-
|
|
631
|
+
purpose: Many-to-many relation between users and roles.
|
|
632
|
+
columns:
|
|
632
633
|
- id: integer, PK, auto-increment
|
|
633
|
-
- user_id: integer, FK
|
|
634
|
-
- role_id: integer, FK
|
|
635
|
-
|
|
636
|
-
- FK user_id
|
|
637
|
-
- FK role_id
|
|
638
|
-
|
|
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
|
-
|
|
646
|
-
|
|
646
|
+
purpose: The system's configurable dashboards.
|
|
647
|
+
columns:
|
|
647
648
|
- id: integer, PK, auto-increment
|
|
648
|
-
- slug: string,
|
|
649
|
-
- created_at: timestamp,
|
|
650
|
-
- updated_at: timestamp,
|
|
651
|
-
|
|
652
|
-
- slug
|
|
653
|
-
|
|
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 (
|
|
656
|
+
- slug (unique)
|
|
656
657
|
```
|
|
657
658
|
|
|
658
659
|
### dashboard_component
|
|
659
660
|
|
|
660
661
|
```yaml
|
|
661
|
-
|
|
662
|
-
|
|
662
|
+
purpose: Components that can be used in dashboards.
|
|
663
|
+
columns:
|
|
663
664
|
- id: integer, PK, auto-increment
|
|
664
|
-
- slug: string,
|
|
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,
|
|
671
|
-
- height: integer,
|
|
672
|
-
- is_resizable: boolean,
|
|
673
|
-
- created_at: timestamp,
|
|
674
|
-
- updated_at: timestamp,
|
|
675
|
-
|
|
676
|
-
- slug
|
|
677
|
-
|
|
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 (
|
|
680
|
+
- slug (unique)
|
|
680
681
|
```
|
|
681
682
|
|
|
682
683
|
### dashboard_role
|
|
683
684
|
|
|
684
685
|
```yaml
|
|
685
|
-
|
|
686
|
-
|
|
686
|
+
purpose: Relation between dashboards and roles for access control.
|
|
687
|
+
columns:
|
|
687
688
|
- id: integer, PK, auto-increment
|
|
688
|
-
- dashboard_id: integer, FK
|
|
689
|
-
- role_id: integer, FK
|
|
690
|
-
|
|
691
|
-
- FK dashboard_id
|
|
692
|
-
- FK role_id
|
|
693
|
-
|
|
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
|
-
|
|
701
|
-
|
|
701
|
+
purpose: Relation between dashboards and users.
|
|
702
|
+
columns:
|
|
702
703
|
- id: integer, PK, auto-increment
|
|
703
|
-
- dashboard_id: integer, FK
|
|
704
|
-
- user_id: integer, FK
|
|
705
|
-
- is_home: boolean,
|
|
706
|
-
|
|
707
|
-
- FK dashboard_id
|
|
708
|
-
- FK user_id
|
|
709
|
-
|
|
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
|
-
|
|
717
|
-
|
|
717
|
+
purpose: Items (widgets) within dashboards.
|
|
718
|
+
columns:
|
|
718
719
|
- id: integer, PK, auto-increment
|
|
719
|
-
- dashboard_id: integer, FK
|
|
720
|
-
- component_id: integer, FK
|
|
721
|
-
- width: integer,
|
|
722
|
-
- height: integer,
|
|
723
|
-
- x_axis: integer,
|
|
724
|
-
- y_axis: integer,
|
|
725
|
-
|
|
726
|
-
- FK dashboard_id
|
|
727
|
-
- FK component_id
|
|
728
|
-
|
|
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
|
-
|
|
736
|
-
|
|
736
|
+
purpose: Relation between dashboard components and roles.
|
|
737
|
+
columns:
|
|
737
738
|
- id: integer, PK, auto-increment
|
|
738
|
-
- component_id: integer, FK
|
|
739
|
-
- role_id: integer, FK
|
|
740
|
-
|
|
741
|
-
- FK component_id
|
|
742
|
-
- FK role_id
|
|
743
|
-
|
|
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
|
-
|
|
751
|
-
|
|
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,
|
|
754
|
-
- provider: string,
|
|
755
|
-
- redirect_uri: string,
|
|
756
|
-
- flow_type: string,
|
|
757
|
-
- expires_at: timestamptz,
|
|
758
|
-
- consumed_at: timestamptz, nullable (
|
|
759
|
-
- created_at: timestamptz,
|
|
760
|
-
|
|
761
|
-
- token_hash
|
|
762
|
-
|
|
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
|
-
>
|
|
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.
|
|
771
|
+
## 8. Relevant business rules
|
|
771
772
|
|
|
772
|
-
-
|
|
773
|
-
-
|
|
774
|
-
- MFA
|
|
775
|
-
-
|
|
776
|
-
- Dashboards
|
|
777
|
-
-
|
|
778
|
-
-
|
|
779
|
-
-
|
|
780
|
-
-
|
|
781
|
-
-
|
|
782
|
-
-
|
|
783
|
-
- MFA
|
|
784
|
-
- WebAuthn
|
|
785
|
-
- **OAuth —
|
|
786
|
-
- **OAuth —
|
|
787
|
-
- **OAuth — auto-
|
|
788
|
-
- **OAuth — roles
|
|
789
|
-
- **OAuth — mobile**: apps
|
|
790
|
-
- **OAuth — Apple (Sign in with Apple)**:
|
|
791
|
-
- **OAuth — LinkedIn**:
|
|
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.
|
|
794
|
+
## 9. Quick usage guide (examples)
|
|
794
795
|
|
|
795
|
-
###
|
|
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": "
|
|
804
|
+
"slug": "my-agent",
|
|
804
805
|
"provider": "openai",
|
|
805
806
|
"model": "gpt-4o-mini",
|
|
806
|
-
"instructions": "
|
|
807
|
+
"instructions": "Be a friendly assistant."
|
|
807
808
|
}
|
|
808
809
|
```
|
|
809
810
|
|
|
810
|
-
|
|
811
|
+
Response:
|
|
811
812
|
|
|
812
813
|
```json
|
|
813
814
|
{
|
|
814
815
|
"id": 1,
|
|
815
|
-
"slug": "
|
|
816
|
+
"slug": "my-agent",
|
|
816
817
|
"provider": "openai",
|
|
817
818
|
"model": "gpt-4o-mini",
|
|
818
|
-
"instructions": "
|
|
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
|
|
826
|
+
### Chat with an AI agent
|
|
826
827
|
|
|
827
828
|
```http
|
|
828
|
-
POST /ai/agent/
|
|
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: "
|
|
834
|
-
- file: (
|
|
834
|
+
- message: "Hi, how are you?"
|
|
835
|
+
- file: (optional file)
|
|
835
836
|
```
|
|
836
837
|
|
|
837
|
-
|
|
838
|
+
Response:
|
|
838
839
|
|
|
839
840
|
```json
|
|
840
841
|
{
|
|
841
|
-
"slug": "
|
|
842
|
+
"slug": "my-agent",
|
|
842
843
|
"provider": "openai",
|
|
843
844
|
"model": "gpt-4o-mini",
|
|
844
|
-
"content": "
|
|
845
|
+
"content": "Hello! I'm doing well, thanks for asking."
|
|
845
846
|
}
|
|
846
847
|
```
|
|
847
848
|
|
|
848
|
-
### Login
|
|
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": "
|
|
856
|
-
"password": "
|
|
856
|
+
"email": "user@example.com",
|
|
857
|
+
"password": "strongPassword123"
|
|
857
858
|
}
|
|
858
859
|
```
|
|
859
860
|
|
|
860
|
-
|
|
861
|
+
Response:
|
|
861
862
|
|
|
862
863
|
```json
|
|
863
864
|
{
|
|
@@ -866,21 +867,21 @@ Resposta:
|
|
|
866
867
|
}
|
|
867
868
|
```
|
|
868
869
|
|
|
869
|
-
###
|
|
870
|
+
### OAuth login started by another app (multi-app hub)
|
|
870
871
|
|
|
871
|
-
|
|
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
|
-
|
|
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.<
|
|
881
|
+
GET /oauth/google/callback/login?code=<code>&state=hhweb.training.login.<signature>
|
|
881
882
|
```
|
|
882
883
|
|
|
883
|
-
|
|
884
|
+
Response:
|
|
884
885
|
|
|
885
886
|
```json
|
|
886
887
|
{
|
|
@@ -888,16 +889,16 @@ Resposta:
|
|
|
888
889
|
}
|
|
889
890
|
```
|
|
890
891
|
|
|
891
|
-
|
|
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
|
-
###
|
|
894
|
+
### Get system information
|
|
894
895
|
|
|
895
896
|
```http
|
|
896
897
|
GET /system
|
|
897
898
|
Authorization: Bearer <token>
|
|
898
899
|
```
|
|
899
900
|
|
|
900
|
-
|
|
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
|
-
|
|
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.
|