@hed-hog/core 0.0.355 → 0.0.358

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.
Files changed (287) hide show
  1. package/dist/ai/ai.service.d.ts +3 -12
  2. package/dist/ai/ai.service.d.ts.map +1 -1
  3. package/dist/ai/ai.service.js +34 -125
  4. package/dist/ai/ai.service.js.map +1 -1
  5. package/dist/auth/guards/auth.guard.d.ts +6 -1
  6. package/dist/auth/guards/auth.guard.d.ts.map +1 -1
  7. package/dist/auth/guards/auth.guard.js +33 -2
  8. package/dist/auth/guards/auth.guard.js.map +1 -1
  9. package/dist/core.module.d.ts.map +1 -1
  10. package/dist/core.module.js +3 -3
  11. package/dist/core.module.js.map +1 -1
  12. package/dist/file/file.controller.d.ts +4 -2
  13. package/dist/file/file.controller.d.ts.map +1 -1
  14. package/dist/file/file.controller.js +44 -38
  15. package/dist/file/file.controller.js.map +1 -1
  16. package/dist/file/file.service.d.ts +2 -2
  17. package/dist/file/file.service.d.ts.map +1 -1
  18. package/dist/file/file.service.js +58 -89
  19. package/dist/file/file.service.js.map +1 -1
  20. package/dist/file/provider/azure.provider.d.ts.map +1 -1
  21. package/dist/file/provider/azure.provider.js +10 -15
  22. package/dist/file/provider/azure.provider.js.map +1 -1
  23. package/dist/file/provider/gcs.provider.d.ts.map +1 -1
  24. package/dist/file/provider/gcs.provider.js +8 -9
  25. package/dist/file/provider/gcs.provider.js.map +1 -1
  26. package/dist/file/provider/local.provider.d.ts.map +1 -1
  27. package/dist/file/provider/local.provider.js +11 -10
  28. package/dist/file/provider/local.provider.js.map +1 -1
  29. package/dist/file/provider/provider.enum.d.ts +2 -1
  30. package/dist/file/provider/provider.enum.d.ts.map +1 -1
  31. package/dist/file/provider/provider.enum.js +1 -0
  32. package/dist/file/provider/provider.enum.js.map +1 -1
  33. package/dist/file/provider/provider.factory.d.ts +1 -1
  34. package/dist/file/provider/provider.factory.d.ts.map +1 -1
  35. package/dist/file/provider/provider.factory.js +18 -5
  36. package/dist/file/provider/provider.factory.js.map +1 -1
  37. package/dist/file/provider/s3.provider.d.ts.map +1 -1
  38. package/dist/file/provider/s3.provider.js +4 -20
  39. package/dist/file/provider/s3.provider.js.map +1 -1
  40. package/dist/file/provider/spaces.provider.d.ts +27 -0
  41. package/dist/file/provider/spaces.provider.d.ts.map +1 -0
  42. package/dist/file/provider/spaces.provider.js +203 -0
  43. package/dist/file/provider/spaces.provider.js.map +1 -0
  44. package/dist/index.d.ts +4 -3
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +4 -3
  47. package/dist/index.js.map +1 -1
  48. package/dist/integration-profile/dto/create.dto.d.ts +9 -0
  49. package/dist/integration-profile/dto/create.dto.d.ts.map +1 -0
  50. package/dist/integration-profile/dto/create.dto.js +54 -0
  51. package/dist/integration-profile/dto/create.dto.js.map +1 -0
  52. package/dist/integration-profile/dto/export.dto.d.ts +5 -0
  53. package/dist/integration-profile/dto/export.dto.d.ts.map +1 -0
  54. package/dist/integration-profile/dto/export.dto.js +29 -0
  55. package/dist/integration-profile/dto/export.dto.js.map +1 -0
  56. package/dist/integration-profile/dto/import-confirm.dto.d.ts +17 -0
  57. package/dist/integration-profile/dto/import-confirm.dto.d.ts.map +1 -0
  58. package/dist/integration-profile/dto/import-confirm.dto.js +77 -0
  59. package/dist/integration-profile/dto/import-confirm.dto.js.map +1 -0
  60. package/dist/integration-profile/dto/update.dto.d.ts.map +1 -0
  61. package/dist/integration-profile/dto/update.dto.js.map +1 -0
  62. package/dist/integration-profile/integration-profile.controller.d.ts +142 -0
  63. package/dist/integration-profile/integration-profile.controller.d.ts.map +1 -0
  64. package/dist/integration-profile/integration-profile.controller.js +166 -0
  65. package/dist/integration-profile/integration-profile.controller.js.map +1 -0
  66. package/dist/integration-profile/integration-profile.module.d.ts +3 -0
  67. package/dist/integration-profile/integration-profile.module.d.ts.map +1 -0
  68. package/dist/{mail-profile/mail-profile.module.js → integration-profile/integration-profile.module.js} +11 -13
  69. package/dist/integration-profile/integration-profile.module.js.map +1 -0
  70. package/dist/integration-profile/integration-profile.service.d.ts +160 -0
  71. package/dist/integration-profile/integration-profile.service.d.ts.map +1 -0
  72. package/dist/integration-profile/integration-profile.service.js +609 -0
  73. package/dist/integration-profile/integration-profile.service.js.map +1 -0
  74. package/dist/integration-profile/integration-profile.utils.d.ts +30 -0
  75. package/dist/integration-profile/integration-profile.utils.d.ts.map +1 -0
  76. package/dist/integration-profile/integration-profile.utils.js +156 -0
  77. package/dist/integration-profile/integration-profile.utils.js.map +1 -0
  78. package/dist/mail/mail.module.d.ts.map +1 -1
  79. package/dist/mail/mail.module.js +36 -23
  80. package/dist/mail/mail.module.js.map +1 -1
  81. package/dist/mail/mail.service.d.ts +4 -2
  82. package/dist/mail/mail.service.d.ts.map +1 -1
  83. package/dist/mail/mail.service.js +33 -51
  84. package/dist/mail/mail.service.js.map +1 -1
  85. package/dist/mcp/guards/mcp-auth.guard.js +1 -1
  86. package/dist/mcp/guards/mcp-auth.guard.js.map +1 -1
  87. package/dist/mcp/mcp-api-key.controller.d.ts +2 -0
  88. package/dist/mcp/mcp-api-key.controller.d.ts.map +1 -1
  89. package/dist/mcp/mcp-api-key.controller.js +1 -1
  90. package/dist/mcp/mcp-api-key.controller.js.map +1 -1
  91. package/dist/mcp/mcp-api-key.service.d.ts +2 -1
  92. package/dist/mcp/mcp-api-key.service.d.ts.map +1 -1
  93. package/dist/mcp/mcp-api-key.service.js +6 -2
  94. package/dist/mcp/mcp-api-key.service.js.map +1 -1
  95. package/dist/mcp/mcp.module.d.ts.map +1 -1
  96. package/dist/mcp/mcp.module.js +3 -5
  97. package/dist/mcp/mcp.module.js.map +1 -1
  98. package/dist/mcp-chat/mcp-chat.service.d.ts.map +1 -1
  99. package/dist/mcp-chat/mcp-chat.service.js +17 -16
  100. package/dist/mcp-chat/mcp-chat.service.js.map +1 -1
  101. package/dist/oauth/interfaces/OAuthProvider.d.ts +1 -1
  102. package/dist/oauth/interfaces/OAuthProvider.d.ts.map +1 -1
  103. package/dist/oauth/oauth.controller.d.ts +4 -1
  104. package/dist/oauth/oauth.controller.d.ts.map +1 -1
  105. package/dist/oauth/oauth.controller.js +31 -5
  106. package/dist/oauth/oauth.controller.js.map +1 -1
  107. package/dist/oauth/oauth.service.d.ts +12 -1
  108. package/dist/oauth/oauth.service.d.ts.map +1 -1
  109. package/dist/oauth/oauth.service.js +125 -23
  110. package/dist/oauth/oauth.service.js.map +1 -1
  111. package/dist/oauth/providers/abstract.provider.d.ts +1 -1
  112. package/dist/oauth/providers/abstract.provider.d.ts.map +1 -1
  113. package/dist/oauth/providers/facebook.provider.d.ts +5 -2
  114. package/dist/oauth/providers/facebook.provider.d.ts.map +1 -1
  115. package/dist/oauth/providers/facebook.provider.js +34 -19
  116. package/dist/oauth/providers/facebook.provider.js.map +1 -1
  117. package/dist/oauth/providers/github.provider.d.ts +6 -2
  118. package/dist/oauth/providers/github.provider.d.ts.map +1 -1
  119. package/dist/oauth/providers/github.provider.js +43 -20
  120. package/dist/oauth/providers/github.provider.js.map +1 -1
  121. package/dist/oauth/providers/google.provider.d.ts +5 -2
  122. package/dist/oauth/providers/google.provider.d.ts.map +1 -1
  123. package/dist/oauth/providers/google.provider.js +34 -19
  124. package/dist/oauth/providers/google.provider.js.map +1 -1
  125. package/dist/oauth/providers/microsoft-entra-id.provider.d.ts +5 -2
  126. package/dist/oauth/providers/microsoft-entra-id.provider.d.ts.map +1 -1
  127. package/dist/oauth/providers/microsoft-entra-id.provider.js +43 -40
  128. package/dist/oauth/providers/microsoft-entra-id.provider.js.map +1 -1
  129. package/dist/oauth/providers/microsoft.provider.d.ts +5 -2
  130. package/dist/oauth/providers/microsoft.provider.d.ts.map +1 -1
  131. package/dist/oauth/providers/microsoft.provider.js +34 -19
  132. package/dist/oauth/providers/microsoft.provider.js.map +1 -1
  133. package/dist/profile/profile.controller.js +1 -1
  134. package/dist/profile/profile.controller.js.map +1 -1
  135. package/dist/security/security.service.d.ts +2 -0
  136. package/dist/security/security.service.d.ts.map +1 -1
  137. package/dist/security/security.service.js +21 -0
  138. package/dist/security/security.service.js.map +1 -1
  139. package/dist/setting/setting.service.d.ts +26 -1
  140. package/dist/setting/setting.service.d.ts.map +1 -1
  141. package/dist/setting/setting.service.js +240 -7
  142. package/dist/setting/setting.service.js.map +1 -1
  143. package/dist/user/user.controller.d.ts +2 -1
  144. package/dist/user/user.controller.d.ts.map +1 -1
  145. package/dist/user/user.controller.js +5 -4
  146. package/dist/user/user.controller.js.map +1 -1
  147. package/dist/user/user.service.d.ts +4 -2
  148. package/dist/user/user.service.d.ts.map +1 -1
  149. package/dist/user/user.service.js +19 -4
  150. package/dist/user/user.service.js.map +1 -1
  151. package/dist/webhook-integration/dto/webhook-action.dto.d.ts +17 -2
  152. package/dist/webhook-integration/dto/webhook-action.dto.d.ts.map +1 -1
  153. package/dist/webhook-integration/dto/webhook-action.dto.js +93 -2
  154. package/dist/webhook-integration/dto/webhook-action.dto.js.map +1 -1
  155. package/dist/webhook-integration/event-webhook.controller.d.ts +1 -0
  156. package/dist/webhook-integration/event-webhook.controller.d.ts.map +1 -1
  157. package/dist/webhook-integration/event-webhook.controller.js +10 -0
  158. package/dist/webhook-integration/event-webhook.controller.js.map +1 -1
  159. package/dist/webhook-integration/event-webhook.service.d.ts +2 -0
  160. package/dist/webhook-integration/event-webhook.service.d.ts.map +1 -1
  161. package/dist/webhook-integration/event-webhook.service.js +30 -0
  162. package/dist/webhook-integration/event-webhook.service.js.map +1 -1
  163. package/dist/webhook-integration/webhook-command.registry.d.ts +15 -0
  164. package/dist/webhook-integration/webhook-command.registry.d.ts.map +1 -0
  165. package/dist/webhook-integration/webhook-command.registry.js +36 -0
  166. package/dist/webhook-integration/webhook-command.registry.js.map +1 -0
  167. package/dist/webhook-integration/webhook-integration.controller.d.ts +20 -0
  168. package/dist/webhook-integration/webhook-integration.controller.d.ts.map +1 -1
  169. package/dist/webhook-integration/webhook-integration.controller.js +48 -0
  170. package/dist/webhook-integration/webhook-integration.controller.js.map +1 -1
  171. package/dist/webhook-integration/webhook-integration.module.d.ts.map +1 -1
  172. package/dist/webhook-integration/webhook-integration.module.js +5 -0
  173. package/dist/webhook-integration/webhook-integration.module.js.map +1 -1
  174. package/dist/webhook-integration/webhook-integration.service.d.ts +27 -2
  175. package/dist/webhook-integration/webhook-integration.service.d.ts.map +1 -1
  176. package/dist/webhook-integration/webhook-integration.service.js +377 -50
  177. package/dist/webhook-integration/webhook-integration.service.js.map +1 -1
  178. package/hedhog/data/file_provider.yaml +9 -1
  179. package/hedhog/data/integration_provider.yaml +148 -0
  180. package/hedhog/data/integration_type.yaml +29 -0
  181. package/hedhog/data/menu.yaml +6 -21
  182. package/hedhog/data/role.yaml +8 -0
  183. package/hedhog/data/route.yaml +73 -373
  184. package/hedhog/data/setting_group.yaml +209 -365
  185. package/hedhog/data/setting_subgroup.yaml +21 -13
  186. package/hedhog/frontend/app/account/components/connected-accounts.tsx.ejs +25 -20
  187. package/hedhog/frontend/app/account/components/tokens.tsx.ejs +421 -0
  188. package/hedhog/frontend/app/account/layout.tsx.ejs +6 -3
  189. package/hedhog/frontend/app/account/mcp-tokens/page.tsx.ejs +2 -2
  190. package/hedhog/frontend/app/account/tokens/page.tsx.ejs +5 -0
  191. package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +938 -225
  192. package/hedhog/frontend/app/configurations/layout.tsx.ejs +1 -1
  193. package/hedhog/frontend/app/dashboard/dashboard-home-tabs.tsx.ejs +3 -1
  194. package/hedhog/frontend/app/integration/profiles/page.tsx.ejs +1830 -0
  195. package/hedhog/frontend/app/integrations-webhooks/page.tsx.ejs +829 -128
  196. package/hedhog/frontend/app/mail/profiles/page.tsx.ejs +1 -820
  197. package/hedhog/frontend/app/setting-list/page.tsx.ejs +229 -395
  198. package/hedhog/frontend/app/users/permissions.tsx.ejs +79 -2
  199. package/hedhog/frontend/app/users/user-sheet.tsx.ejs +7 -2
  200. package/hedhog/frontend/messages/en.json +239 -3
  201. package/hedhog/frontend/messages/pt.json +239 -3
  202. package/hedhog/table/file.yaml +1 -1
  203. package/hedhog/table/integration_profile.yaml +34 -0
  204. package/hedhog/table/integration_provider.yaml +23 -0
  205. package/hedhog/table/integration_type.yaml +19 -0
  206. package/hedhog/table/mcp_api_key.yaml +4 -0
  207. package/hedhog/table/webhook_action.yaml +141 -85
  208. package/package.json +4 -3
  209. package/src/ai/ai.service.ts +35 -153
  210. package/src/auth/guards/auth.guard.ts +32 -0
  211. package/src/core.module.ts +3 -3
  212. package/src/file/file.controller.ts +51 -42
  213. package/src/file/file.service.ts +67 -104
  214. package/src/file/provider/azure.provider.ts +14 -28
  215. package/src/file/provider/gcs.provider.ts +9 -9
  216. package/src/file/provider/local.provider.ts +10 -13
  217. package/src/file/provider/provider.enum.ts +1 -0
  218. package/src/file/provider/provider.factory.ts +21 -6
  219. package/src/file/provider/s3.provider.ts +4 -20
  220. package/src/file/provider/spaces.provider.ts +246 -0
  221. package/src/index.ts +6 -3
  222. package/src/integration-profile/dto/create.dto.ts +44 -0
  223. package/src/integration-profile/dto/export.dto.ts +15 -0
  224. package/src/integration-profile/dto/import-confirm.dto.ts +61 -0
  225. package/src/integration-profile/integration-profile.controller.ts +131 -0
  226. package/src/integration-profile/integration-profile.module.ts +16 -0
  227. package/src/integration-profile/integration-profile.service.ts +793 -0
  228. package/src/integration-profile/integration-profile.utils.ts +173 -0
  229. package/src/mail/mail.module.ts +41 -22
  230. package/src/mail/mail.service.ts +40 -64
  231. package/src/mcp/guards/mcp-auth.guard.ts +1 -1
  232. package/src/mcp/mcp-api-key.controller.ts +2 -2
  233. package/src/mcp/mcp-api-key.service.ts +6 -2
  234. package/src/mcp/mcp.module.ts +3 -5
  235. package/src/mcp-chat/mcp-chat.service.ts +23 -20
  236. package/src/oauth/interfaces/OAuthProvider.ts +1 -1
  237. package/src/oauth/oauth.controller.ts +30 -2
  238. package/src/oauth/oauth.service.ts +194 -35
  239. package/src/oauth/providers/abstract.provider.ts +1 -1
  240. package/src/oauth/providers/facebook.provider.ts +36 -20
  241. package/src/oauth/providers/github.provider.ts +46 -23
  242. package/src/oauth/providers/google.provider.ts +36 -20
  243. package/src/oauth/providers/microsoft-entra-id.provider.ts +42 -46
  244. package/src/oauth/providers/microsoft.provider.ts +36 -21
  245. package/src/profile/profile.controller.ts +2 -2
  246. package/src/security/security.service.ts +34 -1
  247. package/src/setting/setting.service.ts +307 -9
  248. package/src/user/user.controller.ts +4 -1
  249. package/src/user/user.service.ts +20 -2
  250. package/src/webhook-integration/dto/webhook-action.dto.ts +85 -4
  251. package/src/webhook-integration/event-webhook.controller.ts +55 -50
  252. package/src/webhook-integration/event-webhook.service.ts +422 -384
  253. package/src/webhook-integration/webhook-command.registry.ts +42 -0
  254. package/src/webhook-integration/webhook-integration.controller.ts +25 -0
  255. package/src/webhook-integration/webhook-integration.module.ts +50 -45
  256. package/src/webhook-integration/webhook-integration.service.ts +444 -53
  257. package/dist/mail-profile/dto/create.dto.d.ts +0 -23
  258. package/dist/mail-profile/dto/create.dto.d.ts.map +0 -1
  259. package/dist/mail-profile/dto/create.dto.js +0 -139
  260. package/dist/mail-profile/dto/create.dto.js.map +0 -1
  261. package/dist/mail-profile/dto/update.dto.d.ts.map +0 -1
  262. package/dist/mail-profile/dto/update.dto.js.map +0 -1
  263. package/dist/mail-profile/mail-profile.controller.d.ts +0 -94
  264. package/dist/mail-profile/mail-profile.controller.d.ts.map +0 -1
  265. package/dist/mail-profile/mail-profile.controller.js +0 -95
  266. package/dist/mail-profile/mail-profile.controller.js.map +0 -1
  267. package/dist/mail-profile/mail-profile.module.d.ts +0 -3
  268. package/dist/mail-profile/mail-profile.module.d.ts.map +0 -1
  269. package/dist/mail-profile/mail-profile.module.js.map +0 -1
  270. package/dist/mail-profile/mail-profile.service.d.ts +0 -109
  271. package/dist/mail-profile/mail-profile.service.d.ts.map +0 -1
  272. package/dist/mail-profile/mail-profile.service.js +0 -278
  273. package/dist/mail-profile/mail-profile.service.js.map +0 -1
  274. package/dist/mcp/tools/core-mail-profile.mcp-tools.d.ts +0 -32
  275. package/dist/mcp/tools/core-mail-profile.mcp-tools.d.ts.map +0 -1
  276. package/dist/mcp/tools/core-mail-profile.mcp-tools.js +0 -178
  277. package/dist/mcp/tools/core-mail-profile.mcp-tools.js.map +0 -1
  278. package/hedhog/data/mail_profile.yaml +0 -18
  279. package/hedhog/table/mail_profile.yaml +0 -75
  280. package/src/mail-profile/dto/create.dto.ts +0 -145
  281. package/src/mail-profile/mail-profile.controller.ts +0 -56
  282. package/src/mail-profile/mail-profile.module.ts +0 -18
  283. package/src/mail-profile/mail-profile.service.ts +0 -308
  284. package/src/mcp/tools/core-mail-profile.mcp-tools.ts +0 -128
  285. /package/dist/{mail-profile → integration-profile}/dto/update.dto.d.ts +0 -0
  286. /package/dist/{mail-profile → integration-profile}/dto/update.dto.js +0 -0
  287. /package/src/{mail-profile → integration-profile}/dto/update.dto.ts +0 -0
