@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.
- package/dist/ai/ai.service.d.ts +3 -12
- package/dist/ai/ai.service.d.ts.map +1 -1
- package/dist/ai/ai.service.js +34 -125
- package/dist/ai/ai.service.js.map +1 -1
- package/dist/auth/guards/auth.guard.d.ts +6 -1
- package/dist/auth/guards/auth.guard.d.ts.map +1 -1
- package/dist/auth/guards/auth.guard.js +33 -2
- package/dist/auth/guards/auth.guard.js.map +1 -1
- package/dist/core.module.d.ts.map +1 -1
- package/dist/core.module.js +3 -3
- package/dist/core.module.js.map +1 -1
- package/dist/file/file.controller.d.ts +4 -2
- package/dist/file/file.controller.d.ts.map +1 -1
- package/dist/file/file.controller.js +44 -38
- package/dist/file/file.controller.js.map +1 -1
- package/dist/file/file.service.d.ts +2 -2
- package/dist/file/file.service.d.ts.map +1 -1
- package/dist/file/file.service.js +58 -89
- package/dist/file/file.service.js.map +1 -1
- package/dist/file/provider/azure.provider.d.ts.map +1 -1
- package/dist/file/provider/azure.provider.js +10 -15
- package/dist/file/provider/azure.provider.js.map +1 -1
- package/dist/file/provider/gcs.provider.d.ts.map +1 -1
- package/dist/file/provider/gcs.provider.js +8 -9
- package/dist/file/provider/gcs.provider.js.map +1 -1
- package/dist/file/provider/local.provider.d.ts.map +1 -1
- package/dist/file/provider/local.provider.js +11 -10
- package/dist/file/provider/local.provider.js.map +1 -1
- package/dist/file/provider/provider.enum.d.ts +2 -1
- package/dist/file/provider/provider.enum.d.ts.map +1 -1
- package/dist/file/provider/provider.enum.js +1 -0
- package/dist/file/provider/provider.enum.js.map +1 -1
- package/dist/file/provider/provider.factory.d.ts +1 -1
- package/dist/file/provider/provider.factory.d.ts.map +1 -1
- package/dist/file/provider/provider.factory.js +18 -5
- package/dist/file/provider/provider.factory.js.map +1 -1
- package/dist/file/provider/s3.provider.d.ts.map +1 -1
- package/dist/file/provider/s3.provider.js +4 -20
- package/dist/file/provider/s3.provider.js.map +1 -1
- package/dist/file/provider/spaces.provider.d.ts +27 -0
- package/dist/file/provider/spaces.provider.d.ts.map +1 -0
- package/dist/file/provider/spaces.provider.js +203 -0
- package/dist/file/provider/spaces.provider.js.map +1 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/integration-profile/dto/create.dto.d.ts +9 -0
- package/dist/integration-profile/dto/create.dto.d.ts.map +1 -0
- package/dist/integration-profile/dto/create.dto.js +54 -0
- package/dist/integration-profile/dto/create.dto.js.map +1 -0
- package/dist/integration-profile/dto/export.dto.d.ts +5 -0
- package/dist/integration-profile/dto/export.dto.d.ts.map +1 -0
- package/dist/integration-profile/dto/export.dto.js +29 -0
- package/dist/integration-profile/dto/export.dto.js.map +1 -0
- package/dist/integration-profile/dto/import-confirm.dto.d.ts +17 -0
- package/dist/integration-profile/dto/import-confirm.dto.d.ts.map +1 -0
- package/dist/integration-profile/dto/import-confirm.dto.js +77 -0
- package/dist/integration-profile/dto/import-confirm.dto.js.map +1 -0
- package/dist/integration-profile/dto/update.dto.d.ts.map +1 -0
- package/dist/integration-profile/dto/update.dto.js.map +1 -0
- package/dist/integration-profile/integration-profile.controller.d.ts +142 -0
- package/dist/integration-profile/integration-profile.controller.d.ts.map +1 -0
- package/dist/integration-profile/integration-profile.controller.js +166 -0
- package/dist/integration-profile/integration-profile.controller.js.map +1 -0
- package/dist/integration-profile/integration-profile.module.d.ts +3 -0
- package/dist/integration-profile/integration-profile.module.d.ts.map +1 -0
- package/dist/{mail-profile/mail-profile.module.js → integration-profile/integration-profile.module.js} +11 -13
- package/dist/integration-profile/integration-profile.module.js.map +1 -0
- package/dist/integration-profile/integration-profile.service.d.ts +160 -0
- package/dist/integration-profile/integration-profile.service.d.ts.map +1 -0
- package/dist/integration-profile/integration-profile.service.js +609 -0
- package/dist/integration-profile/integration-profile.service.js.map +1 -0
- package/dist/integration-profile/integration-profile.utils.d.ts +30 -0
- package/dist/integration-profile/integration-profile.utils.d.ts.map +1 -0
- package/dist/integration-profile/integration-profile.utils.js +156 -0
- package/dist/integration-profile/integration-profile.utils.js.map +1 -0
- package/dist/mail/mail.module.d.ts.map +1 -1
- package/dist/mail/mail.module.js +36 -23
- package/dist/mail/mail.module.js.map +1 -1
- package/dist/mail/mail.service.d.ts +4 -2
- package/dist/mail/mail.service.d.ts.map +1 -1
- package/dist/mail/mail.service.js +33 -51
- package/dist/mail/mail.service.js.map +1 -1
- package/dist/mcp/guards/mcp-auth.guard.js +1 -1
- package/dist/mcp/guards/mcp-auth.guard.js.map +1 -1
- package/dist/mcp/mcp-api-key.controller.d.ts +2 -0
- package/dist/mcp/mcp-api-key.controller.d.ts.map +1 -1
- package/dist/mcp/mcp-api-key.controller.js +1 -1
- package/dist/mcp/mcp-api-key.controller.js.map +1 -1
- package/dist/mcp/mcp-api-key.service.d.ts +2 -1
- package/dist/mcp/mcp-api-key.service.d.ts.map +1 -1
- package/dist/mcp/mcp-api-key.service.js +6 -2
- package/dist/mcp/mcp-api-key.service.js.map +1 -1
- package/dist/mcp/mcp.module.d.ts.map +1 -1
- package/dist/mcp/mcp.module.js +3 -5
- package/dist/mcp/mcp.module.js.map +1 -1
- package/dist/mcp-chat/mcp-chat.service.d.ts.map +1 -1
- package/dist/mcp-chat/mcp-chat.service.js +17 -16
- package/dist/mcp-chat/mcp-chat.service.js.map +1 -1
- package/dist/oauth/interfaces/OAuthProvider.d.ts +1 -1
- package/dist/oauth/interfaces/OAuthProvider.d.ts.map +1 -1
- package/dist/oauth/oauth.controller.d.ts +4 -1
- package/dist/oauth/oauth.controller.d.ts.map +1 -1
- package/dist/oauth/oauth.controller.js +31 -5
- package/dist/oauth/oauth.controller.js.map +1 -1
- package/dist/oauth/oauth.service.d.ts +12 -1
- package/dist/oauth/oauth.service.d.ts.map +1 -1
- package/dist/oauth/oauth.service.js +125 -23
- package/dist/oauth/oauth.service.js.map +1 -1
- package/dist/oauth/providers/abstract.provider.d.ts +1 -1
- package/dist/oauth/providers/abstract.provider.d.ts.map +1 -1
- package/dist/oauth/providers/facebook.provider.d.ts +5 -2
- package/dist/oauth/providers/facebook.provider.d.ts.map +1 -1
- package/dist/oauth/providers/facebook.provider.js +34 -19
- package/dist/oauth/providers/facebook.provider.js.map +1 -1
- package/dist/oauth/providers/github.provider.d.ts +6 -2
- package/dist/oauth/providers/github.provider.d.ts.map +1 -1
- package/dist/oauth/providers/github.provider.js +43 -20
- package/dist/oauth/providers/github.provider.js.map +1 -1
- package/dist/oauth/providers/google.provider.d.ts +5 -2
- package/dist/oauth/providers/google.provider.d.ts.map +1 -1
- package/dist/oauth/providers/google.provider.js +34 -19
- package/dist/oauth/providers/google.provider.js.map +1 -1
- package/dist/oauth/providers/microsoft-entra-id.provider.d.ts +5 -2
- package/dist/oauth/providers/microsoft-entra-id.provider.d.ts.map +1 -1
- package/dist/oauth/providers/microsoft-entra-id.provider.js +43 -40
- package/dist/oauth/providers/microsoft-entra-id.provider.js.map +1 -1
- package/dist/oauth/providers/microsoft.provider.d.ts +5 -2
- package/dist/oauth/providers/microsoft.provider.d.ts.map +1 -1
- package/dist/oauth/providers/microsoft.provider.js +34 -19
- package/dist/oauth/providers/microsoft.provider.js.map +1 -1
- package/dist/profile/profile.controller.js +1 -1
- package/dist/profile/profile.controller.js.map +1 -1
- package/dist/security/security.service.d.ts +2 -0
- package/dist/security/security.service.d.ts.map +1 -1
- package/dist/security/security.service.js +21 -0
- package/dist/security/security.service.js.map +1 -1
- package/dist/setting/setting.service.d.ts +26 -1
- package/dist/setting/setting.service.d.ts.map +1 -1
- package/dist/setting/setting.service.js +240 -7
- package/dist/setting/setting.service.js.map +1 -1
- package/dist/user/user.controller.d.ts +2 -1
- package/dist/user/user.controller.d.ts.map +1 -1
- package/dist/user/user.controller.js +5 -4
- package/dist/user/user.controller.js.map +1 -1
- package/dist/user/user.service.d.ts +4 -2
- package/dist/user/user.service.d.ts.map +1 -1
- package/dist/user/user.service.js +19 -4
- package/dist/user/user.service.js.map +1 -1
- package/dist/webhook-integration/dto/webhook-action.dto.d.ts +17 -2
- package/dist/webhook-integration/dto/webhook-action.dto.d.ts.map +1 -1
- package/dist/webhook-integration/dto/webhook-action.dto.js +93 -2
- package/dist/webhook-integration/dto/webhook-action.dto.js.map +1 -1
- package/dist/webhook-integration/event-webhook.controller.d.ts +1 -0
- package/dist/webhook-integration/event-webhook.controller.d.ts.map +1 -1
- package/dist/webhook-integration/event-webhook.controller.js +10 -0
- package/dist/webhook-integration/event-webhook.controller.js.map +1 -1
- package/dist/webhook-integration/event-webhook.service.d.ts +2 -0
- package/dist/webhook-integration/event-webhook.service.d.ts.map +1 -1
- package/dist/webhook-integration/event-webhook.service.js +30 -0
- package/dist/webhook-integration/event-webhook.service.js.map +1 -1
- package/dist/webhook-integration/webhook-command.registry.d.ts +15 -0
- package/dist/webhook-integration/webhook-command.registry.d.ts.map +1 -0
- package/dist/webhook-integration/webhook-command.registry.js +36 -0
- package/dist/webhook-integration/webhook-command.registry.js.map +1 -0
- package/dist/webhook-integration/webhook-integration.controller.d.ts +20 -0
- package/dist/webhook-integration/webhook-integration.controller.d.ts.map +1 -1
- package/dist/webhook-integration/webhook-integration.controller.js +48 -0
- package/dist/webhook-integration/webhook-integration.controller.js.map +1 -1
- package/dist/webhook-integration/webhook-integration.module.d.ts.map +1 -1
- package/dist/webhook-integration/webhook-integration.module.js +5 -0
- package/dist/webhook-integration/webhook-integration.module.js.map +1 -1
- package/dist/webhook-integration/webhook-integration.service.d.ts +27 -2
- package/dist/webhook-integration/webhook-integration.service.d.ts.map +1 -1
- package/dist/webhook-integration/webhook-integration.service.js +377 -50
- package/dist/webhook-integration/webhook-integration.service.js.map +1 -1
- package/hedhog/data/file_provider.yaml +9 -1
- package/hedhog/data/integration_provider.yaml +148 -0
- package/hedhog/data/integration_type.yaml +29 -0
- package/hedhog/data/menu.yaml +6 -21
- package/hedhog/data/role.yaml +8 -0
- package/hedhog/data/route.yaml +73 -373
- package/hedhog/data/setting_group.yaml +209 -365
- package/hedhog/data/setting_subgroup.yaml +21 -13
- package/hedhog/frontend/app/account/components/connected-accounts.tsx.ejs +25 -20
- package/hedhog/frontend/app/account/components/tokens.tsx.ejs +421 -0
- package/hedhog/frontend/app/account/layout.tsx.ejs +6 -3
- package/hedhog/frontend/app/account/mcp-tokens/page.tsx.ejs +2 -2
- package/hedhog/frontend/app/account/tokens/page.tsx.ejs +5 -0
- package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +938 -225
- package/hedhog/frontend/app/configurations/layout.tsx.ejs +1 -1
- package/hedhog/frontend/app/dashboard/dashboard-home-tabs.tsx.ejs +3 -1
- package/hedhog/frontend/app/integration/profiles/page.tsx.ejs +1830 -0
- package/hedhog/frontend/app/integrations-webhooks/page.tsx.ejs +829 -128
- package/hedhog/frontend/app/mail/profiles/page.tsx.ejs +1 -820
- package/hedhog/frontend/app/setting-list/page.tsx.ejs +229 -395
- package/hedhog/frontend/app/users/permissions.tsx.ejs +79 -2
- package/hedhog/frontend/app/users/user-sheet.tsx.ejs +7 -2
- package/hedhog/frontend/messages/en.json +239 -3
- package/hedhog/frontend/messages/pt.json +239 -3
- package/hedhog/table/file.yaml +1 -1
- package/hedhog/table/integration_profile.yaml +34 -0
- package/hedhog/table/integration_provider.yaml +23 -0
- package/hedhog/table/integration_type.yaml +19 -0
- package/hedhog/table/mcp_api_key.yaml +4 -0
- package/hedhog/table/webhook_action.yaml +141 -85
- package/package.json +4 -3
- package/src/ai/ai.service.ts +35 -153
- package/src/auth/guards/auth.guard.ts +32 -0
- package/src/core.module.ts +3 -3
- package/src/file/file.controller.ts +51 -42
- package/src/file/file.service.ts +67 -104
- package/src/file/provider/azure.provider.ts +14 -28
- package/src/file/provider/gcs.provider.ts +9 -9
- package/src/file/provider/local.provider.ts +10 -13
- package/src/file/provider/provider.enum.ts +1 -0
- package/src/file/provider/provider.factory.ts +21 -6
- package/src/file/provider/s3.provider.ts +4 -20
- package/src/file/provider/spaces.provider.ts +246 -0
- package/src/index.ts +6 -3
- package/src/integration-profile/dto/create.dto.ts +44 -0
- package/src/integration-profile/dto/export.dto.ts +15 -0
- package/src/integration-profile/dto/import-confirm.dto.ts +61 -0
- package/src/integration-profile/integration-profile.controller.ts +131 -0
- package/src/integration-profile/integration-profile.module.ts +16 -0
- package/src/integration-profile/integration-profile.service.ts +793 -0
- package/src/integration-profile/integration-profile.utils.ts +173 -0
- package/src/mail/mail.module.ts +41 -22
- package/src/mail/mail.service.ts +40 -64
- package/src/mcp/guards/mcp-auth.guard.ts +1 -1
- package/src/mcp/mcp-api-key.controller.ts +2 -2
- package/src/mcp/mcp-api-key.service.ts +6 -2
- package/src/mcp/mcp.module.ts +3 -5
- package/src/mcp-chat/mcp-chat.service.ts +23 -20
- package/src/oauth/interfaces/OAuthProvider.ts +1 -1
- package/src/oauth/oauth.controller.ts +30 -2
- package/src/oauth/oauth.service.ts +194 -35
- package/src/oauth/providers/abstract.provider.ts +1 -1
- package/src/oauth/providers/facebook.provider.ts +36 -20
- package/src/oauth/providers/github.provider.ts +46 -23
- package/src/oauth/providers/google.provider.ts +36 -20
- package/src/oauth/providers/microsoft-entra-id.provider.ts +42 -46
- package/src/oauth/providers/microsoft.provider.ts +36 -21
- package/src/profile/profile.controller.ts +2 -2
- package/src/security/security.service.ts +34 -1
- package/src/setting/setting.service.ts +307 -9
- package/src/user/user.controller.ts +4 -1
- package/src/user/user.service.ts +20 -2
- package/src/webhook-integration/dto/webhook-action.dto.ts +85 -4
- package/src/webhook-integration/event-webhook.controller.ts +55 -50
- package/src/webhook-integration/event-webhook.service.ts +422 -384
- package/src/webhook-integration/webhook-command.registry.ts +42 -0
- package/src/webhook-integration/webhook-integration.controller.ts +25 -0
- package/src/webhook-integration/webhook-integration.module.ts +50 -45
- package/src/webhook-integration/webhook-integration.service.ts +444 -53
- package/dist/mail-profile/dto/create.dto.d.ts +0 -23
- package/dist/mail-profile/dto/create.dto.d.ts.map +0 -1
- package/dist/mail-profile/dto/create.dto.js +0 -139
- package/dist/mail-profile/dto/create.dto.js.map +0 -1
- package/dist/mail-profile/dto/update.dto.d.ts.map +0 -1
- package/dist/mail-profile/dto/update.dto.js.map +0 -1
- package/dist/mail-profile/mail-profile.controller.d.ts +0 -94
- package/dist/mail-profile/mail-profile.controller.d.ts.map +0 -1
- package/dist/mail-profile/mail-profile.controller.js +0 -95
- package/dist/mail-profile/mail-profile.controller.js.map +0 -1
- package/dist/mail-profile/mail-profile.module.d.ts +0 -3
- package/dist/mail-profile/mail-profile.module.d.ts.map +0 -1
- package/dist/mail-profile/mail-profile.module.js.map +0 -1
- package/dist/mail-profile/mail-profile.service.d.ts +0 -109
- package/dist/mail-profile/mail-profile.service.d.ts.map +0 -1
- package/dist/mail-profile/mail-profile.service.js +0 -278
- package/dist/mail-profile/mail-profile.service.js.map +0 -1
- package/dist/mcp/tools/core-mail-profile.mcp-tools.d.ts +0 -32
- package/dist/mcp/tools/core-mail-profile.mcp-tools.d.ts.map +0 -1
- package/dist/mcp/tools/core-mail-profile.mcp-tools.js +0 -178
- package/dist/mcp/tools/core-mail-profile.mcp-tools.js.map +0 -1
- package/hedhog/data/mail_profile.yaml +0 -18
- package/hedhog/table/mail_profile.yaml +0 -75
- package/src/mail-profile/dto/create.dto.ts +0 -145
- package/src/mail-profile/mail-profile.controller.ts +0 -56
- package/src/mail-profile/mail-profile.module.ts +0 -18
- package/src/mail-profile/mail-profile.service.ts +0 -308
- package/src/mcp/tools/core-mail-profile.mcp-tools.ts +0 -128
- /package/dist/{mail-profile → integration-profile}/dto/update.dto.d.ts +0 -0
- /package/dist/{mail-profile → integration-profile}/dto/update.dto.js +0 -0
- /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:
|
|
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:
|
|
260
|
-
pt:
|
|
267
|
+
en: DigitalOcean Spaces
|
|
268
|
+
pt: DigitalOcean Spaces
|
|
261
269
|
description:
|
|
262
|
-
en:
|
|
263
|
-
pt: Configurações de
|
|
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
|
-
|
|
34
|
+
normalizedId: normalizeIntegrationProvider(p),
|
|
41
35
|
};
|
|
42
36
|
}
|
|
43
37
|
return {
|
|
44
38
|
...p,
|
|
45
|
-
|
|
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
|
|
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) =>
|
|
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.
|
|
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
|
-
<
|
|
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">
|
|
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.
|
|
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.
|
|
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: '
|
|
41
|
+
{ value: 'tokens', label: t('tabTokens'), icon: KeyRound },
|
|
42
42
|
];
|
|
43
43
|
|
|
44
44
|
return (
|
|
45
|
-
<div className="flex min-w-0 flex-col
|
|
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
|
|
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}`);
|