@hed-hog/core 0.0.356 → 0.0.359

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 (250) hide show
  1. package/dist/auth/guards/auth.guard.d.ts +6 -1
  2. package/dist/auth/guards/auth.guard.d.ts.map +1 -1
  3. package/dist/auth/guards/auth.guard.js +33 -2
  4. package/dist/auth/guards/auth.guard.js.map +1 -1
  5. package/dist/file/file.controller.d.ts +4 -2
  6. package/dist/file/file.controller.d.ts.map +1 -1
  7. package/dist/file/file.controller.js +44 -38
  8. package/dist/file/file.controller.js.map +1 -1
  9. package/dist/file/file.service.d.ts +2 -2
  10. package/dist/file/file.service.d.ts.map +1 -1
  11. package/dist/file/file.service.js +25 -42
  12. package/dist/file/file.service.js.map +1 -1
  13. package/dist/file/provider/azure.provider.d.ts.map +1 -1
  14. package/dist/file/provider/azure.provider.js +10 -15
  15. package/dist/file/provider/azure.provider.js.map +1 -1
  16. package/dist/file/provider/gcs.provider.d.ts.map +1 -1
  17. package/dist/file/provider/gcs.provider.js +8 -9
  18. package/dist/file/provider/gcs.provider.js.map +1 -1
  19. package/dist/file/provider/local.provider.d.ts.map +1 -1
  20. package/dist/file/provider/local.provider.js +11 -10
  21. package/dist/file/provider/local.provider.js.map +1 -1
  22. package/dist/file/provider/provider.factory.d.ts +1 -1
  23. package/dist/file/provider/provider.factory.d.ts.map +1 -1
  24. package/dist/file/provider/provider.factory.js +16 -6
  25. package/dist/file/provider/provider.factory.js.map +1 -1
  26. package/dist/file/provider/s3.provider.d.ts.map +1 -1
  27. package/dist/file/provider/s3.provider.js +4 -20
  28. package/dist/file/provider/s3.provider.js.map +1 -1
  29. package/dist/file/provider/spaces.provider.d.ts.map +1 -1
  30. package/dist/file/provider/spaces.provider.js +4 -19
  31. package/dist/file/provider/spaces.provider.js.map +1 -1
  32. package/dist/integration-profile/integration-profile.controller.d.ts +1 -1
  33. package/dist/integration-profile/integration-profile.controller.d.ts.map +1 -1
  34. package/dist/integration-profile/integration-profile.controller.js +4 -3
  35. package/dist/integration-profile/integration-profile.controller.js.map +1 -1
  36. package/dist/integration-profile/integration-profile.service.d.ts +1 -1
  37. package/dist/integration-profile/integration-profile.service.d.ts.map +1 -1
  38. package/dist/integration-profile/integration-profile.service.js +7 -4
  39. package/dist/integration-profile/integration-profile.service.js.map +1 -1
  40. package/dist/integration-profile/integration-profile.utils.d.ts +6 -0
  41. package/dist/integration-profile/integration-profile.utils.d.ts.map +1 -1
  42. package/dist/integration-profile/integration-profile.utils.js +9 -0
  43. package/dist/integration-profile/integration-profile.utils.js.map +1 -1
  44. package/dist/mail/mail.module.d.ts.map +1 -1
  45. package/dist/mail/mail.module.js +9 -4
  46. package/dist/mail/mail.module.js.map +1 -1
  47. package/dist/mail/mail.service.d.ts +2 -1
  48. package/dist/mail/mail.service.d.ts.map +1 -1
  49. package/dist/mail/mail.service.js +11 -7
  50. package/dist/mail/mail.service.js.map +1 -1
  51. package/dist/mcp/guards/mcp-auth.guard.js +1 -1
  52. package/dist/mcp/guards/mcp-auth.guard.js.map +1 -1
  53. package/dist/mcp/mcp-api-key.controller.d.ts +2 -0
  54. package/dist/mcp/mcp-api-key.controller.d.ts.map +1 -1
  55. package/dist/mcp/mcp-api-key.controller.js +1 -1
  56. package/dist/mcp/mcp-api-key.controller.js.map +1 -1
  57. package/dist/mcp/mcp-api-key.service.d.ts +2 -1
  58. package/dist/mcp/mcp-api-key.service.d.ts.map +1 -1
  59. package/dist/mcp/mcp-api-key.service.js +6 -2
  60. package/dist/mcp/mcp-api-key.service.js.map +1 -1
  61. package/dist/mcp/mcp.module.d.ts.map +1 -1
  62. package/dist/mcp/mcp.module.js +1 -3
  63. package/dist/mcp/mcp.module.js.map +1 -1
  64. package/dist/oauth/interfaces/OAuthProvider.d.ts +1 -1
  65. package/dist/oauth/interfaces/OAuthProvider.d.ts.map +1 -1
  66. package/dist/oauth/oauth.controller.d.ts +4 -1
  67. package/dist/oauth/oauth.controller.d.ts.map +1 -1
  68. package/dist/oauth/oauth.controller.js +31 -5
  69. package/dist/oauth/oauth.controller.js.map +1 -1
  70. package/dist/oauth/oauth.service.d.ts +12 -1
  71. package/dist/oauth/oauth.service.d.ts.map +1 -1
  72. package/dist/oauth/oauth.service.js +125 -23
  73. package/dist/oauth/oauth.service.js.map +1 -1
  74. package/dist/oauth/providers/abstract.provider.d.ts +1 -1
  75. package/dist/oauth/providers/abstract.provider.d.ts.map +1 -1
  76. package/dist/oauth/providers/facebook.provider.d.ts +5 -2
  77. package/dist/oauth/providers/facebook.provider.d.ts.map +1 -1
  78. package/dist/oauth/providers/facebook.provider.js +34 -19
  79. package/dist/oauth/providers/facebook.provider.js.map +1 -1
  80. package/dist/oauth/providers/github.provider.d.ts +6 -2
  81. package/dist/oauth/providers/github.provider.d.ts.map +1 -1
  82. package/dist/oauth/providers/github.provider.js +43 -20
  83. package/dist/oauth/providers/github.provider.js.map +1 -1
  84. package/dist/oauth/providers/google.provider.d.ts +5 -2
  85. package/dist/oauth/providers/google.provider.d.ts.map +1 -1
  86. package/dist/oauth/providers/google.provider.js +34 -19
  87. package/dist/oauth/providers/google.provider.js.map +1 -1
  88. package/dist/oauth/providers/microsoft-entra-id.provider.d.ts +5 -2
  89. package/dist/oauth/providers/microsoft-entra-id.provider.d.ts.map +1 -1
  90. package/dist/oauth/providers/microsoft-entra-id.provider.js +43 -40
  91. package/dist/oauth/providers/microsoft-entra-id.provider.js.map +1 -1
  92. package/dist/oauth/providers/microsoft.provider.d.ts +5 -2
  93. package/dist/oauth/providers/microsoft.provider.d.ts.map +1 -1
  94. package/dist/oauth/providers/microsoft.provider.js +34 -19
  95. package/dist/oauth/providers/microsoft.provider.js.map +1 -1
  96. package/dist/profile/profile.controller.js +1 -1
  97. package/dist/profile/profile.controller.js.map +1 -1
  98. package/dist/security/security.service.d.ts +2 -0
  99. package/dist/security/security.service.d.ts.map +1 -1
  100. package/dist/security/security.service.js +21 -0
  101. package/dist/security/security.service.js.map +1 -1
  102. package/dist/setting/setting.service.d.ts +26 -1
  103. package/dist/setting/setting.service.d.ts.map +1 -1
  104. package/dist/setting/setting.service.js +240 -5
  105. package/dist/setting/setting.service.js.map +1 -1
  106. package/dist/user/user.controller.d.ts +2 -1
  107. package/dist/user/user.controller.d.ts.map +1 -1
  108. package/dist/user/user.controller.js +5 -4
  109. package/dist/user/user.controller.js.map +1 -1
  110. package/dist/user/user.service.d.ts +4 -2
  111. package/dist/user/user.service.d.ts.map +1 -1
  112. package/dist/user/user.service.js +19 -4
  113. package/dist/user/user.service.js.map +1 -1
  114. package/dist/webhook-integration/dto/webhook-action.dto.d.ts +17 -2
  115. package/dist/webhook-integration/dto/webhook-action.dto.d.ts.map +1 -1
  116. package/dist/webhook-integration/dto/webhook-action.dto.js +93 -2
  117. package/dist/webhook-integration/dto/webhook-action.dto.js.map +1 -1
  118. package/dist/webhook-integration/webhook-command.registry.d.ts +15 -0
  119. package/dist/webhook-integration/webhook-command.registry.d.ts.map +1 -0
  120. package/dist/webhook-integration/webhook-command.registry.js +36 -0
  121. package/dist/webhook-integration/webhook-command.registry.js.map +1 -0
  122. package/dist/webhook-integration/webhook-integration.controller.d.ts +19 -0
  123. package/dist/webhook-integration/webhook-integration.controller.d.ts.map +1 -1
  124. package/dist/webhook-integration/webhook-integration.controller.js +38 -0
  125. package/dist/webhook-integration/webhook-integration.controller.js.map +1 -1
  126. package/dist/webhook-integration/webhook-integration.module.d.ts.map +1 -1
  127. package/dist/webhook-integration/webhook-integration.module.js +5 -0
  128. package/dist/webhook-integration/webhook-integration.module.js.map +1 -1
  129. package/dist/webhook-integration/webhook-integration.service.d.ts +25 -1
  130. package/dist/webhook-integration/webhook-integration.service.d.ts.map +1 -1
  131. package/dist/webhook-integration/webhook-integration.service.js +332 -23
  132. package/dist/webhook-integration/webhook-integration.service.js.map +1 -1
  133. package/hedhog/data/integration_provider.yaml +16 -0
  134. package/hedhog/data/menu.yaml +0 -16
  135. package/hedhog/data/route.yaml +32 -383
  136. package/hedhog/data/setting_group.yaml +179 -174
  137. package/hedhog/data/setting_subgroup.yaml +16 -0
  138. package/hedhog/frontend/app/account/components/tokens.tsx.ejs +421 -0
  139. package/hedhog/frontend/app/account/layout.tsx.ejs +6 -3
  140. package/hedhog/frontend/app/account/mcp-tokens/page.tsx.ejs +2 -2
  141. package/hedhog/frontend/app/account/tokens/page.tsx.ejs +5 -0
  142. package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +414 -76
  143. package/hedhog/frontend/app/configurations/layout.tsx.ejs +1 -1
  144. package/hedhog/frontend/app/dashboard/dashboard-home-tabs.tsx.ejs +3 -1
  145. package/hedhog/frontend/app/integration/profiles/page.tsx.ejs +186 -18
  146. package/hedhog/frontend/app/integrations-webhooks/page.tsx.ejs +597 -21
  147. package/hedhog/frontend/app/mail/profiles/page.tsx.ejs +2 -2
  148. package/hedhog/frontend/app/setting-list/page.tsx.ejs +229 -395
  149. package/hedhog/frontend/messages/en.json +67 -3
  150. package/hedhog/frontend/messages/pt.json +67 -3
  151. package/hedhog/table/file.yaml +1 -1
  152. package/hedhog/table/mcp_api_key.yaml +4 -0
  153. package/hedhog/table/webhook_action.yaml +57 -1
  154. package/package.json +5 -4
  155. package/src/auth/guards/auth.guard.ts +32 -0
  156. package/src/file/file.controller.ts +51 -42
  157. package/src/file/file.service.ts +31 -52
  158. package/src/file/provider/azure.provider.ts +14 -28
  159. package/src/file/provider/gcs.provider.ts +9 -9
  160. package/src/file/provider/local.provider.ts +10 -13
  161. package/src/file/provider/provider.factory.ts +19 -7
  162. package/src/file/provider/s3.provider.ts +4 -20
  163. package/src/file/provider/spaces.provider.ts +4 -19
  164. package/src/integration-profile/integration-profile.controller.ts +2 -0
  165. package/src/integration-profile/integration-profile.service.ts +7 -2
  166. package/src/integration-profile/integration-profile.utils.ts +17 -0
  167. package/src/mail/mail.module.ts +14 -4
  168. package/src/mail/mail.service.ts +12 -7
  169. package/src/mcp/guards/mcp-auth.guard.ts +1 -1
  170. package/src/mcp/mcp-api-key.controller.ts +2 -2
  171. package/src/mcp/mcp-api-key.service.ts +6 -2
  172. package/src/mcp/mcp.module.ts +1 -3
  173. package/src/oauth/interfaces/OAuthProvider.ts +1 -1
  174. package/src/oauth/oauth.controller.ts +30 -2
  175. package/src/oauth/oauth.service.ts +194 -35
  176. package/src/oauth/providers/abstract.provider.ts +1 -1
  177. package/src/oauth/providers/facebook.provider.ts +36 -20
  178. package/src/oauth/providers/github.provider.ts +46 -23
  179. package/src/oauth/providers/google.provider.ts +36 -20
  180. package/src/oauth/providers/microsoft-entra-id.provider.ts +42 -46
  181. package/src/oauth/providers/microsoft.provider.ts +36 -21
  182. package/src/profile/profile.controller.ts +2 -2
  183. package/src/security/security.service.ts +34 -1
  184. package/src/setting/setting.service.ts +307 -7
  185. package/src/user/user.controller.ts +4 -1
  186. package/src/user/user.service.ts +20 -2
  187. package/src/webhook-integration/dto/webhook-action.dto.ts +85 -4
  188. package/src/webhook-integration/webhook-command.registry.ts +42 -0
  189. package/src/webhook-integration/webhook-integration.controller.ts +20 -0
  190. package/src/webhook-integration/webhook-integration.module.ts +50 -45
  191. package/src/webhook-integration/webhook-integration.service.ts +374 -11
  192. package/dist/mail-profile/dto/create.dto.d.ts +0 -23
  193. package/dist/mail-profile/dto/create.dto.d.ts.map +0 -1
  194. package/dist/mail-profile/dto/create.dto.js +0 -139
  195. package/dist/mail-profile/dto/create.dto.js.map +0 -1
  196. package/dist/mail-profile/dto/update.dto.d.ts +0 -6
  197. package/dist/mail-profile/dto/update.dto.d.ts.map +0 -1
  198. package/dist/mail-profile/dto/update.dto.js +0 -9
  199. package/dist/mail-profile/dto/update.dto.js.map +0 -1
  200. package/dist/mail-profile/mail-profile.controller.d.ts +0 -94
  201. package/dist/mail-profile/mail-profile.controller.d.ts.map +0 -1
  202. package/dist/mail-profile/mail-profile.controller.js +0 -95
  203. package/dist/mail-profile/mail-profile.controller.js.map +0 -1
  204. package/dist/mail-profile/mail-profile.module.d.ts +0 -3
  205. package/dist/mail-profile/mail-profile.module.d.ts.map +0 -1
  206. package/dist/mail-profile/mail-profile.module.js +0 -31
  207. package/dist/mail-profile/mail-profile.module.js.map +0 -1
  208. package/dist/mail-profile/mail-profile.service.d.ts +0 -109
  209. package/dist/mail-profile/mail-profile.service.d.ts.map +0 -1
  210. package/dist/mail-profile/mail-profile.service.js +0 -278
  211. package/dist/mail-profile/mail-profile.service.js.map +0 -1
  212. package/dist/mcp/tools/core-mail-profile.mcp-tools.d.ts +0 -20
  213. package/dist/mcp/tools/core-mail-profile.mcp-tools.d.ts.map +0 -1
  214. package/dist/mcp/tools/core-mail-profile.mcp-tools.js +0 -96
  215. package/dist/mcp/tools/core-mail-profile.mcp-tools.js.map +0 -1
  216. package/dist/whatsapp-profile/dto/create.dto.d.ts +0 -11
  217. package/dist/whatsapp-profile/dto/create.dto.d.ts.map +0 -1
  218. package/dist/whatsapp-profile/dto/create.dto.js +0 -55
  219. package/dist/whatsapp-profile/dto/create.dto.js.map +0 -1
  220. package/dist/whatsapp-profile/dto/update.dto.d.ts +0 -6
  221. package/dist/whatsapp-profile/dto/update.dto.d.ts.map +0 -1
  222. package/dist/whatsapp-profile/dto/update.dto.js +0 -9
  223. package/dist/whatsapp-profile/dto/update.dto.js.map +0 -1
  224. package/dist/whatsapp-profile/whatsapp-profile.controller.d.ts +0 -54
  225. package/dist/whatsapp-profile/whatsapp-profile.controller.d.ts.map +0 -1
  226. package/dist/whatsapp-profile/whatsapp-profile.controller.js +0 -85
  227. package/dist/whatsapp-profile/whatsapp-profile.controller.js.map +0 -1
  228. package/dist/whatsapp-profile/whatsapp-profile.module.d.ts +0 -3
  229. package/dist/whatsapp-profile/whatsapp-profile.module.d.ts.map +0 -1
  230. package/dist/whatsapp-profile/whatsapp-profile.module.js +0 -26
  231. package/dist/whatsapp-profile/whatsapp-profile.module.js.map +0 -1
  232. package/dist/whatsapp-profile/whatsapp-profile.service.d.ts +0 -63
  233. package/dist/whatsapp-profile/whatsapp-profile.service.d.ts.map +0 -1
  234. package/dist/whatsapp-profile/whatsapp-profile.service.js +0 -138
  235. package/dist/whatsapp-profile/whatsapp-profile.service.js.map +0 -1
  236. package/hedhog/data/mail_profile.yaml +0 -18
  237. package/hedhog/data/whatsapp_profile.yaml +0 -6
  238. package/hedhog/table/mail_profile.yaml +0 -75
  239. package/hedhog/table/whatsapp_profile.yaml +0 -26
  240. package/src/mail-profile/dto/create.dto.ts +0 -145
  241. package/src/mail-profile/dto/update.dto.ts +0 -4
  242. package/src/mail-profile/mail-profile.controller.ts +0 -56
  243. package/src/mail-profile/mail-profile.module.ts +0 -18
  244. package/src/mail-profile/mail-profile.service.ts +0 -308
  245. package/src/mcp/tools/core-mail-profile.mcp-tools.ts +0 -69
  246. package/src/whatsapp-profile/dto/create.dto.ts +0 -36
  247. package/src/whatsapp-profile/dto/update.dto.ts +0 -4
  248. package/src/whatsapp-profile/whatsapp-profile.controller.ts +0 -51
  249. package/src/whatsapp-profile/whatsapp-profile.module.ts +0 -13
  250. package/src/whatsapp-profile/whatsapp-profile.service.ts +0 -160
@@ -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
+ }