@@ -30,6 +30,14 @@
30
30
  en: Rules for retaining and automatically removing old user notifications
31
31
  pt: Regras para retenção e remoção automática de notificações antigas de usuários
32
32
 
33
+ - slug: maintenance-mode
34
+ name:
35
+ en: Maintenance Mode
36
+ pt: Modo de Manutenção
37
+ description:
38
+ en: Controls whether the system is presented as temporarily unavailable for maintenance
39
+ pt: Controla se o sistema é apresentado como temporariamente indisponível para manutenção
40
+
33
41
  - slug: email-password-auth
34
42
  name:
35
43
  en: Email and Password Authentication
@@ -94,6 +102,14 @@
94
102
  en: Limits and policies for concurrent user sessions
95
103
  pt: Limites e políticas para sessões simultâneas de usuários
96
104
 
105
+ - slug: admin-login-page
106
+ name:
107
+ en: Administration Login Page
108
+ pt: Página de Login da Administração
109
+ description:
110
+ en: Controls which OAuth providers are visible on the Admin login page
111
+ pt: Controla quais provedores OAuth são visíveis na página de login do Admin
112
+
97
113
  - slug: regional-settings
98
114
  name:
99
115
  en: Regional Settings
@@ -246,21 +262,13 @@
246
262
  en: Google Cloud Storage authentication settings
