@hed-hog/core 0.0.347 → 0.0.350
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/dist/auth/auth.controller.d.ts +1 -1
- package/dist/auth/auth.service.d.ts +1 -1
- package/dist/core.module.d.ts.map +1 -1
- package/dist/core.module.js +3 -0
- package/dist/core.module.js.map +1 -1
- package/dist/dashboard/dashboard-user/dashboard-user.controller.d.ts +3 -3
- package/dist/dashboard/dashboard-user/dashboard-user.service.d.ts +3 -3
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/guards/mcp-auth.guard.d.ts +6 -1
- package/dist/mcp/guards/mcp-auth.guard.d.ts.map +1 -1
- package/dist/mcp/guards/mcp-auth.guard.js +29 -2
- package/dist/mcp/guards/mcp-auth.guard.js.map +1 -1
- package/dist/mcp/mcp-api-key.controller.d.ts +32 -0
- package/dist/mcp/mcp-api-key.controller.d.ts.map +1 -0
- package/dist/mcp/mcp-api-key.controller.js +64 -0
- package/dist/mcp/mcp-api-key.controller.js.map +1 -0
- package/dist/mcp/mcp-api-key.service.d.ts +27 -0
- package/dist/mcp/mcp-api-key.service.d.ts.map +1 -0
- package/dist/mcp/mcp-api-key.service.js +74 -0
- package/dist/mcp/mcp-api-key.service.js.map +1 -0
- package/dist/mcp/mcp-registry.service.d.ts +1 -2
- package/dist/mcp/mcp-registry.service.d.ts.map +1 -1
- package/dist/mcp/mcp-registry.service.js +6 -2
- package/dist/mcp/mcp-registry.service.js.map +1 -1
- package/dist/mcp/mcp.module.d.ts.map +1 -1
- package/dist/mcp/mcp.module.js +8 -1
- package/dist/mcp/mcp.module.js.map +1 -1
- package/dist/mcp-chat/mcp-chat.controller.d.ts +4 -0
- package/dist/mcp-chat/mcp-chat.controller.d.ts.map +1 -1
- package/dist/mcp-chat/mcp-chat.controller.js +46 -0
- package/dist/mcp-chat/mcp-chat.controller.js.map +1 -1
- package/dist/mcp-chat/mcp-chat.service.d.ts +6 -1
- package/dist/mcp-chat/mcp-chat.service.d.ts.map +1 -1
- package/dist/mcp-chat/mcp-chat.service.js +125 -17
- package/dist/mcp-chat/mcp-chat.service.js.map +1 -1
- package/dist/notification/dto/create-notification.dto.d.ts +26 -0
- package/dist/notification/dto/create-notification.dto.d.ts.map +1 -0
- package/dist/notification/dto/create-notification.dto.js +86 -0
- package/dist/notification/dto/create-notification.dto.js.map +1 -0
- package/dist/notification/dto/update-progress.dto.d.ts +6 -0
- package/dist/notification/dto/update-progress.dto.d.ts.map +1 -0
- package/dist/notification/dto/update-progress.dto.js +33 -0
- package/dist/notification/dto/update-progress.dto.js.map +1 -0
- package/dist/notification/notification.controller.d.ts +108 -0
- package/dist/notification/notification.controller.d.ts.map +1 -0
- package/dist/notification/notification.controller.js +118 -0
- package/dist/notification/notification.controller.js.map +1 -0
- package/dist/notification/notification.module.d.ts +3 -0
- package/dist/notification/notification.module.d.ts.map +1 -0
- package/dist/notification/notification.module.js +29 -0
- package/dist/notification/notification.module.js.map +1 -0
- package/dist/notification/notification.service.d.ts +98 -0
- package/dist/notification/notification.service.d.ts.map +1 -0
- package/dist/notification/notification.service.js +123 -0
- package/dist/notification/notification.service.js.map +1 -0
- package/dist/profile/profile.controller.d.ts +1 -1
- package/dist/profile/profile.service.d.ts +1 -1
- package/dist/setting/dto/update-setting-list.dto.d.ts +9 -0
- package/dist/setting/dto/update-setting-list.dto.d.ts.map +1 -0
- package/dist/setting/dto/update-setting-list.dto.js +44 -0
- package/dist/setting/dto/update-setting-list.dto.js.map +1 -0
- package/dist/setting/setting.controller.d.ts +19 -0
- package/dist/setting/setting.controller.d.ts.map +1 -1
- package/dist/setting/setting.controller.js +12 -0
- package/dist/setting/setting.controller.js.map +1 -1
- package/dist/setting/setting.service.d.ts +19 -0
- package/dist/setting/setting.service.d.ts.map +1 -1
- package/dist/setting/setting.service.js +79 -0
- package/dist/setting/setting.service.js.map +1 -1
- package/dist/task/task.service.d.ts +12 -0
- package/dist/task/task.service.d.ts.map +1 -1
- package/dist/task/task.service.js +60 -0
- package/dist/task/task.service.js.map +1 -1
- package/dist/user/user.controller.d.ts +2 -2
- package/dist/user/user.service.d.ts +3 -3
- package/dist/webhook-integration/webhook-integration.service.d.ts +4 -0
- package/dist/webhook-integration/webhook-integration.service.d.ts.map +1 -1
- package/dist/webhook-integration/webhook-integration.service.js +184 -34
- package/dist/webhook-integration/webhook-integration.service.js.map +1 -1
- package/dist/webhook-integration/webhook-utils.d.ts +1 -0
- package/dist/webhook-integration/webhook-utils.d.ts.map +1 -1
- package/dist/webhook-integration/webhook-utils.js +38 -0
- package/dist/webhook-integration/webhook-utils.js.map +1 -1
- package/hedhog/data/menu.yaml +48 -4
- package/hedhog/data/route.yaml +133 -0
- package/hedhog/data/setting_group.yaml +45 -0
- package/hedhog/data/setting_subgroup.yaml +16 -0
- package/hedhog/frontend/app/account/components/mcp-tokens.tsx.ejs +285 -0
- package/hedhog/frontend/app/account/layout.tsx.ejs +32 -55
- package/hedhog/frontend/app/account/mcp-tokens/page.tsx.ejs +5 -0
- package/hedhog/frontend/app/integrations-webhooks/page.tsx.ejs +2098 -1835
- package/hedhog/frontend/app/mcp_chat/chat-components.tsx.ejs +595 -0
- package/hedhog/frontend/app/mcp_chat/chat-types.ts.ejs +16 -0
- package/hedhog/frontend/app/mcp_chat/page.tsx.ejs +554 -383
- package/hedhog/frontend/app/setting-list/page.tsx.ejs +649 -0
- package/hedhog/frontend/app/users/page.tsx.ejs +445 -203
- package/hedhog/frontend/app/users/user-sheet.tsx.ejs +16 -37
- package/hedhog/frontend/messages/en.json +241 -2
- package/hedhog/frontend/messages/pt.json +241 -2
- package/hedhog/table/mcp_api_key.yaml +30 -0
- package/hedhog/table/notification.yaml +72 -0
- package/hedhog/table/webhook_action_log.yaml +49 -0
- package/package.json +2 -2
- package/src/core.module.ts +3 -0
- package/src/index.ts +5 -0
- package/src/mcp/guards/mcp-auth.guard.ts +64 -32
- package/src/mcp/mcp-api-key.controller.ts +42 -0
- package/src/mcp/mcp-api-key.service.ts +66 -0
- package/src/mcp/mcp-registry.service.ts +75 -69
- package/src/mcp/mcp.module.ts +43 -36
- package/src/mcp-chat/mcp-chat.controller.ts +113 -60
- package/src/mcp-chat/mcp-chat.service.ts +494 -332
- package/src/notification/dto/create-notification.dto.ts +71 -0
- package/src/notification/dto/update-progress.dto.ts +16 -0
- package/src/notification/notification.controller.ts +71 -0
- package/src/notification/notification.module.ts +16 -0
- package/src/notification/notification.service.ts +124 -0
- package/src/setting/dto/update-setting-list.dto.ts +32 -0
- package/src/setting/setting.controller.ts +9 -0
- package/src/setting/setting.service.ts +91 -0
- package/src/task/task.service.ts +72 -0
- package/src/webhook-integration/webhook-integration.service.ts +934 -735
- package/src/webhook-integration/webhook-utils.ts +136 -79
package/hedhog/data/route.yaml
CHANGED
|
@@ -1290,6 +1290,15 @@
|
|
|
1290
1290
|
slug: admin
|
|
1291
1291
|
- where:
|
|
1292
1292
|
slug: admin-access
|
|
1293
|
+
- url: /setting/:settingId/options
|
|
1294
|
+
method: PUT
|
|
1295
|
+
type: HTTP
|
|
1296
|
+
relations:
|
|
1297
|
+
role:
|
|
1298
|
+
- where:
|
|
1299
|
+
slug: admin
|
|
1300
|
+
- where:
|
|
1301
|
+
slug: admin-access
|
|
1293
1302
|
- url: /setting
|
|
1294
1303
|
method: PUT
|
|
1295
1304
|
type: HTTP
|
|
@@ -2124,6 +2133,16 @@
|
|
|
2124
2133
|
- where:
|
|
2125
2134
|
slug: admin-access
|
|
2126
2135
|
|
|
2136
|
+
- url: /mcp-chat/:id/messages/stream
|
|
2137
|
+
method: POST
|
|
2138
|
+
type: HTTP
|
|
2139
|
+
relations:
|
|
2140
|
+
role:
|
|
2141
|
+
- where:
|
|
2142
|
+
slug: admin
|
|
2143
|
+
- where:
|
|
2144
|
+
slug: admin-access
|
|
2145
|
+
|
|
2127
2146
|
- url: /core/pdf/example
|
|
2128
2147
|
method: GET
|
|
2129
2148
|
type: HTTP
|
|
@@ -2131,3 +2150,117 @@
|
|
|
2131
2150
|
role:
|
|
2132
2151
|
- where:
|
|
2133
2152
|
slug: admin
|
|
2153
|
+
|
|
2154
|
+
- url: /mcp/api-keys
|
|
2155
|
+
method: GET
|
|
2156
|
+
type: HTTP
|
|
2157
|
+
relations:
|
|
2158
|
+
role:
|
|
2159
|
+
- where:
|
|
2160
|
+
slug: admin
|
|
2161
|
+
- where:
|
|
2162
|
+
slug: admin-access
|
|
2163
|
+
- where:
|
|
2164
|
+
slug: user
|
|
2165
|
+
|
|
2166
|
+
- url: /mcp/api-keys
|
|
2167
|
+
method: POST
|
|
2168
|
+
type: HTTP
|
|
2169
|
+
relations:
|
|
2170
|
+
role:
|
|
2171
|
+
- where:
|
|
2172
|
+
slug: admin
|
|
2173
|
+
- where:
|
|
2174
|
+
slug: admin-access
|
|
2175
|
+
- where:
|
|
2176
|
+
slug: user
|
|
2177
|
+
|
|
2178
|
+
- url: /mcp/api-keys/:id
|
|
2179
|
+
method: DELETE
|
|
2180
|
+
type: HTTP
|
|
2181
|
+
relations:
|
|
2182
|
+
role:
|
|
2183
|
+
- where:
|
|
2184
|
+
slug: admin
|
|
2185
|
+
- where:
|
|
2186
|
+
slug: admin-access
|
|
2187
|
+
- where:
|
|
2188
|
+
slug: user
|
|
2189
|
+
|
|
2190
|
+
- url: /notification
|
|
2191
|
+
method: GET
|
|
2192
|
+
type: HTTP
|
|
2193
|
+
relations:
|
|
2194
|
+
role:
|
|
2195
|
+
- where:
|
|
2196
|
+
slug: admin
|
|
2197
|
+
- where:
|
|
2198
|
+
slug: user
|
|
2199
|
+
|
|
2200
|
+
- url: /notification/unread-count
|
|
2201
|
+
method: GET
|
|
2202
|
+
type: HTTP
|
|
2203
|
+
relations:
|
|
2204
|
+
role:
|
|
2205
|
+
- where:
|
|
2206
|
+
slug: admin
|
|
2207
|
+
- where:
|
|
2208
|
+
slug: user
|
|
2209
|
+
|
|
2210
|
+
- url: /notification/read-all
|
|
2211
|
+
method: PATCH
|
|
2212
|
+
type: HTTP
|
|
2213
|
+
relations:
|
|
2214
|
+
role:
|
|
2215
|
+
- where:
|
|
2216
|
+
slug: admin
|
|
2217
|
+
- where:
|
|
2218
|
+
slug: user
|
|
2219
|
+
|
|
2220
|
+
- url: /notification/:id/read
|
|
2221
|
+
method: PATCH
|
|
2222
|
+
type: HTTP
|
|
2223
|
+
relations:
|
|
2224
|
+
role:
|
|
2225
|
+
- where:
|
|
2226
|
+
slug: admin
|
|
2227
|
+
- where:
|
|
2228
|
+
slug: user
|
|
2229
|
+
|
|
2230
|
+
- url: /notification/:id/progress
|
|
2231
|
+
method: PATCH
|
|
2232
|
+
type: HTTP
|
|
2233
|
+
relations:
|
|
2234
|
+
role:
|
|
2235
|
+
- where:
|
|
2236
|
+
slug: admin
|
|
2237
|
+
- where:
|
|
2238
|
+
slug: user
|
|
2239
|
+
|
|
2240
|
+
- url: /notification/:id
|
|
2241
|
+
method: DELETE
|
|
2242
|
+
type: HTTP
|
|
2243
|
+
relations:
|
|
2244
|
+
role:
|
|
2245
|
+
- where:
|
|
2246
|
+
slug: admin
|
|
2247
|
+
- where:
|
|
2248
|
+
slug: user
|
|
2249
|
+
|
|
2250
|
+
- url: /notification
|
|
2251
|
+
method: POST
|
|
2252
|
+
type: HTTP
|
|
2253
|
+
relations:
|
|
2254
|
+
role:
|
|
2255
|
+
- where:
|
|
2256
|
+
slug: admin
|
|
2257
|
+
|
|
2258
|
+
- url: /notification
|
|
2259
|
+
method: DELETE
|
|
2260
|
+
type: HTTP
|
|
2261
|
+
relations:
|
|
2262
|
+
role:
|
|
2263
|
+
- where:
|
|
2264
|
+
slug: admin
|
|
2265
|
+
- where:
|
|
2266
|
+
slug: user
|
|
@@ -8,6 +8,17 @@
|
|
|
8
8
|
pt: Configurações gerais do sistema e configurações básicas
|
|
9
9
|
relations:
|
|
10
10
|
setting:
|
|
11
|
+
- slug: api-body-size-limit
|
|
12
|
+
type: number
|
|
13
|
+
component: input-number
|
|
14
|
+
name:
|
|
15
|
+
en: API Body Size Limit (MB)
|
|
16
|
+
pt: Limite do Corpo da Requisição (MB)
|
|
17
|
+
description:
|
|
18
|
+
en: Maximum size in megabytes for incoming API request bodies. Applies dynamically without restart (50 = 50 MB)
|
|
19
|
+
pt: Tamanho máximo em megabytes para corpos de requisição recebidos pela API. Aplica-se dinamicamente sem reiniciar (50 = 50 MB)
|
|
20
|
+
value: 50
|
|
21
|
+
user_override: false
|
|
11
22
|
- slug: url
|
|
12
23
|
type: string
|
|
13
24
|
name:
|
|
@@ -77,6 +88,34 @@
|
|
|
77
88
|
subgroup_id:
|
|
78
89
|
where:
|
|
79
90
|
slug: session-retention
|
|
91
|
+
- slug: webhook-log-retention-days
|
|
92
|
+
type: number
|
|
93
|
+
component: input-number
|
|
94
|
+
name:
|
|
95
|
+
en: Webhook Log Retention Period
|
|
96
|
+
pt: Período de Retenção de Logs de Webhook
|
|
97
|
+
description:
|
|
98
|
+
en: Number of days to keep incoming webhook execution logs before automatic deletion
|
|
99
|
+
pt: Número de dias para manter logs de execução de webhooks de entrada antes da exclusão automática
|
|
100
|
+
value: 30
|
|
101
|
+
user_override: false
|
|
102
|
+
subgroup_id:
|
|
103
|
+
where:
|
|
104
|
+
slug: webhook-log-retention
|
|
105
|
+
- slug: notification-retention-days
|
|
106
|
+
type: number
|
|
107
|
+
component: input-number
|
|
108
|
+
name:
|
|
109
|
+
en: Notification Retention Period
|
|
110
|
+
pt: Período de Retenção de Notificações
|
|
111
|
+
description:
|
|
112
|
+
en: Number of days to keep user notifications before automatic deletion. Unread notifications are also removed after this period
|
|
113
|
+
pt: Número de dias para manter notificações de usuários antes da exclusão automática. Notificações não lidas também são removidas após este período
|
|
114
|
+
value: 30
|
|
115
|
+
user_override: false
|
|
116
|
+
subgroup_id:
|
|
117
|
+
where:
|
|
118
|
+
slug: notification-retention
|
|
80
119
|
- slug: authentication
|
|
81
120
|
icon: lock-password
|
|
82
121
|
name:
|
|
@@ -1469,6 +1508,12 @@
|
|
|
1469
1508
|
order: 7
|
|
1470
1509
|
- value: text/plain
|
|
1471
1510
|
order: 8
|
|
1511
|
+
- value: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
|
1512
|
+
order: 9
|
|
1513
|
+
- value: application/vnd.openxmlformats-officedocument.wordprocessingml.document
|
|
1514
|
+
order: 10
|
|
1515
|
+
- value: application/vnd.openxmlformats-officedocument.presentationml.presentation
|
|
1516
|
+
order: 11
|
|
1472
1517
|
subgroup_id:
|
|
1473
1518
|
where:
|
|
1474
1519
|
slug: storage-general
|
|
@@ -14,6 +14,22 @@
|
|
|
14
14
|
en: Rules for retaining and removing expired user sessions
|
|
15
15
|
pt: Regras para retenção e remoção de sessões expiradas de usuários
|
|
16
16
|
|
|
17
|
+
- slug: webhook-log-retention
|
|
18
|
+
name:
|
|
19
|
+
en: Webhook Log Retention
|
|
20
|
+
pt: Retenção de Logs de Webhook
|
|
21
|
+
description:
|
|
22
|
+
en: Rules for retaining and removing incoming webhook call logs
|
|
23
|
+
pt: Regras para retenção e remoção de logs de chamadas de webhook de entrada
|
|
24
|
+
|
|
25
|
+
- slug: notification-retention
|
|
26
|
+
name:
|
|
27
|
+
en: Notification Retention
|
|
28
|
+
pt: Retenção de Notificações
|
|
29
|
+
description:
|
|
30
|
+
en: Rules for retaining and automatically removing old user notifications
|
|
31
|
+
pt: Regras para retenção e remoção automática de notificações antigas de usuários
|
|
32
|
+
|
|
17
33
|
- slug: email-password-auth
|
|
18
34
|
name:
|
|
19
35
|
en: Email and Password Authentication
|
|
@@ -0,0 +1,285 @@
|
|
|
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 { Button } from '@/components/ui/button';
|
|
15
|
+
import {
|
|
16
|
+
Card,
|
|
17
|
+
CardContent,
|
|
18
|
+
CardDescription,
|
|
19
|
+
CardHeader,
|
|
20
|
+
CardTitle,
|
|
21
|
+
} from '@/components/ui/card';
|
|
22
|
+
import {
|
|
23
|
+
Dialog,
|
|
24
|
+
DialogContent,
|
|
25
|
+
DialogDescription,
|
|
26
|
+
DialogFooter,
|
|
27
|
+
DialogHeader,
|
|
28
|
+
DialogTitle,
|
|
29
|
+
} from '@/components/ui/dialog';
|
|
30
|
+
import { Input } from '@/components/ui/input';
|
|
31
|
+
import { Label } from '@/components/ui/label';
|
|
32
|
+
import {
|
|
33
|
+
Table,
|
|
34
|
+
TableBody,
|
|
35
|
+
TableCell,
|
|
36
|
+
TableHead,
|
|
37
|
+
TableHeader,
|
|
38
|
+
TableRow,
|
|
39
|
+
} from '@/components/ui/table';
|
|
40
|
+
import { usePagination } from '@/hooks/use-pagination';
|
|
41
|
+
import { formatDateTime } from '@/lib/format-date';
|
|
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 McpApiKey = {
|
|
49
|
+
id: number;
|
|
50
|
+
name: string;
|
|
51
|
+
token_prefix: string;
|
|
52
|
+
last_used_at: string | null;
|
|
53
|
+
expires_at: string | null;
|
|
54
|
+
created_at: string;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export function McpTokens() {
|
|
58
|
+
const { request, getSettingValue } = useApp();
|
|
59
|
+
const t = useTranslations('core.McpTokens');
|
|
60
|
+
|
|
61
|
+
const [keyToRevoke, setKeyToRevoke] = useState<number | null>(null);
|
|
62
|
+
const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false);
|
|
63
|
+
const [newKeyName, setNewKeyName] = useState('');
|
|
64
|
+
const [createdToken, setCreatedToken] = useState<string | null>(null);
|
|
65
|
+
const [isCopied, setIsCopied] = useState(false);
|
|
66
|
+
const [isCopiedUrl, setIsCopiedUrl] = useState(false);
|
|
67
|
+
const {
|
|
68
|
+
items: keys,
|
|
69
|
+
refetch,
|
|
70
|
+
page,
|
|
71
|
+
setPage,
|
|
72
|
+
totalItems,
|
|
73
|
+
pageSize,
|
|
74
|
+
setPageSize,
|
|
75
|
+
} = usePagination({ url: '/mcp/api-keys' });
|
|
76
|
+
|
|
77
|
+
const apiUrl = getSettingValue('api-url') as string | null;
|
|
78
|
+
const mcpUrl = apiUrl ? `${apiUrl.replace(/\/$/, '')}/mcp` : '/mcp';
|
|
79
|
+
|
|
80
|
+
const handleCreate = async () => {
|
|
81
|
+
if (!newKeyName.trim()) return;
|
|
82
|
+
try {
|
|
83
|
+
const res = await request<McpApiKey & { rawToken: string }>({
|
|
84
|
+
url: '/mcp/api-keys',
|
|
85
|
+
method: 'POST',
|
|
86
|
+
data: { name: newKeyName.trim() },
|
|
87
|
+
});
|
|
88
|
+
setCreatedToken(res.data.rawToken);
|
|
89
|
+
setNewKeyName('');
|
|
90
|
+
refetch();
|
|
91
|
+
} catch {
|
|
92
|
+
toast.error(t('createFailure'));
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const handleRevoke = async (id: number) => {
|
|
97
|
+
try {
|
|
98
|
+
await request({ url: `/mcp/api-keys/${id}`, method: 'DELETE' });
|
|
99
|
+
setKeyToRevoke(null);
|
|
100
|
+
refetch();
|
|
101
|
+
toast.success(t('revokeSuccess'));
|
|
102
|
+
} catch {
|
|
103
|
+
toast.error(t('revokeFailure'));
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const handleCopyToken = () => {
|
|
108
|
+
if (!createdToken) return;
|
|
109
|
+
navigator.clipboard.writeText(createdToken);
|
|
110
|
+
setIsCopied(true);
|
|
111
|
+
setTimeout(() => setIsCopied(false), 2000);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const handleCopyUrl = () => {
|
|
115
|
+
navigator.clipboard.writeText(mcpUrl);
|
|
116
|
+
setIsCopiedUrl(true);
|
|
117
|
+
setTimeout(() => setIsCopiedUrl(false), 2000);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const handleCloseCreateDialog = () => {
|
|
121
|
+
setIsCreateDialogOpen(false);
|
|
122
|
+
setCreatedToken(null);
|
|
123
|
+
setNewKeyName('');
|
|
124
|
+
setIsCopied(false);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<>
|
|
129
|
+
<Card>
|
|
130
|
+
<CardHeader className="flex flex-row items-start justify-between">
|
|
131
|
+
<div>
|
|
132
|
+
<CardTitle>{t('title')}</CardTitle>
|
|
133
|
+
<CardDescription className="mt-1">{t('description')}</CardDescription>
|
|
134
|
+
</div>
|
|
135
|
+
<Button size="sm" onClick={() => setIsCreateDialogOpen(true)}>
|
|
136
|
+
<Plus className="size-4 mr-2" />
|
|
137
|
+
{t('createToken')}
|
|
138
|
+
</Button>
|
|
139
|
+
</CardHeader>
|
|
140
|
+
<CardContent className="space-y-4">
|
|
141
|
+
<div className="flex items-center gap-2 rounded-md border bg-muted/40 px-3 py-2">
|
|
142
|
+
<span className="text-sm text-muted-foreground shrink-0">{t('mcpUrlLabel')}:</span>
|
|
143
|
+
<code className="flex-1 text-sm break-all">{mcpUrl}</code>
|
|
144
|
+
<Button variant="ghost" size="icon" className="size-7 shrink-0" onClick={handleCopyUrl}>
|
|
145
|
+
{isCopiedUrl ? <Check className="size-3.5 text-green-500" /> : <Copy className="size-3.5" />}
|
|
146
|
+
</Button>
|
|
147
|
+
</div>
|
|
148
|
+
|
|
149
|
+
{(keys as McpApiKey[]).length === 0 ? (
|
|
150
|
+
<div className="flex flex-col items-center gap-2 py-8 text-muted-foreground">
|
|
151
|
+
<KeyRound className="size-8 opacity-40" />
|
|
152
|
+
<p className="text-sm">{t('noTokens')}</p>
|
|
153
|
+
</div>
|
|
154
|
+
) : (
|
|
155
|
+
<Table>
|
|
156
|
+
<TableHeader>
|
|
157
|
+
<TableRow>
|
|
158
|
+
<TableHead>{t('name')}</TableHead>
|
|
159
|
+
<TableHead>{t('tokenPrefix')}</TableHead>
|
|
160
|
+
<TableHead>{t('lastUsed')}</TableHead>
|
|
161
|
+
<TableHead>{t('createdAt')}</TableHead>
|
|
162
|
+
<TableHead className="w-15" />
|
|
163
|
+
</TableRow>
|
|
164
|
+
</TableHeader>
|
|
165
|
+
<TableBody>
|
|
166
|
+
{(keys as McpApiKey[]).map((key) => (
|
|
167
|
+
<TableRow key={key.id}>
|
|
168
|
+
<TableCell className="font-medium">{key.name}</TableCell>
|
|
169
|
+
<TableCell>
|
|
170
|
+
<code className="rounded bg-muted px-1.5 py-0.5 text-xs">
|
|
171
|
+
{key.token_prefix}…
|
|
172
|
+
</code>
|
|
173
|
+
</TableCell>
|
|
174
|
+
<TableCell className="text-muted-foreground text-sm">
|
|
175
|
+
{key.last_used_at ? formatDateTime(key.last_used_at, getSettingValue) : t('never')}
|
|
176
|
+
</TableCell>
|
|
177
|
+
<TableCell className="text-muted-foreground text-sm">
|
|
178
|
+
{formatDateTime(key.created_at, getSettingValue)}
|
|
179
|
+
</TableCell>
|
|
180
|
+
<TableCell>
|
|
181
|
+
<Button
|
|
182
|
+
variant="ghost"
|
|
183
|
+
size="icon"
|
|
184
|
+
className="size-8 text-destructive hover:text-destructive"
|
|
185
|
+
onClick={() => setKeyToRevoke(key.id)}
|
|
186
|
+
>
|
|
187
|
+
<Trash2 className="size-4" />
|
|
188
|
+
</Button>
|
|
189
|
+
</TableCell>
|
|
190
|
+
</TableRow>
|
|
191
|
+
))}
|
|
192
|
+
</TableBody>
|
|
193
|
+
</Table>
|
|
194
|
+
)}
|
|
195
|
+
|
|
196
|
+
<PaginationFooter
|
|
197
|
+
currentPage={page}
|
|
198
|
+
pageSize={pageSize}
|
|
199
|
+
totalItems={totalItems}
|
|
200
|
+
onPageChange={setPage}
|
|
201
|
+
onPageSizeChange={(newSize) => {
|
|
202
|
+
setPageSize(newSize);
|
|
203
|
+
setPage(1);
|
|
204
|
+
}}
|
|
205
|
+
pageSizeOptions={[10]}
|
|
206
|
+
/>
|
|
207
|
+
</CardContent>
|
|
208
|
+
</Card>
|
|
209
|
+
|
|
210
|
+
{/* Revoke confirmation */}
|
|
211
|
+
<AlertDialog open={keyToRevoke !== null} onOpenChange={(open) => !open && setKeyToRevoke(null)}>
|
|
212
|
+
<AlertDialogContent>
|
|
213
|
+
<AlertDialogHeader>
|
|
214
|
+
<AlertDialogTitle>{t('revokeTokenTitle')}</AlertDialogTitle>
|
|
215
|
+
<AlertDialogDescription>{t('revokeTokenDescription')}</AlertDialogDescription>
|
|
216
|
+
</AlertDialogHeader>
|
|
217
|
+
<AlertDialogFooter>
|
|
218
|
+
<AlertDialogCancel>{t('cancel')}</AlertDialogCancel>
|
|
219
|
+
<AlertDialogAction
|
|
220
|
+
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
|
221
|
+
onClick={() => keyToRevoke !== null && handleRevoke(keyToRevoke)}
|
|
222
|
+
>
|
|
223
|
+
{t('revokeConfirm')}
|
|
224
|
+
</AlertDialogAction>
|
|
225
|
+
</AlertDialogFooter>
|
|
226
|
+
</AlertDialogContent>
|
|
227
|
+
</AlertDialog>
|
|
228
|
+
|
|
229
|
+
{/* Create token dialog */}
|
|
230
|
+
<Dialog open={isCreateDialogOpen} onOpenChange={(open) => !open && handleCloseCreateDialog()}>
|
|
231
|
+
<DialogContent className="sm:max-w-md">
|
|
232
|
+
{createdToken === null ? (
|
|
233
|
+
<>
|
|
234
|
+
<DialogHeader>
|
|
235
|
+
<DialogTitle>{t('createTokenTitle')}</DialogTitle>
|
|
236
|
+
<DialogDescription>{t('createTokenDescription')}</DialogDescription>
|
|
237
|
+
</DialogHeader>
|
|
238
|
+
<div className="space-y-2 py-2">
|
|
239
|
+
<Label htmlFor="token-name">{t('tokenNameLabel')}</Label>
|
|
240
|
+
<Input
|
|
241
|
+
id="token-name"
|
|
242
|
+
placeholder={t('tokenNamePlaceholder')}
|
|
243
|
+
value={newKeyName}
|
|
244
|
+
onChange={(e) => setNewKeyName(e.target.value)}
|
|
245
|
+
onKeyDown={(e) => e.key === 'Enter' && handleCreate()}
|
|
246
|
+
/>
|
|
247
|
+
</div>
|
|
248
|
+
<DialogFooter>
|
|
249
|
+
<Button variant="outline" onClick={handleCloseCreateDialog}>{t('cancel')}</Button>
|
|
250
|
+
<Button onClick={handleCreate} disabled={!newKeyName.trim()}>
|
|
251
|
+
{t('createToken')}
|
|
252
|
+
</Button>
|
|
253
|
+
</DialogFooter>
|
|
254
|
+
</>
|
|
255
|
+
) : (
|
|
256
|
+
<>
|
|
257
|
+
<DialogHeader>
|
|
258
|
+
<DialogTitle>{t('tokenCreatedTitle')}</DialogTitle>
|
|
259
|
+
</DialogHeader>
|
|
260
|
+
<div className="space-y-3 py-2">
|
|
261
|
+
<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">
|
|
262
|
+
{t('tokenCreatedWarning')}
|
|
263
|
+
</div>
|
|
264
|
+
<div className="space-y-1">
|
|
265
|
+
<Label>{t('tokenCreatedLabel')}</Label>
|
|
266
|
+
<div className="flex items-center gap-2">
|
|
267
|
+
<code className="flex-1 rounded border bg-muted px-3 py-2 text-xs break-all">
|
|
268
|
+
{createdToken}
|
|
269
|
+
</code>
|
|
270
|
+
<Button variant="outline" size="icon" onClick={handleCopyToken} className="shrink-0">
|
|
271
|
+
{isCopied ? <Check className="size-4 text-green-500" /> : <Copy className="size-4" />}
|
|
272
|
+
</Button>
|
|
273
|
+
</div>
|
|
274
|
+
</div>
|
|
275
|
+
</div>
|
|
276
|
+
<DialogFooter>
|
|
277
|
+
<Button onClick={handleCloseCreateDialog}>{t('closeDialog')}</Button>
|
|
278
|
+
</DialogFooter>
|
|
279
|
+
</>
|
|
280
|
+
)}
|
|
281
|
+
</DialogContent>
|
|
282
|
+
</Dialog>
|
|
283
|
+
</>
|
|
284
|
+
);
|
|
285
|
+
}
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { PageHeader } from '@/components/entity-list';
|
|
4
|
-
import {
|
|
4
|
+
import { cn } from '@/lib/utils';
|
|
5
5
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
TooltipTrigger,
|
|
9
|
-
} from '@/components/ui/tooltip';
|
|
10
|
-
import {
|
|
11
|
-
Link,
|
|
6
|
+
KeyRound,
|
|
7
|
+
Link as LinkIcon,
|
|
12
8
|
Lock,
|
|
13
9
|
LucideIcon,
|
|
14
10
|
Mail,
|
|
@@ -17,7 +13,8 @@ import {
|
|
|
17
13
|
User,
|
|
18
14
|
} from 'lucide-react';
|
|
19
15
|
import { useTranslations } from 'next-intl';
|
|
20
|
-
import
|
|
16
|
+
import Link from 'next/link';
|
|
17
|
+
import { usePathname } from 'next/navigation';
|
|
21
18
|
import { ReactNode } from 'react';
|
|
22
19
|
|
|
23
20
|
type LayoutProps = {
|
|
@@ -32,7 +29,6 @@ type TabConfig = {
|
|
|
32
29
|
|
|
33
30
|
export default function AccountLayout({ children }: LayoutProps) {
|
|
34
31
|
const pathname = usePathname();
|
|
35
|
-
const router = useRouter();
|
|
36
32
|
const t = useTranslations('core.Profile');
|
|
37
33
|
|
|
38
34
|
const tabs: TabConfig[] = [
|
|
@@ -40,26 +36,13 @@ export default function AccountLayout({ children }: LayoutProps) {
|
|
|
40
36
|
{ value: 'password', label: t('tabPassword'), icon: Lock },
|
|
41
37
|
{ value: 'email', label: t('tabEmail'), icon: Mail },
|
|
42
38
|
{ value: '2fa', label: t('tab2fa'), icon: Shield },
|
|
43
|
-
{ value: 'accounts', label: t('tabAccounts'), icon:
|
|
39
|
+
{ value: 'accounts', label: t('tabAccounts'), icon: LinkIcon },
|
|
44
40
|
{ value: 'sessions', label: t('tabSessions'), icon: Smartphone },
|
|
41
|
+
{ value: 'mcp-tokens', label: t('tabMcpTokens'), icon: KeyRound },
|
|
45
42
|
];
|
|
46
43
|
|
|
47
|
-
const getCurrentTab = () => {
|
|
48
|
-
if (pathname.includes('/profile')) return 'profile';
|
|
49
|
-
if (pathname.includes('/password')) return 'password';
|
|
50
|
-
if (pathname.includes('/email')) return 'email';
|
|
51
|
-
if (pathname.includes('/2fa')) return '2fa';
|
|
52
|
-
if (pathname.includes('/accounts')) return 'accounts';
|
|
53
|
-
if (pathname.includes('/sessions')) return 'sessions';
|
|
54
|
-
return 'profile';
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const handleTabChange = (value: string) => {
|
|
58
|
-
router.push(`/core/account/${value}`);
|
|
59
|
-
};
|
|
60
|
-
|
|
61
44
|
return (
|
|
62
|
-
<div className="flex flex-col
|
|
45
|
+
<div className="flex min-w-0 flex-col overflow-x-hidden px-4">
|
|
63
46
|
<PageHeader
|
|
64
47
|
breadcrumbs={[
|
|
65
48
|
{ label: t('home'), href: '/' },
|
|
@@ -68,38 +51,32 @@ export default function AccountLayout({ children }: LayoutProps) {
|
|
|
68
51
|
title={t('title')}
|
|
69
52
|
description={t('description')}
|
|
70
53
|
/>
|
|
71
|
-
<div className="py-2">
|
|
72
|
-
<Tabs value={getCurrentTab()} onValueChange={handleTabChange}>
|
|
73
|
-
<TabsList className="grid w-full grid-cols-6 lg:w-fit gap-1 rounded-sm">
|
|
74
|
-
{tabs.map((tab) => {
|
|
75
|
-
const Icon = tab.icon;
|
|
76
|
-
const isActive = getCurrentTab() === tab.value;
|
|
77
54
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
<div className="mt-2">{children}</div>
|
|
101
|
-
</Tabs>
|
|
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">
|
|
57
|
+
{tabs.map((tab) => {
|
|
58
|
+
const Icon = tab.icon;
|
|
59
|
+
const isActive = pathname.includes(`/${tab.value}`);
|
|
60
|
+
return (
|
|
61
|
+
<Link
|
|
62
|
+
key={tab.value}
|
|
63
|
+
href={`/core/account/${tab.value}`}
|
|
64
|
+
className={cn(
|
|
65
|
+
'flex shrink-0 items-center gap-2 border-b-2 px-3 py-3 text-sm font-medium whitespace-nowrap transition-colors',
|
|
66
|
+
isActive
|
|
67
|
+
? 'border-primary text-primary'
|
|
68
|
+
: 'border-transparent text-muted-foreground hover:border-muted-foreground hover:text-foreground'
|
|
69
|
+
)}
|
|
70
|
+
>
|
|
71
|
+
<Icon className="size-4" />
|
|
72
|
+
<span className="hidden sm:inline">{tab.label}</span>
|
|
73
|
+
</Link>
|
|
74
|
+
);
|
|
75
|
+
})}
|
|
76
|
+
</nav>
|
|
102
77
|
</div>
|
|
78
|
+
|
|
79
|
+
<div className="min-w-0 pb-25">{children}</div>
|
|
103
80
|
</div>
|
|
104
81
|
);
|
|
105
82
|
}
|