@hed-hog/core 0.0.370 → 0.0.373
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/LICENSE.md +21 -0
- package/README.md +2 -0
- package/dist/ai/ai.service.d.ts.map +1 -1
- package/dist/ai/ai.service.js +10 -7
- package/dist/ai/ai.service.js.map +1 -1
- package/dist/app-command/app-command.module.d.ts +3 -0
- package/dist/app-command/app-command.module.d.ts.map +1 -0
- package/dist/app-command/app-command.module.js +22 -0
- package/dist/app-command/app-command.module.js.map +1 -0
- package/dist/app-command/app-command.registry.d.ts +22 -0
- package/dist/app-command/app-command.registry.d.ts.map +1 -0
- package/dist/{webhook-integration/webhook-command.registry.js → app-command/app-command.registry.js} +6 -6
- package/dist/app-command/app-command.registry.js.map +1 -0
- package/dist/auth/auth.controller.d.ts +2 -2
- package/dist/auth/auth.controller.d.ts.map +1 -1
- package/dist/auth/auth.controller.js +20 -7
- package/dist/auth/auth.controller.js.map +1 -1
- package/dist/auth/auth.service.d.ts +3 -3
- package/dist/auth/auth.service.d.ts.map +1 -1
- package/dist/auth/auth.service.js +10 -36
- package/dist/auth/auth.service.js.map +1 -1
- package/dist/auth/dto/forget.dto.d.ts +1 -0
- package/dist/auth/dto/forget.dto.d.ts.map +1 -1
- package/dist/auth/dto/forget.dto.js +5 -0
- package/dist/auth/dto/forget.dto.js.map +1 -1
- package/dist/auth/guards/auth.guard.d.ts.map +1 -1
- package/dist/auth/guards/auth.guard.js +4 -1
- package/dist/auth/guards/auth.guard.js.map +1 -1
- package/dist/challenge/challenge.service.d.ts +8 -7
- package/dist/challenge/challenge.service.d.ts.map +1 -1
- package/dist/challenge/challenge.service.js +27 -19
- package/dist/challenge/challenge.service.js.map +1 -1
- package/dist/file/dto/upload.dto.d.ts +3 -0
- package/dist/file/dto/upload.dto.d.ts.map +1 -1
- package/dist/file/dto/upload.dto.js +22 -0
- package/dist/file/dto/upload.dto.js.map +1 -1
- package/dist/file/file.controller.d.ts +2 -1
- package/dist/file/file.controller.d.ts.map +1 -1
- package/dist/file/file.controller.js +29 -16
- package/dist/file/file.controller.js.map +1 -1
- package/dist/file/file.service.d.ts +51 -1
- package/dist/file/file.service.d.ts.map +1 -1
- package/dist/file/file.service.js +250 -18
- package/dist/file/file.service.js.map +1 -1
- package/dist/file/image-optimization.service.d.ts +17 -4
- package/dist/file/image-optimization.service.d.ts.map +1 -1
- package/dist/file/image-optimization.service.js +101 -46
- package/dist/file/image-optimization.service.js.map +1 -1
- package/dist/file/provider/s3.provider.d.ts.map +1 -1
- package/dist/file/provider/s3.provider.js +15 -7
- package/dist/file/provider/s3.provider.js.map +1 -1
- package/dist/file/provider/spaces.provider.d.ts.map +1 -1
- package/dist/file/provider/spaces.provider.js +18 -8
- package/dist/file/provider/spaces.provider.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/integration/services/event-subscriber.registry.d.ts +5 -0
- package/dist/integration/services/event-subscriber.registry.d.ts.map +1 -1
- package/dist/integration/services/event-subscriber.registry.js +15 -0
- package/dist/integration/services/event-subscriber.registry.js.map +1 -1
- package/dist/integration/services/outbox.processor.d.ts.map +1 -1
- package/dist/integration/services/outbox.processor.js +2 -4
- package/dist/integration/services/outbox.processor.js.map +1 -1
- package/dist/integration/services/outbox.service.d.ts +4 -2
- package/dist/integration/services/outbox.service.d.ts.map +1 -1
- package/dist/integration/services/outbox.service.js +26 -15
- package/dist/integration/services/outbox.service.js.map +1 -1
- package/dist/integration-profile/integration-credential-crypto.service.d.ts +43 -0
- package/dist/integration-profile/integration-credential-crypto.service.d.ts.map +1 -0
- package/dist/integration-profile/integration-credential-crypto.service.js +142 -0
- package/dist/integration-profile/integration-credential-crypto.service.js.map +1 -0
- package/dist/integration-profile/integration-profile.controller.d.ts +6 -7
- package/dist/integration-profile/integration-profile.controller.d.ts.map +1 -1
- package/dist/integration-profile/integration-profile.controller.js +4 -3
- package/dist/integration-profile/integration-profile.controller.js.map +1 -1
- package/dist/integration-profile/integration-profile.module.d.ts.map +1 -1
- package/dist/integration-profile/integration-profile.module.js +5 -2
- package/dist/integration-profile/integration-profile.module.js.map +1 -1
- package/dist/integration-profile/integration-profile.secrets.d.ts +16 -0
- package/dist/integration-profile/integration-profile.secrets.d.ts.map +1 -0
- package/dist/integration-profile/integration-profile.secrets.js +56 -0
- package/dist/integration-profile/integration-profile.secrets.js.map +1 -0
- package/dist/integration-profile/integration-profile.service.d.ts +125 -8
- package/dist/integration-profile/integration-profile.service.d.ts.map +1 -1
- package/dist/integration-profile/integration-profile.service.js +257 -68
- package/dist/integration-profile/integration-profile.service.js.map +1 -1
- package/dist/integration-profile/integration-profile.utils.d.ts +17 -0
- package/dist/integration-profile/integration-profile.utils.d.ts.map +1 -1
- package/dist/integration-profile/integration-profile.utils.js +31 -0
- package/dist/integration-profile/integration-profile.utils.js.map +1 -1
- package/dist/language/en.json +178 -0
- package/dist/language/pt.json +178 -0
- package/dist/mail/mail.controller.d.ts.map +1 -1
- package/dist/mail/mail.controller.js +5 -28
- package/dist/mail/mail.controller.js.map +1 -1
- package/dist/mail/mail.module.js +18 -18
- package/dist/mail/mail.module.js.map +1 -1
- package/dist/mail/mail.service.d.ts +13 -2
- package/dist/mail/mail.service.d.ts.map +1 -1
- package/dist/mail/mail.service.js +25 -15
- package/dist/mail/mail.service.js.map +1 -1
- package/dist/mcp-chat/mcp-chat.service.js +6 -5
- package/dist/mcp-chat/mcp-chat.service.js.map +1 -1
- package/dist/oauth/providers/facebook.provider.js +5 -5
- package/dist/oauth/providers/facebook.provider.js.map +1 -1
- package/dist/oauth/providers/github.provider.js +5 -5
- package/dist/oauth/providers/github.provider.js.map +1 -1
- package/dist/oauth/providers/google.provider.js +5 -5
- package/dist/oauth/providers/google.provider.js.map +1 -1
- package/dist/oauth/providers/microsoft-entra-id.provider.js +5 -5
- package/dist/oauth/providers/microsoft-entra-id.provider.js.map +1 -1
- package/dist/oauth/providers/microsoft.provider.js +5 -5
- package/dist/oauth/providers/microsoft.provider.js.map +1 -1
- package/dist/pdf/pdf.service.d.ts +4 -0
- package/dist/pdf/pdf.service.d.ts.map +1 -1
- package/dist/pdf/pdf.service.js +16 -9
- package/dist/pdf/pdf.service.js.map +1 -1
- package/dist/profile/profile.controller.d.ts +7 -7
- package/dist/profile/profile.controller.d.ts.map +1 -1
- package/dist/profile/profile.controller.js +28 -21
- package/dist/profile/profile.controller.js.map +1 -1
- package/dist/profile/profile.service.d.ts +7 -7
- package/dist/profile/profile.service.d.ts.map +1 -1
- package/dist/profile/profile.service.js +21 -25
- package/dist/profile/profile.service.js.map +1 -1
- package/dist/setting/setting.service.d.ts +1 -0
- package/dist/setting/setting.service.d.ts.map +1 -1
- package/dist/setting/setting.service.js +15 -2
- package/dist/setting/setting.service.js.map +1 -1
- package/dist/token/token.service.d.ts.map +1 -1
- package/dist/token/token.service.js +2 -1
- package/dist/token/token.service.js.map +1 -1
- package/dist/user/user.controller.d.ts +9 -0
- package/dist/user/user.controller.d.ts.map +1 -1
- package/dist/user/user.controller.js +13 -0
- package/dist/user/user.controller.js.map +1 -1
- package/dist/user/user.service.d.ts +7 -0
- package/dist/user/user.service.d.ts.map +1 -1
- package/dist/user/user.service.js +19 -0
- package/dist/user/user.service.js.map +1 -1
- package/dist/utils/webauthn-origin.d.ts +11 -0
- package/dist/utils/webauthn-origin.d.ts.map +1 -0
- package/dist/utils/webauthn-origin.js +37 -0
- package/dist/utils/webauthn-origin.js.map +1 -0
- package/dist/webhook-integration/webhook-integration.controller.d.ts +13 -0
- package/dist/webhook-integration/webhook-integration.controller.d.ts.map +1 -1
- package/dist/webhook-integration/webhook-integration.controller.js +29 -0
- package/dist/webhook-integration/webhook-integration.controller.js.map +1 -1
- package/dist/webhook-integration/webhook-integration.module.js +3 -3
- package/dist/webhook-integration/webhook-integration.module.js.map +1 -1
- package/dist/webhook-integration/webhook-integration.service.d.ts +18 -2
- package/dist/webhook-integration/webhook-integration.service.d.ts.map +1 -1
- package/dist/webhook-integration/webhook-integration.service.js +110 -6
- package/dist/webhook-integration/webhook-integration.service.js.map +1 -1
- package/hedhog/data/integration_provider.yaml +9 -0
- package/hedhog/data/integration_type.yaml +6 -0
- package/hedhog/data/menu.yaml +6 -0
- package/hedhog/data/role.yaml +7 -0
- package/hedhog/data/route.yaml +36 -0
- package/hedhog/data/setting_group.yaml +119 -13
- package/hedhog/frontend/app/access-log/page.tsx.ejs +35 -3
- package/hedhog/frontend/app/account/components/active-sessions.tsx.ejs +36 -3
- package/hedhog/frontend/app/account/components/mcp-tokens.tsx.ejs +39 -4
- package/hedhog/frontend/app/account/components/tokens.tsx.ejs +39 -4
- package/hedhog/frontend/app/ai_agent/page.tsx.ejs +35 -2
- package/hedhog/frontend/app/configurations/[slug]/components/byte-size-input.tsx.ejs +160 -0
- package/hedhog/frontend/app/configurations/[slug]/components/duration-input.tsx.ejs +157 -0
- package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +244 -63
- package/hedhog/frontend/app/dashboard/[slug]/dashboard-content.tsx.ejs +33 -1
- package/hedhog/frontend/app/dashboard/management/tabs/component-roles-tab.tsx.ejs +35 -3
- package/hedhog/frontend/app/dashboard/management/tabs/components-tab.tsx.ejs +36 -4
- package/hedhog/frontend/app/dashboard/management/tabs/dashboard-roles-tab.tsx.ejs +35 -3
- package/hedhog/frontend/app/dashboard/management/tabs/dashboards-tab.tsx.ejs +36 -4
- package/hedhog/frontend/app/dashboard/management/tabs/items-tab.tsx.ejs +35 -3
- package/hedhog/frontend/app/integration/profiles/page.tsx.ejs +156 -38
- package/hedhog/frontend/app/integrations-webhooks/page.tsx.ejs +118 -11
- package/hedhog/frontend/app/mail/log/page.tsx.ejs +37 -4
- package/hedhog/frontend/app/menu/page.tsx.ejs +34 -2
- package/hedhog/frontend/app/preferences/page.tsx.ejs +77 -0
- package/hedhog/frontend/app/roles/menus.tsx.ejs +35 -3
- package/hedhog/frontend/app/roles/page.tsx.ejs +34 -2
- package/hedhog/frontend/app/roles/routes.tsx.ejs +36 -4
- package/hedhog/frontend/app/roles/users.tsx.ejs +36 -4
- package/hedhog/frontend/app/routes/page.tsx.ejs +34 -2
- package/hedhog/frontend/app/setting-list/page.tsx.ejs +34 -2
- package/hedhog/frontend/app/users/page.tsx.ejs +51 -21
- package/hedhog/frontend/messages/en.json +69 -4
- package/hedhog/frontend/messages/pt.json +69 -4
- package/hedhog/table/setting.yaml +2 -0
- package/hedhog/table/webhook_call_log.yaml +49 -46
- package/package.json +9 -4
- package/src/ai/ai.service.ts +9 -6
- package/src/app-command/app-command.module.ts +9 -0
- package/src/{webhook-integration/webhook-command.registry.ts → app-command/app-command.registry.ts} +9 -9
- package/src/auth/auth.controller.ts +20 -4
- package/src/auth/auth.service.ts +10 -36
- package/src/auth/dto/forget.dto.ts +5 -1
- package/src/auth/guards/auth.guard.ts +4 -1
- package/src/challenge/challenge.service.ts +30 -19
- package/src/file/dto/upload.dto.ts +25 -1
- package/src/file/file.controller.ts +31 -18
- package/src/file/file.service.ts +341 -16
- package/src/file/image-optimization.service.ts +111 -46
- package/src/file/provider/s3.provider.ts +16 -7
- package/src/file/provider/spaces.provider.ts +15 -5
- package/src/index.ts +4 -1
- package/src/integration/services/event-subscriber.registry.ts +15 -0
- package/src/integration/services/outbox.processor.ts +2 -5
- package/src/integration/services/outbox.service.ts +35 -17
- package/src/integration-profile/integration-credential-crypto.service.ts +146 -0
- package/src/integration-profile/integration-profile.controller.ts +2 -0
- package/src/integration-profile/integration-profile.module.ts +5 -2
- package/src/integration-profile/integration-profile.secrets.ts +51 -0
- package/src/integration-profile/integration-profile.service.ts +335 -54
- package/src/integration-profile/integration-profile.utils.ts +56 -0
- package/src/language/en.json +5 -1
- package/src/language/pt.json +5 -1
- package/src/mail/mail.controller.ts +5 -29
- package/src/mail/mail.module.ts +5 -5
- package/src/mail/mail.service.ts +37 -13
- package/src/mcp-chat/mcp-chat.service.ts +5 -5
- package/src/oauth/oauth-callback-coordinator.service.spec.ts +159 -0
- package/src/oauth/providers/facebook.provider.ts +3 -3
- package/src/oauth/providers/github.provider.ts +3 -3
- package/src/oauth/providers/google.provider.ts +3 -3
- package/src/oauth/providers/microsoft-entra-id.provider.ts +3 -3
- package/src/oauth/providers/microsoft.provider.ts +3 -3
- package/src/pdf/pdf.service.ts +19 -6
- package/src/profile/profile.controller.ts +21 -14
- package/src/profile/profile.service.ts +23 -20
- package/src/security/security.service.spec.ts +222 -0
- package/src/setting/setting.service.ts +15 -2
- package/src/token/token.service.ts +2 -1
- package/src/user/user.controller.ts +26 -16
- package/src/user/user.service.ts +25 -0
- package/src/utils/webauthn-origin.ts +38 -0
- package/src/webhook-integration/webhook-integration.controller.ts +23 -0
- package/src/webhook-integration/webhook-integration.module.ts +3 -3
- package/src/webhook-integration/webhook-integration.service.ts +115 -5
- package/dist/webhook-integration/webhook-command.registry.d.ts +0 -22
- package/dist/webhook-integration/webhook-command.registry.d.ts.map +0 -1
- package/dist/webhook-integration/webhook-command.registry.js.map +0 -1
- package/src/menu/menu.service.spec.ts +0 -247
- package/src/route/route.service.spec.ts +0 -300
- package/src/screen/screen.service.spec.ts +0 -298
- package/src/user/user.service.spec.ts +0 -294
|
@@ -68,7 +68,7 @@ import {
|
|
|
68
68
|
IconX,
|
|
69
69
|
} from '@tabler/icons-react';
|
|
70
70
|
import { useTranslations } from 'next-intl';
|
|
71
|
-
import { useState } from 'react';
|
|
71
|
+
import { useMemo, useState } from 'react';
|
|
72
72
|
import { toast } from 'sonner';
|
|
73
73
|
|
|
74
74
|
interface Component {
|
|
@@ -111,6 +111,8 @@ interface RoleOption {
|
|
|
111
111
|
name?: string;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
const DEFAULT_PAGE_SIZES = [6, 12, 24, 48, 96] as const;
|
|
115
|
+
|
|
114
116
|
export function ComponentsTab() {
|
|
115
117
|
const t = useTranslations('core.DashboardManagement');
|
|
116
118
|
const { request, locales, currentLocaleCode } = useApp();
|
|
@@ -133,10 +135,40 @@ export function ComponentsTab() {
|
|
|
133
135
|
useState<Component | null>(null);
|
|
134
136
|
const [selectedRoleIds, setSelectedRoleIds] = useState<number[]>([]);
|
|
135
137
|
const [page, setPage] = useState(1);
|
|
138
|
+
const { data: generalSettings } = useQuery<{
|
|
139
|
+
data: Array<{ slug: string; value: string }>;
|
|
140
|
+
}>({
|
|
141
|
+
queryKey: ['setting-group-general'],
|
|
142
|
+
queryFn: async () => {
|
|
143
|
+
const response = await request<{
|
|
144
|
+
data: Array<{ slug: string; value: string }>;
|
|
145
|
+
}>({ url: '/setting/group/general', method: 'GET' });
|
|
146
|
+
return response.data;
|
|
147
|
+
},
|
|
148
|
+
staleTime: 5 * 60 * 1000,
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const pageSizeOptions = useMemo(() => {
|
|
152
|
+
const setting = generalSettings?.data?.find(
|
|
153
|
+
(s) => s.slug === 'pagination-page-sizes'
|
|
154
|
+
);
|
|
155
|
+
if (!setting?.value) return DEFAULT_PAGE_SIZES;
|
|
156
|
+
try {
|
|
157
|
+
const parsed = JSON.parse(setting.value) as string[];
|
|
158
|
+
const sizes = parsed
|
|
159
|
+
.map(Number)
|
|
160
|
+
.filter((n) => !isNaN(n) && n > 0)
|
|
161
|
+
.sort((a, b) => a - b);
|
|
162
|
+
return sizes.length > 0 ? sizes : DEFAULT_PAGE_SIZES;
|
|
163
|
+
} catch {
|
|
164
|
+
return DEFAULT_PAGE_SIZES;
|
|
165
|
+
}
|
|
166
|
+
}, [generalSettings]);
|
|
167
|
+
|
|
136
168
|
const [pageSize, setPageSize] = usePersistedPageSize({
|
|
137
169
|
storageKey: 'pagination:global:pageSize',
|
|
138
|
-
defaultValue:
|
|
139
|
-
allowedValues:
|
|
170
|
+
defaultValue: 12,
|
|
171
|
+
allowedValues: pageSizeOptions,
|
|
140
172
|
});
|
|
141
173
|
const [searchQuery, setSearchQuery] = useState('');
|
|
142
174
|
const [roleSearchQuery, setRoleSearchQuery] = useState('');
|
|
@@ -909,7 +941,7 @@ export function ComponentsTab() {
|
|
|
909
941
|
<SelectValue />
|
|
910
942
|
</SelectTrigger>
|
|
911
943
|
<SelectContent>
|
|
912
|
-
{
|
|
944
|
+
{pageSizeOptions.map((size) => (
|
|
913
945
|
<SelectItem key={size} value={String(size)}>
|
|
914
946
|
{size}
|
|
915
947
|
</SelectItem>
|
|
@@ -133,6 +133,8 @@ type RequestError = {
|
|
|
133
133
|
};
|
|
134
134
|
};
|
|
135
135
|
|
|
136
|
+
const DEFAULT_PAGE_SIZES = [6, 12, 24, 48, 96] as const;
|
|
137
|
+
|
|
136
138
|
export function DashboardRolesTab() {
|
|
137
139
|
const t = useTranslations('core.DashboardManagement');
|
|
138
140
|
const { request, currentLocaleCode, getSettingValue } = useApp();
|
|
@@ -146,10 +148,40 @@ export function DashboardRolesTab() {
|
|
|
146
148
|
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
|
147
149
|
const [roleToDelete, setRoleToDelete] = useState<number | null>(null);
|
|
148
150
|
const [page, setPage] = useState(1);
|
|
151
|
+
const { data: generalSettings } = useQuery<{
|
|
152
|
+
data: Array<{ slug: string; value: string }>;
|
|
153
|
+
}>({
|
|
154
|
+
queryKey: ['setting-group-general'],
|
|
155
|
+
queryFn: async () => {
|
|
156
|
+
const response = await request<{
|
|
157
|
+
data: Array<{ slug: string; value: string }>;
|
|
158
|
+
}>({ url: '/setting/group/general', method: 'GET' });
|
|
159
|
+
return response.data;
|
|
160
|
+
},
|
|
161
|
+
staleTime: 5 * 60 * 1000,
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
const pageSizeOptions = useMemo(() => {
|
|
165
|
+
const setting = generalSettings?.data?.find(
|
|
166
|
+
(s) => s.slug === 'pagination-page-sizes'
|
|
167
|
+
);
|
|
168
|
+
if (!setting?.value) return DEFAULT_PAGE_SIZES;
|
|
169
|
+
try {
|
|
170
|
+
const parsed = JSON.parse(setting.value) as string[];
|
|
171
|
+
const sizes = parsed
|
|
172
|
+
.map(Number)
|
|
173
|
+
.filter((n) => !isNaN(n) && n > 0)
|
|
174
|
+
.sort((a, b) => a - b);
|
|
175
|
+
return sizes.length > 0 ? sizes : DEFAULT_PAGE_SIZES;
|
|
176
|
+
} catch {
|
|
177
|
+
return DEFAULT_PAGE_SIZES;
|
|
178
|
+
}
|
|
179
|
+
}, [generalSettings]);
|
|
180
|
+
|
|
149
181
|
const [pageSize, setPageSize] = usePersistedPageSize({
|
|
150
182
|
storageKey: 'pagination:global:pageSize',
|
|
151
|
-
defaultValue:
|
|
152
|
-
allowedValues:
|
|
183
|
+
defaultValue: 12,
|
|
184
|
+
allowedValues: pageSizeOptions,
|
|
153
185
|
});
|
|
154
186
|
const searchQuery = '';
|
|
155
187
|
const debouncedSearch = useDebounce(searchQuery, 500);
|
|
@@ -539,7 +571,7 @@ export function DashboardRolesTab() {
|
|
|
539
571
|
<SelectValue />
|
|
540
572
|
</SelectTrigger>
|
|
541
573
|
<SelectContent>
|
|
542
|
-
{
|
|
574
|
+
{pageSizeOptions.map((size) => (
|
|
543
575
|
<SelectItem key={size} value={String(size)}>
|
|
544
576
|
{size}
|
|
545
577
|
</SelectItem>
|
|
@@ -67,7 +67,7 @@ import {
|
|
|
67
67
|
IconX,
|
|
68
68
|
} from '@tabler/icons-react';
|
|
69
69
|
import { useTranslations } from 'next-intl';
|
|
70
|
-
import { useState } from 'react';
|
|
70
|
+
import { useMemo, useState } from 'react';
|
|
71
71
|
import { toast } from 'sonner';
|
|
72
72
|
|
|
73
73
|
interface Dashboard {
|
|
@@ -98,6 +98,8 @@ interface RoleOption {
|
|
|
98
98
|
name?: string;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
const DEFAULT_PAGE_SIZES = [6, 12, 24, 48, 96] as const;
|
|
102
|
+
|
|
101
103
|
export function DashboardsTab() {
|
|
102
104
|
const t = useTranslations('core.DashboardManagement');
|
|
103
105
|
const { request, locales, currentLocaleCode } = useApp();
|
|
@@ -120,10 +122,40 @@ export function DashboardsTab() {
|
|
|
120
122
|
useState<Dashboard | null>(null);
|
|
121
123
|
const [selectedRoleIds, setSelectedRoleIds] = useState<number[]>([]);
|
|
122
124
|
const [page, setPage] = useState(1);
|
|
125
|
+
const { data: generalSettings } = useQuery<{
|
|
126
|
+
data: Array<{ slug: string; value: string }>;
|
|
127
|
+
}>({
|
|
128
|
+
queryKey: ['setting-group-general'],
|
|
129
|
+
queryFn: async () => {
|
|
130
|
+
const response = await request<{
|
|
131
|
+
data: Array<{ slug: string; value: string }>;
|
|
132
|
+
}>({ url: '/setting/group/general', method: 'GET' });
|
|
133
|
+
return response.data;
|
|
134
|
+
},
|
|
135
|
+
staleTime: 5 * 60 * 1000,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const pageSizeOptions = useMemo(() => {
|
|
139
|
+
const setting = generalSettings?.data?.find(
|
|
140
|
+
(s) => s.slug === 'pagination-page-sizes'
|
|
141
|
+
);
|
|
142
|
+
if (!setting?.value) return DEFAULT_PAGE_SIZES;
|
|
143
|
+
try {
|
|
144
|
+
const parsed = JSON.parse(setting.value) as string[];
|
|
145
|
+
const sizes = parsed
|
|
146
|
+
.map(Number)
|
|
147
|
+
.filter((n) => !isNaN(n) && n > 0)
|
|
148
|
+
.sort((a, b) => a - b);
|
|
149
|
+
return sizes.length > 0 ? sizes : DEFAULT_PAGE_SIZES;
|
|
150
|
+
} catch {
|
|
151
|
+
return DEFAULT_PAGE_SIZES;
|
|
152
|
+
}
|
|
153
|
+
}, [generalSettings]);
|
|
154
|
+
|
|
123
155
|
const [pageSize, setPageSize] = usePersistedPageSize({
|
|
124
156
|
storageKey: 'pagination:global:pageSize',
|
|
125
|
-
defaultValue:
|
|
126
|
-
allowedValues:
|
|
157
|
+
defaultValue: 12,
|
|
158
|
+
allowedValues: pageSizeOptions,
|
|
127
159
|
});
|
|
128
160
|
const [searchQuery, setSearchQuery] = useState('');
|
|
129
161
|
const [roleSearchQuery, setRoleSearchQuery] = useState('');
|
|
@@ -653,7 +685,7 @@ export function DashboardsTab() {
|
|
|
653
685
|
<SelectValue />
|
|
654
686
|
</SelectTrigger>
|
|
655
687
|
<SelectContent>
|
|
656
|
-
{
|
|
688
|
+
{pageSizeOptions.map((size) => (
|
|
657
689
|
<SelectItem key={size} value={String(size)}>
|
|
658
690
|
{size}
|
|
659
691
|
</SelectItem>
|
|
@@ -127,6 +127,8 @@ type ComponentListResponse = {
|
|
|
127
127
|
data?: Component[];
|
|
128
128
|
};
|
|
129
129
|
|
|
130
|
+
const DEFAULT_PAGE_SIZES = [6, 12, 24, 48, 96] as const;
|
|
131
|
+
|
|
130
132
|
export function ItemsTab() {
|
|
131
133
|
const t = useTranslations('core.DashboardManagement');
|
|
132
134
|
const { request, currentLocaleCode, getSettingValue } = useApp();
|
|
@@ -144,10 +146,40 @@ export function ItemsTab() {
|
|
|
144
146
|
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
|
145
147
|
const [itemToDelete, setItemToDelete] = useState<number | null>(null);
|
|
146
148
|
const [page, setPage] = useState(1);
|
|
149
|
+
const { data: generalSettings } = useQuery<{
|
|
150
|
+
data: Array<{ slug: string; value: string }>;
|
|
151
|
+
}>({
|
|
152
|
+
queryKey: ['setting-group-general'],
|
|
153
|
+
queryFn: async () => {
|
|
154
|
+
const response = await request<{
|
|
155
|
+
data: Array<{ slug: string; value: string }>;
|
|
156
|
+
}>({ url: '/setting/group/general', method: 'GET' });
|
|
157
|
+
return response.data;
|
|
158
|
+
},
|
|
159
|
+
staleTime: 5 * 60 * 1000,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const pageSizeOptions = useMemo(() => {
|
|
163
|
+
const setting = generalSettings?.data?.find(
|
|
164
|
+
(s) => s.slug === 'pagination-page-sizes'
|
|
165
|
+
);
|
|
166
|
+
if (!setting?.value) return DEFAULT_PAGE_SIZES;
|
|
167
|
+
try {
|
|
168
|
+
const parsed = JSON.parse(setting.value) as string[];
|
|
169
|
+
const sizes = parsed
|
|
170
|
+
.map(Number)
|
|
171
|
+
.filter((n) => !isNaN(n) && n > 0)
|
|
172
|
+
.sort((a, b) => a - b);
|
|
173
|
+
return sizes.length > 0 ? sizes : DEFAULT_PAGE_SIZES;
|
|
174
|
+
} catch {
|
|
175
|
+
return DEFAULT_PAGE_SIZES;
|
|
176
|
+
}
|
|
177
|
+
}, [generalSettings]);
|
|
178
|
+
|
|
147
179
|
const [pageSize, setPageSize] = usePersistedPageSize({
|
|
148
180
|
storageKey: 'pagination:global:pageSize',
|
|
149
|
-
defaultValue:
|
|
150
|
-
allowedValues:
|
|
181
|
+
defaultValue: 12,
|
|
182
|
+
allowedValues: pageSizeOptions,
|
|
151
183
|
});
|
|
152
184
|
const searchQuery = '';
|
|
153
185
|
const debouncedSearch = useDebounce(searchQuery, 500);
|
|
@@ -647,7 +679,7 @@ export function ItemsTab() {
|
|
|
647
679
|
<SelectValue />
|
|
648
680
|
</SelectTrigger>
|
|
649
681
|
<SelectContent>
|
|
650
|
-
{
|
|
682
|
+
{pageSizeOptions.map((size) => (
|
|
651
683
|
<SelectItem key={size} value={String(size)}>
|
|
652
684
|
{size}
|
|
653
685
|
</SelectItem>
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
AlertDialogHeader,
|
|
20
20
|
AlertDialogTitle,
|
|
21
21
|
} from '@/components/ui/alert-dialog';
|
|
22
|
+
import { DoNodePoolPicker } from '@/components/integration-profile/do-node-pool-picker';
|
|
22
23
|
import { Avatar, AvatarFallback } from '@/components/ui/avatar';
|
|
23
24
|
import { Badge } from '@/components/ui/badge';
|
|
24
25
|
import { Button } from '@/components/ui/button';
|
|
@@ -57,6 +58,7 @@ import {
|
|
|
57
58
|
TableHeader,
|
|
58
59
|
TableRow,
|
|
59
60
|
} from '@/components/ui/table';
|
|
61
|
+
import { TableBodySkeleton } from '@/components/ui/skeletons';
|
|
60
62
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
|
61
63
|
import { usePagination } from '@/hooks/use-pagination';
|
|
62
64
|
import { formatDate } from '@/lib/format-date';
|
|
@@ -166,6 +168,9 @@ type ImportValidationResponse = {
|
|
|
166
168
|
|
|
167
169
|
// ─── Provider field definitions ───────────────────────────────────────────────
|
|
168
170
|
|
|
171
|
+
/** Placeholder devolvido pelo backend no lugar de um segredo armazenado. */
|
|
172
|
+
const SECRET_MASK = '********';
|
|
173
|
+
|
|
169
174
|
const PROVIDER_FIELDS: Record<string, FieldDef[]> = {
|
|
170
175
|
smtp: [
|
|
171
176
|
{
|
|
@@ -653,6 +658,61 @@ const PROVIDER_FIELDS: Record<string, FieldDef[]> = {
|
|
|
653
658
|
placeholder: 'https://eu.altcha.org/api/v1',
|
|
654
659
|
},
|
|
655
660
|
],
|
|
661
|
+
// O node pool de vídeo NÃO é um campo de texto: é escolhido por nome via picker
|
|
662
|
+
// dedicado (carrega os pools da DO API com o token + cluster_id). Ver DoNodePoolPicker.
|
|
663
|
+
digitalocean: [
|
|
664
|
+
{
|
|
665
|
+
key: 'api_token',
|
|
666
|
+
labelKey: 'fieldApiToken',
|
|
667
|
+
type: 'password',
|
|
668
|
+
required: true,
|
|
669
|
+
colSpan: true,
|
|
670
|
+
},
|
|
671
|
+
{
|
|
672
|
+
key: 'cluster_id',
|
|
673
|
+
labelKey: 'fieldClusterId',
|
|
674
|
+
type: 'text',
|
|
675
|
+
required: true,
|
|
676
|
+
},
|
|
677
|
+
{
|
|
678
|
+
key: 'region',
|
|
679
|
+
labelKey: 'fieldRegion',
|
|
680
|
+
type: 'text',
|
|
681
|
+
required: true,
|
|
682
|
+
placeholder: 'nyc1',
|
|
683
|
+
},
|
|
684
|
+
],
|
|
685
|
+
kubernetes: [
|
|
686
|
+
{
|
|
687
|
+
key: 'api_server',
|
|
688
|
+
labelKey: 'fieldApiServer',
|
|
689
|
+
type: 'url',
|
|
690
|
+
required: true,
|
|
691
|
+
colSpan: true,
|
|
692
|
+
placeholder: 'https://10.0.0.1:6443',
|
|
693
|
+
},
|
|
694
|
+
{
|
|
695
|
+
key: 'token',
|
|
696
|
+
labelKey: 'fieldToken',
|
|
697
|
+
type: 'password',
|
|
698
|
+
required: true,
|
|
699
|
+
colSpan: true,
|
|
700
|
+
},
|
|
701
|
+
{
|
|
702
|
+
key: 'ca_cert',
|
|
703
|
+
labelKey: 'fieldCaCert',
|
|
704
|
+
type: 'password',
|
|
705
|
+
required: false,
|
|
706
|
+
colSpan: true,
|
|
707
|
+
},
|
|
708
|
+
{
|
|
709
|
+
key: 'namespace',
|
|
710
|
+
labelKey: 'fieldNamespace',
|
|
711
|
+
type: 'text',
|
|
712
|
+
required: false,
|
|
713
|
+
placeholder: 'hcode',
|
|
714
|
+
},
|
|
715
|
+
],
|
|
656
716
|
};
|
|
657
717
|
|
|
658
718
|
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
@@ -722,6 +782,7 @@ function TypeTab({
|
|
|
722
782
|
totalItems,
|
|
723
783
|
search,
|
|
724
784
|
setSearch,
|
|
785
|
+
isLoading,
|
|
725
786
|
} = usePagination({ url: `/integration-profile?typeId=${typeId}` });
|
|
726
787
|
|
|
727
788
|
useEffect(() => {
|
|
@@ -786,13 +847,7 @@ function TypeTab({
|
|
|
786
847
|
placeholder={t('searchPlaceholder')}
|
|
787
848
|
/>
|
|
788
849
|
|
|
789
|
-
{profiles.length
|
|
790
|
-
<EmptyState
|
|
791
|
-
icon={<PlugZap className="h-12 w-12" />}
|
|
792
|
-
title={t('noProfilesFound')}
|
|
793
|
-
description={t('noProfilesHint')}
|
|
794
|
-
/>
|
|
795
|
-
) : (
|
|
850
|
+
{isLoading || profiles.length > 0 ? (
|
|
796
851
|
<div className="rounded-md border">
|
|
797
852
|
<Table>
|
|
798
853
|
<TableHeader>
|
|
@@ -826,7 +881,10 @@ function TypeTab({
|
|
|
826
881
|
</TableRow>
|
|
827
882
|
</TableHeader>
|
|
828
883
|
<TableBody>
|
|
829
|
-
{
|
|
884
|
+
{isLoading ? (
|
|
885
|
+
<TableBodySkeleton rows={pageSize} columns={9} />
|
|
886
|
+
) : (
|
|
887
|
+
profiles.map((profile) => (
|
|
830
888
|
<TableRow
|
|
831
889
|
key={profile.id}
|
|
832
890
|
className="cursor-pointer"
|
|
@@ -907,10 +965,17 @@ function TypeTab({
|
|
|
907
965
|
</div>
|
|
908
966
|
</TableCell>
|
|
909
967
|
</TableRow>
|
|
910
|
-
|
|
968
|
+
))
|
|
969
|
+
)}
|
|
911
970
|
</TableBody>
|
|
912
971
|
</Table>
|
|
913
972
|
</div>
|
|
973
|
+
) : (
|
|
974
|
+
<EmptyState
|
|
975
|
+
icon={<PlugZap className="h-12 w-12" />}
|
|
976
|
+
title={t('noProfilesFound')}
|
|
977
|
+
description={t('noProfilesHint')}
|
|
978
|
+
/>
|
|
914
979
|
)}
|
|
915
980
|
|
|
916
981
|
<PaginationFooter
|
|
@@ -1075,6 +1140,10 @@ export default function IntegrationProfilesPage() {
|
|
|
1075
1140
|
{};
|
|
1076
1141
|
const providerSlug =
|
|
1077
1142
|
providers.find((p) => p.id === data.provider_id)?.slug ?? '';
|
|
1143
|
+
// Segredos chegam mascarados (********) do backend e PERMANECEM mascarados no
|
|
1144
|
+
// form: o campo fica desabilitado mostrando ********; o botão "trocar" limpa e
|
|
1145
|
+
// reabilita para digitar um novo valor. Em branco ou mascarado, o backend
|
|
1146
|
+
// preserva o segredo armazenado.
|
|
1078
1147
|
if (
|
|
1079
1148
|
providerSlug.endsWith('-oauth') &&
|
|
1080
1149
|
!Array.isArray(loadedConfig.callback_urls)
|
|
@@ -1844,6 +1913,15 @@ export default function IntegrationProfilesPage() {
|
|
|
1844
1913
|
|
|
1845
1914
|
const isPassword = field.type === 'password';
|
|
1846
1915
|
const isVisible = visibleFields[field.key] ?? false;
|
|
1916
|
+
const currentValue = String(
|
|
1917
|
+
formData.config[field.key] ?? ''
|
|
1918
|
+
);
|
|
1919
|
+
// Segredo já armazenado: campo desabilitado mostrando ******** +
|
|
1920
|
+
// botão "trocar" que limpa o valor e reabilita para digitar um novo.
|
|
1921
|
+
const isLockedSecret =
|
|
1922
|
+
isPassword &&
|
|
1923
|
+
Boolean(editingProfile) &&
|
|
1924
|
+
currentValue === SECRET_MASK;
|
|
1847
1925
|
|
|
1848
1926
|
return (
|
|
1849
1927
|
<div
|
|
@@ -1865,49 +1943,76 @@ export default function IntegrationProfilesPage() {
|
|
|
1865
1943
|
id={`ip-cfg-${field.key}`}
|
|
1866
1944
|
type={
|
|
1867
1945
|
isPassword
|
|
1868
|
-
? isVisible
|
|
1946
|
+
? isVisible && !isLockedSecret
|
|
1869
1947
|
? 'text'
|
|
1870
1948
|
: 'password'
|
|
1871
1949
|
: field.type === 'number'
|
|
1872
1950
|
? 'number'
|
|
1873
1951
|
: 'text'
|
|
1874
1952
|
}
|
|
1875
|
-
value={
|
|
1953
|
+
value={currentValue}
|
|
1876
1954
|
onChange={(e) =>
|
|
1877
1955
|
updateConfig(field.key, e.target.value)
|
|
1878
1956
|
}
|
|
1879
|
-
placeholder={
|
|
1880
|
-
|
|
1957
|
+
placeholder={
|
|
1958
|
+
isPassword && editingProfile
|
|
1959
|
+
? t('leaveBlankToKeep')
|
|
1960
|
+
: field.placeholder
|
|
1961
|
+
}
|
|
1962
|
+
required={
|
|
1963
|
+
field.required &&
|
|
1964
|
+
!(isPassword && Boolean(editingProfile))
|
|
1965
|
+
}
|
|
1881
1966
|
autoComplete={
|
|
1882
1967
|
isPassword ? 'new-password' : undefined
|
|
1883
1968
|
}
|
|
1884
1969
|
className={isPassword ? 'pr-10' : undefined}
|
|
1970
|
+
disabled={isLockedSecret}
|
|
1885
1971
|
/>
|
|
1886
|
-
{isPassword &&
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1972
|
+
{isPassword &&
|
|
1973
|
+
(isLockedSecret ? (
|
|
1974
|
+
<Button
|
|
1975
|
+
type="button"
|
|
1976
|
+
variant="ghost"
|
|
1977
|
+
size="icon"
|
|
1978
|
+
className="absolute right-1 top-1/2 h-8 w-8 -translate-y-1/2"
|
|
1979
|
+
onClick={() => {
|
|
1980
|
+
updateConfig(field.key, '');
|
|
1981
|
+
setVisibleFields((p) => ({
|
|
1982
|
+
...p,
|
|
1983
|
+
[field.key]: true,
|
|
1984
|
+
}));
|
|
1985
|
+
}}
|
|
1986
|
+
aria-label={t('changeSecret')}
|
|
1987
|
+
title={t('changeSecret')}
|
|
1988
|
+
>
|
|
1989
|
+
<Pencil className="h-4 w-4" />
|
|
1990
|
+
</Button>
|
|
1991
|
+
) : (
|
|
1992
|
+
<Button
|
|
1993
|
+
type="button"
|
|
1994
|
+
variant="ghost"
|
|
1995
|
+
size="icon"
|
|
1996
|
+
className="absolute right-1 top-1/2 h-8 w-8 -translate-y-1/2"
|
|
1997
|
+
onClick={() =>
|
|
1998
|
+
setVisibleFields((p) => ({
|
|
1999
|
+
...p,
|
|
2000
|
+
[field.key]: !p[field.key],
|
|
2001
|
+
}))
|
|
2002
|
+
}
|
|
2003
|
+
aria-label={
|
|
2004
|
+
isVisible
|
|
2005
|
+
? t('hidePassword')
|
|
2006
|
+
: t('showPassword')
|
|
2007
|
+
}
|
|
2008
|
+
>
|
|
2009
|
+
{isVisible ? (
|
|
2010
|
+
<EyeOff className="h-4 w-4" />
|
|
2011
|
+
) : (
|
|
2012
|
+
<Eye className="h-4 w-4" />
|
|
2013
|
+
)}
|
|
2014
|
+
</Button>
|
|
2015
|
+
))}
|
|
1911
2016
|
</div>
|
|
1912
2017
|
</div>
|
|
1913
2018
|
);
|
|
@@ -1916,6 +2021,19 @@ export default function IntegrationProfilesPage() {
|
|
|
1916
2021
|
</div>
|
|
1917
2022
|
)}
|
|
1918
2023
|
|
|
2024
|
+
{/* ── DigitalOcean: node pool de vídeo (escolha por nome via API) ── */}
|
|
2025
|
+
{selectedProvider?.slug === 'digitalocean' && (
|
|
2026
|
+
<DoNodePoolPicker
|
|
2027
|
+
apiToken={String(formData.config.api_token ?? '')}
|
|
2028
|
+
clusterId={String(formData.config.cluster_id ?? '')}
|
|
2029
|
+
value={String(formData.config.video_node_pool_name ?? '')}
|
|
2030
|
+
profileId={editingProfile?.id}
|
|
2031
|
+
onChange={(name) =>
|
|
2032
|
+
updateConfig('video_node_pool_name', name)
|
|
2033
|
+
}
|
|
2034
|
+
/>
|
|
2035
|
+
)}
|
|
2036
|
+
|
|
1919
2037
|
{/* ── Callback URLs (OAuth only) ── */}
|
|
1920
2038
|
{isOAuthProvider && (
|
|
1921
2039
|
<div className="rounded-lg border">
|