247
263
  pt: Configurações de autenticação do Google Cloud Storage
248
264
 
249
- - slug: openai
250
- name:
251
- en: OpenAI
252
- pt: OpenAI
253
- description:
254
- en: OpenAI service credential settings
255
- pt: Configurações de credenciais do serviço OpenAI
256
-
257
- - slug: gemini
265
+ - slug: do-spaces-storage
258
266
  name:
259
- en: Gemini
260
- pt: Gemini
267
+ en: DigitalOcean Spaces
268
+ pt: DigitalOcean Spaces
261
269
  description:
262
- en: Google Gemini service credential settings
263
- pt: Configurações de credenciais do serviço Google Gemini
270
+ en: DigitalOcean Spaces bucket access and optional CDN settings
271
+ pt: Configurações de acesso ao bucket e CDN opcional do DigitalOcean Spaces
264
272
 
265
273
  - slug: outbox-general
266
274
  name:
@@ -9,14 +9,15 @@ import {
9
9
  CardHeader,
10
10
  CardTitle,
11
11
  } from '@/components/ui/card';
12
+ import {
13
+ formatIntegrationProviderName,
14
+ IntegrationLogo,
15
+ normalizeIntegrationProvider,
16
+ } from '@/components/ui/integration-logo';
12
17
  import { useApp } from '@hed-hog/next-app-provider';
13
18
  import { Link as LinkIcon, Unlink } from 'lucide-react';
14
19
  import { useTranslations } from 'next-intl';
15
20
  import Link from 'next/link';
16
- import FacebookIcon from '../../../../../../../public/facebook.svg';
17
- import GithubIcon from '../../../../../../../public/github.svg';
18
- import GoogleIcon from '../../../../../../../public/google.svg';
19
- import MicrosoftIcon from '../../../../../../../public/microsoft.svg';
20
21
 
21
22
  export function ConnectedAccounts() {
22
23
  const { getSettingValue, request, setAccessToken, showToastHandler, user } =
@@ -24,25 +25,18 @@ export function ConnectedAccounts() {
24
25
  const t = useTranslations('core.ConnectedAccounts');
25
26
  const availableProviders = getSettingValue('providers') || '[]';
26
27
 
27
- const providerIcons: Record<string, React.ComponentType<any>> = {
28
- google: GoogleIcon,
29
- facebook: FacebookIcon,
30
- github: GithubIcon,
31
- microsoft: MicrosoftIcon,
32
- };
33
-
34
28
  const providers = Array.isArray(availableProviders)
35
29
  ? availableProviders.map((p: any) => {
36
30
  if (typeof p === 'string') {
37
31
  return {
38
32
  id: p,
39
33
  name: p.charAt(0).toUpperCase() + p.slice(1),
40
- icon: providerIcons[p] ?? (() => null),
34
+ normalizedId: normalizeIntegrationProvider(p),
41
35
  };
42
36
  }
43
37
  return {
44
38
  ...p,
45
- icon: providerIcons[p.id] ?? (() => null),
39
+ normalizedId: normalizeIntegrationProvider(p.id),
46
40
  };
47
41
  })
48
42
  : [];
@@ -50,7 +44,7 @@ export function ConnectedAccounts() {
50
44
  const handleDisconnect = async (email: string, providerName: string) => {
51
45
  try {
52
46
  const { data }: any = await request({
53
- url: `/oauth/${providerName.toLowerCase()}`,
47
+ url: `/oauth/${normalizeIntegrationProvider(providerName)}`,
54
48
  method: 'DELETE',
55
49
  data: { email },
56
50
  });
@@ -66,8 +60,10 @@ export function ConnectedAccounts() {
66
60
  };
67
61
 
68
62
  const getConnectionInfo = (providerName: string) => {
63
+ const normalizedProviderName = normalizeIntegrationProvider(providerName);
69
64
  const accounts = (user?.user_account ?? []).filter(
70
- (ua) => ua.provider === providerName.toLowerCase()
65
+ (ua) =>
66
+ normalizeIntegrationProvider(ua.provider) === normalizedProviderName
71
67
  );
72
68
 
73
69
  return {
@@ -85,7 +81,7 @@ export function ConnectedAccounts() {
85
81
  </CardHeader>
86
82
  <CardContent className="space-y-4 px-6 pb-6">
87
83
  {providers?.map((provider: any) => {
88
- const { isConnected, account } = getConnectionInfo(provider.name);
84
+ const { isConnected, account } = getConnectionInfo(provider.id);
89
85
 
90
86
  return (
91
87
  <div
@@ -94,11 +90,20 @@ export function ConnectedAccounts() {
94
90
  >
95
91
  <div className="flex items-center gap-4">
96
92
  <div className="flex size-10 items-center justify-center rounded-lg bg-muted">
97
- <img className="size-5" src={provider.icon.src} />
93
+ <IntegrationLogo
94
+ provider={provider.id}
95
+ size={20}
96
+ decorative
97
+ />
98
98
  </div>
99
99
  <div>
100
100
  <div className="flex items-center gap-2">
101
- <p className="font-medium">{provider.name}</p>
101
+ <p className="font-medium">
102
+ {formatIntegrationProviderName(
103
+ provider.id,
104
+ provider.name
105
+ )}
106
+ </p>
102
107
  {Boolean(isConnected) && (
103
108
  <Badge variant="default" className="text-xs">
104
109
  {t('connected')}
@@ -118,7 +123,7 @@ export function ConnectedAccounts() {
118
123
  variant="outline"
119
124
  size="sm"
120
125
  onClick={() =>
121
- handleDisconnect(String(account?.email), provider.name)
126
+ handleDisconnect(String(account?.email), provider.id)
122
127
  }
123
128
  >
124
129
  <Unlink className="mr-2 size-4" />
@@ -126,7 +131,7 @@ export function ConnectedAccounts() {
126
131
  </Button>
127
132
  ) : (
128
133
  <Link
129
- href={`${process.env.NEXT_PUBLIC_API_BASE_URL}/oauth/${provider.name.toLowerCase()}/connect`}
134
+ href={`${process.env.NEXT_PUBLIC_API_BASE_URL}/oauth/${provider.normalizedId}/connect`}
130
135
  >
131
136
  <Button type="button" variant="outline" size="sm">
132
137
  <LinkIcon className="mr-2 size-4" />
@@ -0,0 +1,421 @@
1
+ 'use client';
2
+
3
+ import { PaginationFooter } from '@/components/entity-list/pagination-footer';
4
+ import {
5
+ AlertDialog,
6
+ AlertDialogAction,
7
+ AlertDialogCancel,
8
+ AlertDialogContent,
9
+ AlertDialogDescription,
10
+ AlertDialogFooter,
11
+ AlertDialogHeader,
12
+ AlertDialogTitle,
13
+ } from '@/components/ui/alert-dialog';
14
+ import { Badge } from '@/components/ui/badge';
15
+ import { Button } from '@/components/ui/button';
16
+ import {
17
+ Card,
18
+ CardContent,
19
+ CardDescription,
20
+ CardHeader,
21
+ CardTitle,
22
+ } from '@/components/ui/card';
23
+ import { Input } from '@/components/ui/input';
24
+ import { Label } from '@/components/ui/label';
25
+ import {
26
+ Sheet,
27
+ SheetDescription,
28
+ SheetFooter,
29
+ SheetHeader,
30
+ SheetTitle,
31
+ } from '@/components/ui/sheet';
32
+ import {
33
+ Table,
34
+ TableBody,
35
+ TableCell,
36
+ TableHead,
37
+ TableHeader,
38
+ TableRow,
39
+ } from '@/components/ui/table';
40
+ import { ResizableSheetContent } from '@/components/ui/resizable-sheet-content';
41
+ import { usePagination } from '@/hooks/use-pagination';
42
+ import { useApp } from '@hed-hog/next-app-provider';
43
+ import { Check, Copy, KeyRound, Plus, Trash2 } from 'lucide-react';
44
+ import { useTranslations } from 'next-intl';
45
+ import { useState } from 'react';
46
+ import { toast } from 'sonner';
47
+
48
+ type ApiToken = {
49
+ id: number;
50
+ name: string;
51
+ type: 'mcp' | 'api';
52
+ token_prefix: string;
53
+ last_used_at: string | null;
54
+ expires_at: string | null;
55
+ created_at: string;
56
+ };
57
+
58
+ function daysSince(
59
+ date: string | null,
60
+ t: (key: string, values?: Record<string, unknown>) => string,
61
+ ): string {
62
+ if (!date) return t('never');
63
+ const days = Math.floor(
64
+ (Date.now() - new Date(date).getTime()) / 86400000,
65
+ );
66
+ if (days === 0) return t('today');
67
+ return t('daysAgo', { days });
68
+ }
69
+
70
+ function CopyButton({ value }: { value: string }) {
71
+ const [copied, setCopied] = useState(false);
72
+ const handleCopy = () => {
73
+ navigator.clipboard.writeText(value);
74
+ setCopied(true);
75
+ setTimeout(() => setCopied(false), 2000);
76
+ };
77
+ return (
78
+ <Button variant="ghost" size="icon" className="size-7 shrink-0" onClick={handleCopy}>
79
+ {copied ? (
80
+ <Check className="size-3.5 text-green-500" />
81
+ ) : (
82
+ <Copy className="size-3.5" />
83
+ )}
84
+ </Button>
85
+ );
86
+ }
87
+
88
+ function CodeRow({ label, value }: { label: string; value: string }) {
89
+ return (
90
+ <div className="flex items-center gap-2 rounded-md border bg-muted/40 px-3 py-2">
91
+ <span className="text-sm text-muted-foreground shrink-0">{label}:</span>
92
+ <code className="flex-1 text-xs break-all">{value}</code>
93
+ <CopyButton value={value} />
94
+ </div>
95
+ );
96
+ }
97
+
98
+ export function Tokens() {
99
+ const { request, getSettingValue } = useApp();
100
+ const t = useTranslations('core.Tokens');
101
+
102
+ const [keyToRevoke, setKeyToRevoke] = useState<number | null>(null);
103
+ const [isSheetOpen, setIsSheetOpen] = useState(false);
104
+ const [newKeyName, setNewKeyName] = useState('');
105
+ const [newKeyType, setNewKeyType] = useState<'mcp' | 'api'>('mcp');
106
+ const [createdToken, setCreatedToken] = useState<string | null>(null);
107
+ const [isCopied, setIsCopied] = useState(false);
108
+ const {
109
+ items: keys,
110
+ refetch,
111
+ page,
112
+ setPage,
113
+ totalItems,
114
+ pageSize,
115
+ setPageSize,
116
+ } = usePagination({ url: '/mcp/api-keys' });
117
+
118
+ const apiUrl = (getSettingValue('api-url') as string | null)?.replace(/\/$/, '') ?? '';
119
+ const mcpUrl = apiUrl ? `${apiUrl}/mcp` : '/mcp';
120
+
121
+ const handleCreate = async () => {
122
+ if (!newKeyName.trim()) return;
123
+ try {
124
+ const res = await request<ApiToken & { rawToken: string }>({
125
+ url: '/mcp/api-keys',
126
+ method: 'POST',
127
+ data: { name: newKeyName.trim(), type: newKeyType },
128
+ });
129
+ setCreatedToken(res.data.rawToken);
130
+ setNewKeyName('');
131
+ refetch();
132
+ } catch {
133
+ toast.error(t('createFailure'));
134
+ }
135
+ };
136
+
137
+ const handleRevoke = async (id: number) => {
138
+ try {
139
+ await request({ url: `/mcp/api-keys/${id}`, method: 'DELETE' });
140
+ setKeyToRevoke(null);
141
+ refetch();
142
+ toast.success(t('revokeSuccess'));
143
+ } catch {
144
+ toast.error(t('revokeFailure'));
145
+ }
146
+ };
147
+
148
+ const handleCopyToken = () => {
149
+ if (!createdToken) return;
150
+ navigator.clipboard.writeText(createdToken);
151
+ setIsCopied(true);
152
+ setTimeout(() => setIsCopied(false), 2000);
153
+ };
154
+
155
+ const handleCloseSheet = () => {
156
+ setIsSheetOpen(false);
157
+ setCreatedToken(null);
158
+ setNewKeyName('');
159
+ setNewKeyType('mcp');
160
+ setIsCopied(false);
161
+ };
162
+
163
+ return (
164
+ <>
165
+ <Card>
166
+ <CardHeader className="flex flex-row items-start justify-between">
167
+ <div>
168
+ <CardTitle>{t('title')}</CardTitle>
169
+ <CardDescription className="mt-1">{t('description')}</CardDescription>
170
+ </div>
171
+ <Button size="sm" onClick={() => setIsSheetOpen(true)}>
172
+ <Plus className="size-4 mr-2" />
173
+ {t('createToken')}
174
+ </Button>
175
+ </CardHeader>
176
+ <CardContent className="space-y-4">
177
+ {(keys as ApiToken[]).length === 0 ? (
178
+ <div className="flex flex-col items-center gap-2 py-8 text-muted-foreground">
179
+ <KeyRound className="size-8 opacity-40" />
180
+ <p className="text-sm">{t('noTokens')}</p>
181
+ </div>
182
+ ) : (
183
+ <Table>
184
+ <TableHeader>
185
+ <TableRow>
186
+ <TableHead>{t('name')}</TableHead>
187
+ <TableHead>{t('type')}</TableHead>
188
+ <TableHead>{t('tokenPrefix')}</TableHead>
189
+ <TableHead>{t('lastUsed')}</TableHead>
190
+ <TableHead>{t('createdAt')}</TableHead>
191
+ <TableHead className="w-15" />
192
+ </TableRow>
193
+ </TableHeader>
194
+ <TableBody>
195
+ {(keys as ApiToken[]).map((key) => (
196
+ <TableRow key={key.id}>
197
+ <TableCell className="font-medium">{key.name}</TableCell>
198
+ <TableCell>
199
+ <Badge
200
+ variant={key.type === 'mcp' ? 'default' : 'secondary'}
201
+ className="text-xs"
202
+ >
203
+ {key.type === 'mcp' ? t('typeMcp') : t('typeApi')}
204
+ </Badge>
205
+ </TableCell>
206
+ <TableCell>
207
+ <code className="rounded bg-muted px-1.5 py-0.5 text-xs">
208
+ {key.token_prefix}…
209
+ </code>
210
+ </TableCell>
211
+ <TableCell className="text-muted-foreground text-sm">
212
+ {daysSince(key.last_used_at, t as any)}
213
+ </TableCell>
214
+ <TableCell className="text-muted-foreground text-sm">
215
+ {new Date(key.created_at).toLocaleDateString()}
216
+ </TableCell>
217
+ <TableCell>
218
+ <Button
219
+ variant="ghost"
220
+ size="icon"
221
+ className="size-8 text-destructive hover:text-destructive"
222
+ onClick={() => setKeyToRevoke(key.id)}
223
+ >
224
+ <Trash2 className="size-4" />
225
+ </Button>
226
+ </TableCell>
227
+ </TableRow>
228
+ ))}
229
+ </TableBody>
230
+ </Table>
231
+ )}
232
+
233
+ <PaginationFooter
234
+ currentPage={page}
235
+ pageSize={pageSize}
236
+ totalItems={totalItems}
237
+ onPageChange={setPage}
238
+ onPageSizeChange={(newSize) => {
239
+ setPageSize(newSize);
240
+ setPage(1);
241
+ }}
242
+ pageSizeOptions={[10]}
243
+ />
244
+ </CardContent>
245
+ </Card>
246
+
247
+ {/* Revoke confirmation */}
248
+ <AlertDialog
249
+ open={keyToRevoke !== null}
250
+ onOpenChange={(open) => !open && setKeyToRevoke(null)}
251
+ >
252
+ <AlertDialogContent>
253
+ <AlertDialogHeader>
254
+ <AlertDialogTitle>{t('revokeTokenTitle')}</AlertDialogTitle>
255
+ <AlertDialogDescription>
256
+ {t('revokeTokenDescription')}
257
+ </AlertDialogDescription>
258
+ </AlertDialogHeader>
259
+ <AlertDialogFooter>
260
+ <AlertDialogCancel>{t('cancel')}</AlertDialogCancel>
261
+ <AlertDialogAction
262
+ className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
263
+ onClick={() => keyToRevoke !== null && handleRevoke(keyToRevoke)}
264
+ >
265
+ {t('revokeConfirm')}
266
+ </AlertDialogAction>
267
+ </AlertDialogFooter>
268
+ </AlertDialogContent>
269
+ </AlertDialog>
270
+
271
+ {/* Create token sheet */}
272
+ <Sheet open={isSheetOpen} onOpenChange={(open) => !open && handleCloseSheet()}>
273
+ <ResizableSheetContent sheetId="create-token" defaultWidth={520}>
274
+ <SheetHeader>
275
+ <SheetTitle>
276
+ {createdToken ? t('tokenCreatedTitle') : t('createTokenTitle')}
277
+ </SheetTitle>
278
+ <SheetDescription>
279
+ {createdToken ? t('tokenCreatedWarning') : t('createTokenDescription')}
280
+ </SheetDescription>
281
+ </SheetHeader>
282
+
283
+ {createdToken === null ? (
284
+ <div className="flex flex-col gap-6 px-4 py-2 overflow-y-auto">
285
+ {/* Type selector */}
286
+ <div className="space-y-2">
287
+ <Label>{t('tokenTypeLabel')}</Label>
288
+ <div className="flex gap-2">
289
+ <Button
290
+ type="button"
291
+ variant={newKeyType === 'mcp' ? 'default' : 'outline'}
292
+ size="sm"
293
+ className="flex-1"
294
+ onClick={() => setNewKeyType('mcp')}
295
+ >
296
+ {t('typeMcp')}
297
+ </Button>
298
+ <Button
299
+ type="button"
300
+ variant={newKeyType === 'api' ? 'default' : 'outline'}
301
+ size="sm"
302
+ className="flex-1"
303
+ onClick={() => setNewKeyType('api')}
304
+ >
305
+ {t('typeApi')}
306
+ </Button>
307
+ </div>
308
+ </div>
309
+
310
+ {/* Token name */}
311
+ <div className="space-y-2">
312
+ <Label htmlFor="token-name">{t('tokenNameLabel')}</Label>
313
+ <Input
314
+ id="token-name"
315
+ placeholder={t('tokenNamePlaceholder')}
316
+ value={newKeyName}
317
+ onChange={(e) => setNewKeyName(e.target.value)}
318
+ onKeyDown={(e) => e.key === 'Enter' && handleCreate()}
319
+ />
320
+ </div>
321
+
322
+ {/* Usage instructions */}
323
+ <div className="space-y-3 rounded-lg border bg-muted/30 p-4">
324
+ <p className="text-sm font-medium">{t('usageTitle')}</p>
325
+
326
+ {newKeyType === 'mcp' ? (
327
+ <div className="space-y-3">
328
+ <p className="text-xs text-muted-foreground">{t('mcpUsageDesc')}</p>
329
+ <CodeRow label={t('serverUrlLabel')} value={mcpUrl} />
330
+ <div className="space-y-1">
331
+ <p className="text-xs text-muted-foreground">{t('mcpUsageHeader')}</p>
332
+ <pre className="rounded bg-muted px-3 py-2 text-xs overflow-x-auto">
333
+ {`Authorization: Bearer <${t('yourToken')}>`}
334
+ </pre>
335
+ </div>
336
+ <div className="space-y-1">
337
+ <p className="text-xs text-muted-foreground">{t('mcpUsageExampleLabel')}</p>
338
+ <pre className="rounded bg-muted px-3 py-2 text-xs overflow-x-auto whitespace-pre-wrap break-all">
339
+ {`{
340
+ "mcpServers": {
341
+ "hub": {
342
+ "type": "sse",
343
+ "url": "${mcpUrl}",
344
+ "headers": {
345
+ "Authorization": "Bearer <${t('yourToken')}>"
346
+ }
347
+ }
348
+ }
349
+ }`}
350
+ </pre>
351
+ </div>
352
+ </div>
353
+ ) : (
354
+ <div className="space-y-3">
355
+ <p className="text-xs text-muted-foreground">{t('apiUsageDesc')}</p>
356
+ <CodeRow label={t('serverUrlLabel')} value={apiUrl || '/api'} />
357
+ <div className="space-y-1">
358
+ <p className="text-xs text-muted-foreground">{t('apiUsageHeader')}</p>
359
+ <pre className="rounded bg-muted px-3 py-2 text-xs overflow-x-auto">
360
+ {`Authorization: Bearer <${t('yourToken')}>`}
361
+ </pre>
362
+ </div>
363
+ <div className="space-y-1">
364
+ <p className="text-xs text-muted-foreground">{t('apiUsageExampleLabel')}</p>
365
+ <pre className="rounded bg-muted px-3 py-2 text-xs overflow-x-auto whitespace-pre-wrap break-all">
366
+ {`curl -H "Authorization: Bearer <${t('yourToken')}>" \\\n ${apiUrl || '/api'}/users/me`}
367
+ </pre>
368
+ </div>
369
+ </div>
370
+ )}
371
+ </div>
372
+ </div>
373
+ ) : (
374
+ <div className="flex flex-col gap-4 px-4 py-2 overflow-y-auto">
375
+ <div className="rounded-md border border-yellow-300 bg-yellow-50 px-3 py-2 text-sm text-yellow-800 dark:border-yellow-700 dark:bg-yellow-950 dark:text-yellow-300">
376
+ {t('tokenCreatedWarning')}
377
+ </div>
378
+ <div className="space-y-1">
379
+ <Label>{t('tokenCreatedLabel')}</Label>
380
+ <div className="flex items-center gap-2">
381
+ <code className="flex-1 rounded border bg-muted px-3 py-2 text-xs break-all">
382
+ {createdToken}
383
+ </code>
384
+ <Button
385
+ variant="outline"
386
+ size="icon"
387
+ onClick={handleCopyToken}
388
+ className="shrink-0"
389
+ >
390
+ {isCopied ? (
391
+ <Check className="size-4 text-green-500" />
392
+ ) : (
393
+ <Copy className="size-4" />
394
+ )}
395
+ </Button>
396
+ </div>
397
+ </div>
398
+ </div>
399
+ )}
400
+
401
+ <SheetFooter className="px-4">
402
+ {createdToken === null ? (
403
+ <>
404
+ <Button variant="outline" onClick={handleCloseSheet}>
405
+ {t('cancel')}
406
+ </Button>
407
+ <Button onClick={handleCreate} disabled={!newKeyName.trim()}>
408
+ {t('createToken')}
409
+ </Button>
410
+ </>
411
+ ) : (
412
+ <Button className="w-full" onClick={handleCloseSheet}>
413
+ {t('closeSheet')}
414
+ </Button>
415
+ )}
416
+ </SheetFooter>
417
+ </ResizableSheetContent>
418
+ </Sheet>
419
+ </>
420
+ );
421
+ }
@@ -38,11 +38,11 @@ export default function AccountLayout({ children }: LayoutProps) {
38
38
  { value: '2fa', label: t('tab2fa'), icon: Shield },
39
39
  { value: 'accounts', label: t('tabAccounts'), icon: LinkIcon },
40
40
  { value: 'sessions', label: t('tabSessions'), icon: Smartphone },
41
- { value: 'mcp-tokens', label: t('tabMcpTokens'), icon: KeyRound },
41
+ { value: 'tokens', label: t('tabTokens'), icon: KeyRound },
42
42
  ];
43
43
 
44
44
  return (
45
- <div className="flex min-w-0 flex-col overflow-x-hidden px-4">
45
+ <div className="flex min-w-0 flex-col px-4">
46
46
  <PageHeader
47
47
  breadcrumbs={[
48
48
  { label: t('home'), href: '/' },
@@ -53,7 +53,10 @@ export default function AccountLayout({ children }: LayoutProps) {
53
53
  />
54
54
 
55
55
  <div className="sticky top-0 z-20 -mx-4 mb-6 border-b border-border bg-background/95 px-4 backdrop-blur supports-backdrop-filter:bg-background/80">
56
- <nav className="flex items-end gap-1 overflow-x-auto" aria-label="Account tabs">
56
+ <nav
57
+ className="flex items-end gap-1 overflow-x-auto"
58
+ aria-label="Account tabs"
59
+ >
57
60
  {tabs.map((tab) => {
58
61
  const Icon = tab.icon;
59
62
  const isActive = pathname.includes(`/${tab.value}`);
@@ -1,5 +1,5 @@
1
- import { McpTokens } from '../components/mcp-tokens';
1
+ import { redirect } from 'next/navigation';
2
2
 
3
3
  export default function McpTokensPage() {
4
- return <McpTokens />;
4
+ redirect('/core/account/tokens');
5
5
  }
@@ -0,0 +1,5 @@
1
+ import { Tokens } from '../components/tokens';
2
+
3
+ export default function TokensPage() {
4
+ return <Tokens />;
5
+ }