@nsxbet/admin-sdk 0.7.1 → 0.8.0
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/CHECKLIST.md +48 -13
- package/README.md +24 -74
- package/dist/auth/client/bff.d.ts +38 -0
- package/dist/auth/client/bff.js +270 -0
- package/dist/auth/client/in-memory.d.ts +1 -1
- package/dist/auth/client/in-memory.js +2 -2
- package/dist/auth/client/index.d.ts +1 -1
- package/dist/auth/client/index.js +2 -2
- package/dist/auth/client/interface.d.ts +4 -4
- package/dist/auth/client/interface.js +1 -1
- package/dist/auth/components/LoginPage.d.ts +8 -0
- package/dist/auth/components/LoginPage.js +32 -0
- package/dist/auth/components/UserSelector.js +2 -2
- package/dist/auth/components/index.d.ts +2 -0
- package/dist/auth/components/index.js +1 -0
- package/dist/auth/index.d.ts +3 -2
- package/dist/auth/index.js +2 -2
- package/dist/components/AuthProvider.d.ts +3 -3
- package/dist/components/AuthProvider.js +25 -10
- package/dist/env.d.ts +17 -0
- package/dist/env.js +50 -0
- package/dist/hooks/useAuth.d.ts +3 -3
- package/dist/hooks/useAuth.js +1 -1
- package/dist/hooks/useFetch.js +6 -1
- package/dist/hooks/useI18n.js +2 -2
- package/dist/i18n/config.d.ts +2 -1
- package/dist/i18n/config.js +4 -3
- package/dist/i18n/index.d.ts +1 -1
- package/dist/i18n/index.js +1 -1
- package/dist/i18n/locales/en-US.json +7 -0
- package/dist/i18n/locales/es.json +7 -0
- package/dist/i18n/locales/pt-BR.json +7 -0
- package/dist/i18n/locales/ro.json +7 -0
- package/dist/index.d.ts +6 -5
- package/dist/index.js +5 -2
- package/dist/registry/client/http.js +6 -1
- package/dist/registry/client/in-memory.js +20 -5
- package/dist/registry/types/manifest.d.ts +5 -0
- package/dist/registry/types/manifest.js +4 -1
- package/dist/registry/types/module.d.ts +6 -2
- package/dist/sdk-version.d.ts +5 -0
- package/dist/sdk-version.js +5 -0
- package/dist/shell/AdminShell.d.ts +12 -9
- package/dist/shell/AdminShell.js +56 -70
- package/dist/shell/components/ModuleOverview.js +1 -5
- package/dist/shell/components/RegistryPage.js +1 -1
- package/dist/shell/components/TopBar.js +2 -2
- package/dist/shell/index.d.ts +1 -1
- package/dist/shell/polling-config.d.ts +4 -3
- package/dist/shell/polling-config.js +11 -9
- package/dist/shell/types.d.ts +3 -1
- package/dist/types/platform.d.ts +2 -11
- package/dist/vite/config.d.ts +4 -9
- package/dist/vite/config.js +85 -27
- package/dist/vite/index.d.ts +1 -1
- package/dist/vite/index.js +1 -1
- package/dist/vite/plugins.js +6 -1
- package/package.json +11 -5
- package/scripts/write-sdk-version.mjs +21 -0
- package/dist/auth/client/keycloak.d.ts +0 -18
- package/dist/auth/client/keycloak.js +0 -129
- package/dist/shell/BackofficeShell.d.ts +0 -37
- package/dist/shell/BackofficeShell.js +0 -339
- package/dist/types/keycloak.d.ts +0 -25
- package/dist/types/keycloak.js +0 -1
|
@@ -5,6 +5,17 @@
|
|
|
5
5
|
* Used for local development and testing without a backend.
|
|
6
6
|
*/
|
|
7
7
|
import { parseManifest } from '../types';
|
|
8
|
+
import { SDK_PACKAGE_VERSION } from '../../sdk-version.js';
|
|
9
|
+
function parseManifestWithSdkDefault(value) {
|
|
10
|
+
const raw = value;
|
|
11
|
+
const merged = {
|
|
12
|
+
...raw,
|
|
13
|
+
sdkVersion: typeof raw.sdkVersion === 'string' && raw.sdkVersion.trim() !== ''
|
|
14
|
+
? raw.sdkVersion
|
|
15
|
+
: SDK_PACKAGE_VERSION,
|
|
16
|
+
};
|
|
17
|
+
return parseManifest(merged);
|
|
18
|
+
}
|
|
8
19
|
const DEFAULT_STORAGE_KEY = '@nsxbet/registry';
|
|
9
20
|
/**
|
|
10
21
|
* Create an in-memory registry client
|
|
@@ -47,12 +58,13 @@ export function createInMemoryRegistryClient(options = {}) {
|
|
|
47
58
|
if (options.seed && data.modules.length === 0) {
|
|
48
59
|
const now = new Date().toISOString();
|
|
49
60
|
for (const manifest of options.seed) {
|
|
50
|
-
const validated =
|
|
61
|
+
const validated = parseManifestWithSdkDefault(manifest);
|
|
51
62
|
data.modules.push({
|
|
52
63
|
id: ++data.autoIncrement,
|
|
53
64
|
moduleId: validated.id,
|
|
54
65
|
baseUrl: '',
|
|
55
66
|
manifest: validated,
|
|
67
|
+
sdkVersion: validated.sdkVersion,
|
|
56
68
|
navigationOrder: validated.navigationOrder ?? data.autoIncrement * 10,
|
|
57
69
|
enabled: true,
|
|
58
70
|
registeredAt: now,
|
|
@@ -66,12 +78,13 @@ export function createInMemoryRegistryClient(options = {}) {
|
|
|
66
78
|
const now = new Date().toISOString();
|
|
67
79
|
for (const moduleConfig of options.initialModules) {
|
|
68
80
|
const { baseUrl, ...manifestData } = moduleConfig;
|
|
69
|
-
const validated =
|
|
81
|
+
const validated = parseManifestWithSdkDefault(manifestData);
|
|
70
82
|
data.modules.push({
|
|
71
83
|
id: ++data.autoIncrement,
|
|
72
84
|
moduleId: validated.id,
|
|
73
85
|
baseUrl,
|
|
74
86
|
manifest: validated,
|
|
87
|
+
sdkVersion: validated.sdkVersion,
|
|
75
88
|
navigationOrder: validated.navigationOrder ?? data.autoIncrement * 10,
|
|
76
89
|
enabled: true,
|
|
77
90
|
registeredAt: now,
|
|
@@ -92,7 +105,7 @@ export function createInMemoryRegistryClient(options = {}) {
|
|
|
92
105
|
result = result.filter((m) => m.manifest.category === filters.category);
|
|
93
106
|
}
|
|
94
107
|
if (filters?.status) {
|
|
95
|
-
result = result.filter(() => filters.status === 'active');
|
|
108
|
+
result = result.filter((m) => filters.status === 'active' ? m.enabled : !m.enabled);
|
|
96
109
|
}
|
|
97
110
|
// Sort by navigationOrder
|
|
98
111
|
result.sort((a, b) => a.navigationOrder - b.navigationOrder);
|
|
@@ -119,13 +132,13 @@ export function createInMemoryRegistryClient(options = {}) {
|
|
|
119
132
|
const manifestData = await response.json();
|
|
120
133
|
// Extract manifest without entry (entry is fetched by DynamicModule at runtime)
|
|
121
134
|
const { entry: _entry, ...manifest } = manifestData;
|
|
122
|
-
const validated =
|
|
135
|
+
const validated = parseManifestWithSdkDefault(manifest);
|
|
123
136
|
return this.registerFromManifest(validated, baseUrl);
|
|
124
137
|
},
|
|
125
138
|
async registerFromManifest(manifest, baseUrl = '') {
|
|
126
139
|
data = loadStorage(storageKey);
|
|
127
140
|
// Validate manifest
|
|
128
|
-
const validated =
|
|
141
|
+
const validated = parseManifestWithSdkDefault(manifest);
|
|
129
142
|
// Check for duplicate moduleId
|
|
130
143
|
if (data.modules.some((m) => m.moduleId === validated.id)) {
|
|
131
144
|
throw new Error(`Module with id ${validated.id} already exists`);
|
|
@@ -136,6 +149,7 @@ export function createInMemoryRegistryClient(options = {}) {
|
|
|
136
149
|
moduleId: validated.id,
|
|
137
150
|
baseUrl,
|
|
138
151
|
manifest: validated,
|
|
152
|
+
sdkVersion: validated.sdkVersion,
|
|
139
153
|
navigationOrder: validated.navigationOrder ?? data.autoIncrement * 10,
|
|
140
154
|
enabled: true,
|
|
141
155
|
registeredAt: now,
|
|
@@ -265,6 +279,7 @@ export function createInMemoryRegistryClient(options = {}) {
|
|
|
265
279
|
supportChannel: m.manifest.owners?.supportChannel ?? '#general',
|
|
266
280
|
},
|
|
267
281
|
status: 'active',
|
|
282
|
+
sdkVersion: m.manifest.sdkVersion ?? SDK_PACKAGE_VERSION,
|
|
268
283
|
navigationOrder: m.navigationOrder,
|
|
269
284
|
icon: m.manifest.icon,
|
|
270
285
|
navigation: m.manifest.navigation ? {
|
|
@@ -82,6 +82,11 @@ export interface AdminModuleManifest {
|
|
|
82
82
|
permissions?: ModulePermissions;
|
|
83
83
|
/** Module ownership info */
|
|
84
84
|
owners?: ModuleOwners;
|
|
85
|
+
/**
|
|
86
|
+
* Semver of @nsxbet/admin-sdk the module was built against.
|
|
87
|
+
* Injected at build time by `defineModuleConfig` — omit in source `admin.module.json`.
|
|
88
|
+
*/
|
|
89
|
+
sdkVersion?: string;
|
|
85
90
|
}
|
|
86
91
|
/**
|
|
87
92
|
* Validates that an object is a valid AdminModuleManifest
|
|
@@ -36,6 +36,9 @@ export function validateManifest(value) {
|
|
|
36
36
|
if (!isLocalizedField(obj.description)) {
|
|
37
37
|
return false;
|
|
38
38
|
}
|
|
39
|
+
if (typeof obj.sdkVersion !== "string" || obj.sdkVersion.trim() === "") {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
39
42
|
// Optional field validations
|
|
40
43
|
if (obj.category !== undefined && typeof obj.category !== 'string') {
|
|
41
44
|
return false;
|
|
@@ -74,7 +77,7 @@ export function validateManifest(value) {
|
|
|
74
77
|
*/
|
|
75
78
|
export function parseManifest(value) {
|
|
76
79
|
if (!validateManifest(value)) {
|
|
77
|
-
throw new Error(
|
|
80
|
+
throw new Error("Invalid manifest: must include id, title (all locales), description (all locales), routeBase (starting with /), and sdkVersion (non-empty string, injected at build time by defineModuleConfig). Title and description must include en-US, pt-BR, es, ro.");
|
|
78
81
|
}
|
|
79
82
|
return value;
|
|
80
83
|
}
|
|
@@ -18,6 +18,8 @@ export interface RegisteredModule {
|
|
|
18
18
|
version?: string;
|
|
19
19
|
/** Full parsed module.manifest.json (without entry - fetched at runtime) */
|
|
20
20
|
manifest: AdminModuleManifest;
|
|
21
|
+
/** Semver of @nsxbet/admin-sdk (DB column; mirrors manifest.sdkVersion) */
|
|
22
|
+
sdkVersion?: string;
|
|
21
23
|
/** Admin-controlled navigation order */
|
|
22
24
|
navigationOrder: number;
|
|
23
25
|
/** Whether the module is enabled */
|
|
@@ -59,7 +61,7 @@ export interface ModuleFilters {
|
|
|
59
61
|
/** Filter by category */
|
|
60
62
|
category?: string;
|
|
61
63
|
/** Filter by status */
|
|
62
|
-
status?: 'active' | '
|
|
64
|
+
status?: 'active' | 'disabled';
|
|
63
65
|
}
|
|
64
66
|
/**
|
|
65
67
|
* Catalog structure for shell consumption (denormalized)
|
|
@@ -96,7 +98,9 @@ export interface CatalogModule {
|
|
|
96
98
|
team: string;
|
|
97
99
|
supportChannel: string;
|
|
98
100
|
};
|
|
99
|
-
status: 'active' | '
|
|
101
|
+
status: 'active' | 'disabled';
|
|
102
|
+
/** Semver of @nsxbet/admin-sdk the module was built against */
|
|
103
|
+
sdkVersion: string;
|
|
100
104
|
navigationOrder?: number;
|
|
101
105
|
icon?: string;
|
|
102
106
|
navigation?: {
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { AuthClient } from "../auth/client/interface";
|
|
2
2
|
import type { AdminModuleManifest, RegistryClient } from "../registry";
|
|
3
|
+
/** Use admin-bff cookie / Okta auth (`createBffAuthClient`). `true` uses current origin. */
|
|
4
|
+
export type AdminShellBffConfig = true | {
|
|
5
|
+
baseUrl?: string;
|
|
6
|
+
};
|
|
3
7
|
export interface AdminShellProps {
|
|
4
8
|
/**
|
|
5
9
|
* Module manifests to load (used for standalone mode when no registryClient)
|
|
@@ -12,17 +16,12 @@ export interface AdminShellProps {
|
|
|
12
16
|
*/
|
|
13
17
|
children?: React.ReactNode;
|
|
14
18
|
/**
|
|
15
|
-
*
|
|
16
|
-
* If not provided, uses in-memory (mock) authentication
|
|
19
|
+
* BFF / Okta cookie authentication (recommended for production).
|
|
17
20
|
*/
|
|
18
|
-
|
|
19
|
-
url: string;
|
|
20
|
-
realm: string;
|
|
21
|
-
clientId: string;
|
|
22
|
-
};
|
|
21
|
+
bff?: AdminShellBffConfig;
|
|
23
22
|
/**
|
|
24
23
|
* Auth client to use for authentication.
|
|
25
|
-
* If not provided, creates one
|
|
24
|
+
* If not provided, creates one from `bff` or in-memory (mock) authentication depending on environment.
|
|
26
25
|
*/
|
|
27
26
|
authClient?: AuthClient;
|
|
28
27
|
/** Registry client for fetching modules from API */
|
|
@@ -33,5 +32,9 @@ export interface AdminShellProps {
|
|
|
33
32
|
inMemoryRegistry?: boolean;
|
|
34
33
|
/** Environment (default: "local") */
|
|
35
34
|
environment?: string;
|
|
35
|
+
/** BFF base URL (passed from shell's env config). Only used when `bff` is `true`. */
|
|
36
|
+
bffBaseUrl?: string;
|
|
37
|
+
/** Registry polling interval in ms (passed from shell's env config). */
|
|
38
|
+
registryPollInterval?: number;
|
|
36
39
|
}
|
|
37
|
-
export declare function AdminShell({ modules: manifests, children,
|
|
40
|
+
export declare function AdminShell({ modules: manifests, children, bff, authClient: providedAuthClient, registryClient, apiUrl, inMemoryRegistry, environment, bffBaseUrl: bffBaseUrlProp, registryPollInterval: registryPollIntervalProp, }: AdminShellProps): import("react/jsx-runtime").JSX.Element;
|
package/dist/shell/AdminShell.js
CHANGED
|
@@ -15,7 +15,7 @@ import { RegistryPage } from "./components/RegistryPage";
|
|
|
15
15
|
import { HomePage } from "./components/HomePage";
|
|
16
16
|
import { AuthProvider, useAuthContext } from "../components/AuthProvider";
|
|
17
17
|
import { createInMemoryAuthClient, createMockUsersFromRoles, } from "../auth/client/in-memory";
|
|
18
|
-
import {
|
|
18
|
+
import { createBffAuthClient } from "../auth/client/bff";
|
|
19
19
|
import { createInMemoryRegistryClient } from "../registry/client/in-memory";
|
|
20
20
|
import { createCachedCatalog } from "../registry/cache/cached-catalog";
|
|
21
21
|
import { DevtoolsPanel } from "./components/DevtoolsPanel";
|
|
@@ -25,8 +25,9 @@ import { useRegistryPolling } from "../registry/useRegistryPolling";
|
|
|
25
25
|
import { DynamicModule } from "../router/DynamicModule";
|
|
26
26
|
import { SidebarProvider, SidebarInset } from "@nsxbet/admin-ui";
|
|
27
27
|
import { initTelemetry, track, trackError } from "./telemetry";
|
|
28
|
-
import { initI18n, saveLocale, isSupportedLocale, i18n } from "../i18n";
|
|
28
|
+
import { initI18n, saveLocale, isSupportedLocale, i18n, DEFAULT_LOCALE } from "../i18n";
|
|
29
29
|
import { resolvePollingInterval } from "./polling-config";
|
|
30
|
+
import { SDK_PACKAGE_VERSION } from "../sdk-version.js";
|
|
30
31
|
const TIMEZONE_STORAGE_KEY = "admin-timezone-mode";
|
|
31
32
|
function getStoredTimezoneMode() {
|
|
32
33
|
if (typeof window === "undefined")
|
|
@@ -62,6 +63,7 @@ function manifestToModule(manifest, baseUrl) {
|
|
|
62
63
|
supportChannel: manifest.owners?.supportChannel || "",
|
|
63
64
|
},
|
|
64
65
|
status: "active",
|
|
66
|
+
sdkVersion: manifest.sdkVersion ?? SDK_PACKAGE_VERSION,
|
|
65
67
|
navigationOrder: manifest.navigationOrder,
|
|
66
68
|
icon: manifest.icon,
|
|
67
69
|
navigation: manifest.navigation ? {
|
|
@@ -96,6 +98,7 @@ function catalogModuleToModule(m) {
|
|
|
96
98
|
permissions: m.permissions,
|
|
97
99
|
owners: m.owners,
|
|
98
100
|
status: m.status,
|
|
101
|
+
sdkVersion: m.sdkVersion,
|
|
99
102
|
navigationOrder: m.navigationOrder,
|
|
100
103
|
icon: m.icon,
|
|
101
104
|
navigation: m.navigation ? {
|
|
@@ -176,6 +179,9 @@ function ShellContent({ modules, children, environment, locale, onLocaleChange,
|
|
|
176
179
|
fetch: async (input, init) => {
|
|
177
180
|
const token = await auth.getAccessToken();
|
|
178
181
|
const headers = new Headers(init?.headers);
|
|
182
|
+
if (token === null) {
|
|
183
|
+
return fetch(input, { ...init, headers, credentials: "include" });
|
|
184
|
+
}
|
|
179
185
|
headers.set("Authorization", `Bearer ${token}`);
|
|
180
186
|
return fetch(input, { ...init, headers });
|
|
181
187
|
},
|
|
@@ -220,12 +226,12 @@ function ShellContent({ modules, children, environment, locale, onLocaleChange,
|
|
|
220
226
|
owners: module.owners,
|
|
221
227
|
} })) : (_jsxs("div", { className: "text-muted-foreground", children: ["Module ", module.id, " has no baseUrl configured"] })) }) }, module.id))), _jsx(Route, { path: "*", element: _jsx(MainContent, { modules: modules, moduleBreadcrumbs: moduleBreadcrumbs, children: isStandaloneMode ? children : null }) })] })] })] }), _jsx(CommandPalette, { open: commandPaletteOpen, onOpenChange: onCommandPaletteChange, catalog: catalog }), _jsx(DevtoolsPanel, { environment: environment, modules: modules, catalogVersion: catalog.version, catalogGeneratedAt: catalog.generatedAt, registryMode: registryClient ? "api" : "in-memory", cacheStatus: cacheStatus })] }));
|
|
222
228
|
}
|
|
223
|
-
export function AdminShell({ modules: manifests = [], children,
|
|
229
|
+
export function AdminShell({ modules: manifests = [], children, bff, authClient: providedAuthClient, registryClient, apiUrl, inMemoryRegistry = true, environment = "local", bffBaseUrl: bffBaseUrlProp, registryPollInterval: registryPollIntervalProp, }) {
|
|
224
230
|
const [commandPaletteOpen, setCommandPaletteOpen] = useState(false);
|
|
225
231
|
const [locale, setLocale] = useState(() => {
|
|
226
232
|
// Initialize i18n and get the current language
|
|
227
233
|
initI18n();
|
|
228
|
-
return i18n.language ||
|
|
234
|
+
return i18n.language || DEFAULT_LOCALE;
|
|
229
235
|
});
|
|
230
236
|
const [apiModules, setApiModules] = useState([]);
|
|
231
237
|
const [initialCatalogVersion, setInitialCatalogVersion] = useState("");
|
|
@@ -270,70 +276,47 @@ export function AdminShell({ modules: manifests = [], children, keycloak, authCl
|
|
|
270
276
|
if (providedAuthClient) {
|
|
271
277
|
return providedAuthClient;
|
|
272
278
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
// Check if we should use mock auth
|
|
285
|
-
const useMockAuth = typeof window !== 'undefined' &&
|
|
286
|
-
(mockAuthExplicit || noAuthConfig);
|
|
287
|
-
if (useMockAuth) {
|
|
288
|
-
if (isProd && mockAuthExplicit) {
|
|
289
|
-
console.warn('[AdminShell] Mock auth is active in production build (VITE_MOCK_AUTH=true). ' +
|
|
290
|
-
'Use real authentication (Keycloak) for production deployments.');
|
|
291
|
-
}
|
|
292
|
-
// Default mock users with explicit roles for tasks and users modules
|
|
293
|
-
const defaultMockUsers = createMockUsersFromRoles({
|
|
294
|
-
admin: [
|
|
295
|
-
'admin.tasks.view',
|
|
296
|
-
'admin.tasks.edit',
|
|
297
|
-
'admin.tasks.delete',
|
|
298
|
-
'admin.users.view',
|
|
299
|
-
'admin.users.edit',
|
|
300
|
-
'admin.users.delete',
|
|
301
|
-
'admin.platform.view',
|
|
302
|
-
'admin.platform.edit',
|
|
303
|
-
'admin.platform.delete',
|
|
304
|
-
],
|
|
305
|
-
editor: [
|
|
306
|
-
'admin.tasks.view',
|
|
307
|
-
'admin.tasks.edit',
|
|
308
|
-
'admin.users.view',
|
|
309
|
-
'admin.users.edit',
|
|
310
|
-
'admin.platform.view',
|
|
311
|
-
'admin.platform.edit',
|
|
312
|
-
],
|
|
313
|
-
viewer: ['admin.tasks.view', 'admin.users.view', 'admin.platform.view'],
|
|
314
|
-
noAccess: [],
|
|
315
|
-
});
|
|
316
|
-
return createInMemoryAuthClient({ users: defaultMockUsers, gatewayUrl: import.meta.env.VITE_ADMIN_GATEWAY_URL });
|
|
279
|
+
if (bff) {
|
|
280
|
+
const baseUrl = bff === true
|
|
281
|
+
? bffBaseUrlProp
|
|
282
|
+
? bffBaseUrlProp.replace(/\/$/, '')
|
|
283
|
+
: window.location.origin
|
|
284
|
+
: bff.baseUrl
|
|
285
|
+
? bff.baseUrl.replace(/\/$/, '')
|
|
286
|
+
: bffBaseUrlProp
|
|
287
|
+
? bffBaseUrlProp.replace(/\/$/, '')
|
|
288
|
+
: window.location.origin;
|
|
289
|
+
return createBffAuthClient({ bffBaseUrl: baseUrl });
|
|
317
290
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
291
|
+
const defaultMockUsers = createMockUsersFromRoles({
|
|
292
|
+
admin: [
|
|
293
|
+
'admin.tasks.view',
|
|
294
|
+
'admin.tasks.edit',
|
|
295
|
+
'admin.tasks.delete',
|
|
296
|
+
'admin.users.view',
|
|
297
|
+
'admin.users.edit',
|
|
298
|
+
'admin.users.delete',
|
|
299
|
+
'admin.platform.view',
|
|
300
|
+
'admin.platform.edit',
|
|
301
|
+
'admin.platform.delete',
|
|
302
|
+
],
|
|
303
|
+
editor: [
|
|
304
|
+
'admin.tasks.view',
|
|
305
|
+
'admin.tasks.edit',
|
|
306
|
+
'admin.users.view',
|
|
307
|
+
'admin.users.edit',
|
|
308
|
+
'admin.platform.view',
|
|
309
|
+
'admin.platform.edit',
|
|
310
|
+
],
|
|
311
|
+
viewer: ['admin.tasks.view', 'admin.users.view', 'admin.platform.view'],
|
|
312
|
+
noAccess: [],
|
|
321
313
|
});
|
|
322
|
-
|
|
314
|
+
return createInMemoryAuthClient({ users: defaultMockUsers });
|
|
315
|
+
}, [providedAuthClient, bff, bffBaseUrlProp]);
|
|
323
316
|
// Initialize telemetry
|
|
324
317
|
useEffect(() => {
|
|
325
318
|
initTelemetry(environment);
|
|
326
319
|
}, [environment]);
|
|
327
|
-
// Initialize Keycloak configuration (for legacy support)
|
|
328
|
-
useEffect(() => {
|
|
329
|
-
if (keycloak) {
|
|
330
|
-
window.__KEYCLOAK_CONFIG__ = {
|
|
331
|
-
url: keycloak.url,
|
|
332
|
-
realm: keycloak.realm,
|
|
333
|
-
clientId: keycloak.clientId,
|
|
334
|
-
};
|
|
335
|
-
}
|
|
336
|
-
}, [keycloak]);
|
|
337
320
|
// Initialize in-memory registry if enabled and no registryClient
|
|
338
321
|
useEffect(() => {
|
|
339
322
|
if (!registryClient && inMemoryRegistry && manifests.length > 0) {
|
|
@@ -385,8 +368,8 @@ export function AdminShell({ modules: manifests = [], children, keycloak, authCl
|
|
|
385
368
|
const pollingInterval = useMemo(() => {
|
|
386
369
|
if (!registryClient || !initialCatalogVersion)
|
|
387
370
|
return 0;
|
|
388
|
-
return resolvePollingInterval(environment);
|
|
389
|
-
}, [registryClient, initialCatalogVersion, environment]);
|
|
371
|
+
return resolvePollingInterval(environment, registryPollIntervalProp);
|
|
372
|
+
}, [registryClient, initialCatalogVersion, environment, registryPollIntervalProp]);
|
|
390
373
|
// Poll for catalog version changes
|
|
391
374
|
const { hasUpdates, dismiss: dismissUpdate } = useRegistryPolling({
|
|
392
375
|
registryClient: registryClient,
|
|
@@ -428,13 +411,16 @@ export function AdminShell({ modules: manifests = [], children, keycloak, authCl
|
|
|
428
411
|
setCacheStatus(cachedCatalogOps.getStatus());
|
|
429
412
|
}
|
|
430
413
|
}, [cachedCatalogOps]);
|
|
431
|
-
//
|
|
414
|
+
// Resolve shell content — loading, unavailable, or ready
|
|
415
|
+
let shellContent;
|
|
432
416
|
if (isLoading) {
|
|
433
|
-
|
|
417
|
+
shellContent = (_jsx("div", { className: "flex h-screen items-center justify-center", children: _jsx("div", { className: "text-muted-foreground", children: "Loading modules..." }) }));
|
|
418
|
+
}
|
|
419
|
+
else if (cacheStatus.state === "unavailable") {
|
|
420
|
+
shellContent = _jsx(RegistryUnavailable, { onRetry: handleRetry });
|
|
434
421
|
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
return (_jsx(BrowserRouter, { children: _jsx(I18nextProvider, { i18n: i18n, children: _jsx(ThemeProvider, { children: _jsx(RegistryUnavailable, { onRetry: handleRetry }) }) }) }));
|
|
422
|
+
else {
|
|
423
|
+
shellContent = (_jsx(ShellContent, { modules: modules, environment: environment, locale: locale, onLocaleChange: handleLocaleChange, onSearchClick: handleSearchClick, catalog: catalog, commandPaletteOpen: commandPaletteOpen, onCommandPaletteChange: setCommandPaletteOpen, apiUrl: apiUrl, registryClient: registryClient, isStandaloneMode: isStandaloneMode, cacheStatus: cacheStatus, onRetry: handleRetry, hasUpdates: hasUpdates, onDismissUpdate: dismissUpdate, localeCallbacksRef: localeCallbacksRef, timezoneMode: timezoneMode, onTimezoneToggle: handleTimezoneToggle, timezoneCallbacksRef: timezoneCallbacksRef, children: children }));
|
|
438
424
|
}
|
|
439
|
-
return (_jsx(BrowserRouter, { children: _jsx(I18nextProvider, { i18n: i18n, children: _jsx(ThemeProvider, { children: _jsx(AuthProvider, { authClient: authClient, children:
|
|
425
|
+
return (_jsx(BrowserRouter, { children: _jsx(I18nextProvider, { i18n: i18n, children: _jsx(ThemeProvider, { children: _jsx(AuthProvider, { authClient: authClient, children: shellContent }) }) }) }));
|
|
440
426
|
}
|
|
@@ -13,11 +13,7 @@ export function ModuleOverview({ modules }) {
|
|
|
13
13
|
if (!module) {
|
|
14
14
|
return (_jsx("div", { className: "p-6 max-w-3xl mx-auto space-y-6", children: _jsx(Card, { className: "border-destructive", children: _jsx(CardContent, { className: "pt-6", children: _jsxs("div", { className: "flex items-center gap-3 text-destructive", children: [_jsx(Icon, { name: "alert-circle", className: "h-6 w-6" }), _jsxs("div", { children: [_jsx("h1", { className: "text-xl font-bold", children: t('errors.moduleNotFound') }), _jsx("p", { className: "text-muted-foreground mt-1", children: t('errors.moduleNotFoundDescription', { moduleId }) })] })] }) }) }) }));
|
|
15
15
|
}
|
|
16
|
-
const statusVariant = module.status === "active"
|
|
17
|
-
? "success"
|
|
18
|
-
: module.status === "deprecated"
|
|
19
|
-
? "warning"
|
|
20
|
-
: "secondary";
|
|
16
|
+
const statusVariant = module.status === "active" ? "success" : "secondary";
|
|
21
17
|
const moduleTitle = resolveLocalizedString(module.title, i18n.language);
|
|
22
18
|
const moduleDescription = resolveLocalizedString(module.description, i18n.language);
|
|
23
19
|
return (_jsxs("div", { className: "p-6 max-w-3xl mx-auto space-y-6", children: [_jsxs("div", { className: "flex items-start gap-4", children: [_jsx("div", { className: "flex-shrink-0 p-3 rounded-lg bg-muted", children: _jsx(Icon, { name: module.icon || "package", className: "h-8 w-8" }) }), _jsxs("div", { className: "flex-1", children: [_jsxs("div", { className: "flex items-center gap-3 mb-1", children: [_jsx("h1", { className: "text-3xl font-bold", children: moduleTitle }), _jsx(Badge, { variant: statusVariant, className: "capitalize", children: module.status })] }), moduleDescription && (_jsx("p", { className: "text-muted-foreground", children: moduleDescription }))] })] }), module.commands && module.commands.length > 0 && (_jsxs(Card, { children: [_jsxs(CardHeader, { className: "pb-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Icon, { name: "zap", className: "h-5 w-5 text-muted-foreground" }), _jsx(CardTitle, { className: "text-lg", children: t('moduleOverview.actions') })] }), _jsx(CardDescription, { children: t('moduleOverview.actionsDescription') })] }), _jsx(CardContent, { children: _jsx("div", { className: "grid gap-3 sm:grid-cols-2 lg:grid-cols-3", children: module.commands.map((command) => (_jsxs("button", { onClick: () => navigate(command.route), className: "flex items-center gap-3 rounded-lg border border-border bg-card p-4 text-left transition-colors hover:bg-accent hover:border-accent", children: [_jsx("div", { className: "flex-shrink-0 p-2 rounded-md bg-muted", children: _jsx(Icon, { name: command.icon || "file-text", className: "h-5 w-5" }) }), _jsx("span", { className: "font-medium", children: resolveLocalizedString(command.title, i18n.language) })] }, command.id))) }) })] })), _jsxs(Card, { children: [_jsx(CardHeader, { className: "pb-3", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Icon, { name: "info", className: "h-5 w-5 text-muted-foreground" }), _jsx(CardTitle, { className: "text-lg", children: t('moduleOverview.information') })] }) }), _jsx(CardContent, { children: _jsxs("dl", { className: "space-y-3", children: [_jsxs("div", { className: "flex items-center justify-between py-2 border-b border-border", children: [_jsxs("dt", { className: "text-sm font-medium text-muted-foreground flex items-center gap-2", children: [_jsx(Icon, { name: "folder", className: "h-4 w-4" }), t('moduleOverview.category')] }), _jsx("dd", { className: "text-sm font-medium", children: module.category })] }), _jsxs("div", { className: "flex items-center justify-between py-2 border-b border-border", children: [_jsxs("dt", { className: "text-sm font-medium text-muted-foreground flex items-center gap-2", children: [_jsx(Icon, { name: "activity", className: "h-4 w-4" }), t('moduleOverview.status')] }), _jsx("dd", { children: _jsx(Badge, { variant: statusVariant, className: "capitalize", children: module.status }) })] }), module.owners?.team && (_jsxs("div", { className: "flex items-center justify-between py-2 border-b border-border", children: [_jsxs("dt", { className: "text-sm font-medium text-muted-foreground flex items-center gap-2", children: [_jsx(Icon, { name: "users", className: "h-4 w-4" }), t('moduleOverview.owner')] }), _jsx("dd", { className: "text-sm font-medium", children: module.owners.team })] })), module.owners?.supportChannel && (_jsxs("div", { className: "flex items-center justify-between py-2", children: [_jsxs("dt", { className: "text-sm font-medium text-muted-foreground flex items-center gap-2", children: [_jsx(Icon, { name: "message-circle", className: "h-4 w-4" }), t('moduleOverview.support')] }), _jsx("dd", { className: "text-sm font-medium", children: module.owners.supportChannel })] }))] }) })] })] }));
|
|
@@ -201,7 +201,7 @@ export function RegistryPage({ apiUrl, registryClient }) {
|
|
|
201
201
|
}
|
|
202
202
|
return (_jsxs("div", { className: "p-6 max-w-4xl mx-auto space-y-6", "data-testid": "registry-page", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-3xl font-bold mb-1", "data-testid": "registry-heading", children: t("registryPage.title") }), _jsx("p", { className: "text-muted-foreground", children: t("registryPage.description") })] }), _jsxs("div", { className: "flex items-center gap-2", children: [canEdit && !isReorderMode && (_jsxs(Button, { variant: "outline", size: "sm", onClick: enterReorderMode, children: [_jsx(Icon, { name: "arrow-up-down", className: "h-4 w-4 mr-2" }), t("registryPage.manageOrder")] })), _jsx(Badge, { variant: apiUrl ? "default" : "secondary", children: apiUrl ? t("registryPage.apiMode") : t("registryPage.inMemoryMode") })] })] }), error && (_jsx(Card, { className: "border-destructive bg-destructive/10", children: _jsx(CardContent, { className: "pt-6", children: _jsxs("div", { className: "flex items-center gap-3 text-destructive", children: [_jsx(Icon, { name: "alert-circle", className: "h-5 w-5" }), _jsx("p", { children: error })] }) }) })), isReorderMode && (_jsx(Card, { children: _jsxs(CardContent, { className: "pt-6", children: [_jsxs("div", { className: "flex items-center justify-between mb-4", children: [_jsx("h2", { className: "text-lg font-semibold", children: t("registryPage.manageOrder") }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { variant: "outline", size: "sm", onClick: cancelReorder, disabled: isSavingOrder, children: t("common.cancel") }), _jsx(Button, { size: "sm", onClick: saveOrder, disabled: isSavingOrder || !hasOrderChanged, children: isSavingOrder ? (_jsxs(_Fragment, { children: [_jsx(Icon, { name: "loader-2", className: "h-4 w-4 mr-2 animate-spin" }), t("registryPage.savingOrder")] })) : (t("registryPage.saveOrder")) })] })] }), _jsx("div", { className: "space-y-1", children: reorderList.map((module, index) => (_jsxs("div", { className: "flex items-center gap-3 p-2 rounded-lg border bg-background", children: [_jsx(Badge, { variant: "outline", className: "font-mono text-xs shrink-0", children: t("registryPage.position", { position: index + 1 }) }), _jsx("div", { className: "flex-shrink-0 p-1.5 rounded bg-muted", children: _jsx(Icon, { name: module.manifest.icon || "package", className: "h-4 w-4" }) }), _jsx("span", { className: "flex-1 font-medium truncate", children: resolveLocalizedString(module.manifest.title, i18n.language) }), _jsxs("div", { className: "flex gap-1 shrink-0", children: [_jsx(Button, { variant: "ghost", size: "icon", className: "h-7 w-7", onClick: () => moveUp(index), disabled: index === 0, children: _jsx(Icon, { name: "chevron-up", className: "h-4 w-4" }) }), _jsx(Button, { variant: "ghost", size: "icon", className: "h-7 w-7", onClick: () => moveDown(index), disabled: index === reorderList.length - 1, children: _jsx(Icon, { name: "chevron-down", className: "h-4 w-4" }) })] })] }, module.id))) })] }) })), !isReorderMode && (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Icon, { name: "package", className: "h-5 w-5 text-muted-foreground" }), _jsxs("h2", { className: "text-lg font-semibold", children: [t("registryPage.registeredModules"), " ", !isLoading && (_jsxs("span", { className: "text-muted-foreground font-normal", children: ["(", modules.length, ")"] }))] })] }), isLoading ? (_jsx(Card, { children: _jsxs(CardContent, { className: "py-8 text-center", children: [_jsx(Icon, { name: "loader-2", className: "h-8 w-8 mx-auto mb-3 animate-spin text-muted-foreground" }), _jsx("p", { className: "text-muted-foreground", children: t("registryPage.loadingModules") })] }) })) : modules.length === 0 ? (_jsx(Card, { children: _jsxs(CardContent, { className: "py-8 text-center", children: [_jsx(Icon, { name: "package", className: "h-8 w-8 mx-auto mb-3 text-muted-foreground" }), _jsx("p", { className: "text-muted-foreground", children: t("registryPage.noModules") }), _jsx("p", { className: "text-sm text-muted-foreground mt-1", children: t("registryPage.noModulesHint") })] }) })) : (_jsx("div", { className: "space-y-3", children: modules.map((module, index) => (_jsx(Card, { className: !module.enabled ? "opacity-60 border-dashed" : undefined, "data-testid": `module-card-${module.moduleId}`, children: _jsx(CardContent, { className: "pt-6", children: _jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { className: "flex items-start gap-4 flex-1 min-w-0", children: [_jsx("div", { className: "flex-shrink-0 p-2 rounded-lg bg-muted", children: _jsx(Icon, { name: module.manifest.icon || "package", className: "h-6 w-6" }) }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsxs("div", { className: "flex items-center gap-2 mb-1 flex-wrap", children: [_jsx("h3", { className: "font-semibold truncate", children: resolveLocalizedString(module.manifest.title, i18n.language) }), canEdit && (_jsx(Badge, { variant: module.enabled ? "success" : "secondary", children: module.enabled
|
|
203
203
|
? t("registryPage.enabled")
|
|
204
|
-
: t("registryPage.disabled") })), module.version ? (_jsxs(Badge, { variant: "outline", "data-testid": `version-badge-${module.moduleId}`, children: ["v", module.version] })) : (_jsx(Badge, { variant: "outline", className: "text-muted-foreground", children: t("registryPage.unversioned") }))] }), _jsx("p", { className: "text-sm text-muted-foreground mb-3", children: resolveLocalizedString(module.manifest.description, i18n.language) ||
|
|
204
|
+
: t("registryPage.disabled") })), module.version ? (_jsxs(Badge, { variant: "outline", "data-testid": `version-badge-${module.moduleId}`, children: ["v", module.version] })) : (_jsx(Badge, { variant: "outline", className: "text-muted-foreground", children: t("registryPage.unversioned") })), (module.sdkVersion ?? module.manifest.sdkVersion) && (_jsxs(Badge, { variant: "outline", className: "font-mono text-xs", children: ["SDK ", module.sdkVersion ?? module.manifest.sdkVersion] }))] }), _jsx("p", { className: "text-sm text-muted-foreground mb-3", children: resolveLocalizedString(module.manifest.description, i18n.language) ||
|
|
205
205
|
t("registryPage.noDescription") }), _jsxs("div", { className: "flex flex-wrap gap-x-4 gap-y-1 text-xs text-muted-foreground", children: [_jsxs("span", { className: "flex items-center gap-1", children: [_jsx(Icon, { name: "hash", className: "h-3 w-3" }), module.moduleId] }), _jsxs("span", { className: "flex items-center gap-1", children: [_jsx(Icon, { name: "folder", className: "h-3 w-3" }), module.manifest.category || "General"] }), _jsxs("span", { className: "flex items-center gap-1", children: [_jsx(Icon, { name: "navigation", className: "h-3 w-3" }), module.manifest.routeBase] }), module.baseUrl && (_jsxs("span", { className: "flex items-center gap-1", title: module.baseUrl, children: [_jsx(Icon, { name: "link", className: "h-3 w-3" }), _jsx("span", { className: "truncate max-w-[200px]", children: module.baseUrl })] }))] })] })] }), _jsxs("div", { className: "flex items-center gap-2 flex-shrink-0", children: [_jsx(Badge, { variant: "outline", className: "font-mono text-xs", children: t("registryPage.position", { position: index + 1 }) }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => openVersionHistory(module), title: t("registryPage.versionHistory"), "data-testid": `version-history-btn-${module.moduleId}`, children: _jsx(Icon, { name: "history", className: "h-4 w-4" }) }), canEdit && (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Label, { className: "text-sm text-muted-foreground", children: t("registryPage.enabled") }), _jsx(Switch, { checked: module.enabled, onCheckedChange: () => handleToggleEnabled(module), "data-testid": `enable-switch-${module.moduleId}` })] })), canDelete && (_jsx(Button, { variant: "destructive", size: "sm", onClick: () => handleDeleteModule(module), children: _jsx(Icon, { name: "trash-2", className: "h-4 w-4" }) }))] })] }) }) }, module.id))) }))] })), _jsx(Dialog, { open: !!disableTarget, onOpenChange: (open) => !open && setDisableTarget(null), children: _jsxs(DialogContent, { children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: t("registryPage.disableTitle", {
|
|
206
206
|
name: disableTarget
|
|
207
207
|
? resolveLocalizedString(disableTarget.manifest.title, i18n.language)
|
|
@@ -3,7 +3,7 @@ import { useState, useEffect } from "react";
|
|
|
3
3
|
import { Search, Moon, Sun, Check, Clock, Globe } from "lucide-react";
|
|
4
4
|
import { useTheme } from "./theme-provider";
|
|
5
5
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, Badge, Button, } from "@nsxbet/admin-ui";
|
|
6
|
-
import { SUPPORTED_LOCALES, LOCALE_FLAGS, LOCALE_NAMES } from "../../i18n";
|
|
6
|
+
import { SUPPORTED_LOCALES, DEFAULT_LOCALE, LOCALE_FLAGS, LOCALE_NAMES } from "../../i18n";
|
|
7
7
|
import { useI18n } from "../../hooks/useI18n";
|
|
8
8
|
const ENV_COLORS = {
|
|
9
9
|
local: "bg-gray-500",
|
|
@@ -30,7 +30,7 @@ const ENV_HEADER_COLORS_DARK = {
|
|
|
30
30
|
export function TopBar({ onSearchClick, environment = "local", locale: externalLocale, onLocaleChange, timezoneMode = "local", onTimezoneToggle, }) {
|
|
31
31
|
const { theme, setTheme } = useTheme();
|
|
32
32
|
const { t } = useI18n();
|
|
33
|
-
const [locale, setLocale] = useState(externalLocale ||
|
|
33
|
+
const [locale, setLocale] = useState(externalLocale || DEFAULT_LOCALE);
|
|
34
34
|
useEffect(() => {
|
|
35
35
|
if (externalLocale && SUPPORTED_LOCALES.includes(externalLocale)) {
|
|
36
36
|
setLocale(externalLocale);
|
package/dist/shell/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { AdminShell } from "./AdminShell";
|
|
2
|
-
export type { AdminShellProps } from "./AdminShell";
|
|
2
|
+
export type { AdminShellProps, AdminShellBffConfig } from "./AdminShell";
|
|
3
3
|
export { TopBar, LeftNav, MainContent, CommandPalette, ThemeProvider, useTheme, } from "./components";
|
|
4
4
|
export type { TopBarProps, CommandPaletteProps } from "./components";
|
|
5
5
|
export type { Module, ModuleCommand, ModuleStatus, NavigationSection, ModuleNavigation, Catalog, ImportMap, SearchableItem, SearchResult, } from "./types";
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
* Resolve the registry polling interval in milliseconds.
|
|
3
3
|
*
|
|
4
4
|
* Priority:
|
|
5
|
-
* 1. Explicit `
|
|
6
|
-
* 2.
|
|
5
|
+
* 1. Explicit `explicitInterval` parameter (passed from shell's env config)
|
|
6
|
+
* 2. `REGISTRY_POLL_INTERVAL` from SDK `env` (parsed from `window.__ENV__` via `/env.js`)
|
|
7
|
+
* 3. Environment-based default (local: 60s, staging: 5min, production: 15min)
|
|
7
8
|
*
|
|
8
9
|
* Returns 0 (disabled) when the explicit value is "0".
|
|
9
10
|
*/
|
|
10
|
-
export declare function resolvePollingInterval(environment: string): number;
|
|
11
|
+
export declare function resolvePollingInterval(environment: string, explicitInterval?: number): number;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { env } from "../env.js";
|
|
1
2
|
const ENVIRONMENT_DEFAULTS = {
|
|
2
3
|
local: 60000,
|
|
3
4
|
development: 60000,
|
|
@@ -9,18 +10,19 @@ const ENVIRONMENT_DEFAULTS = {
|
|
|
9
10
|
* Resolve the registry polling interval in milliseconds.
|
|
10
11
|
*
|
|
11
12
|
* Priority:
|
|
12
|
-
* 1. Explicit `
|
|
13
|
-
* 2.
|
|
13
|
+
* 1. Explicit `explicitInterval` parameter (passed from shell's env config)
|
|
14
|
+
* 2. `REGISTRY_POLL_INTERVAL` from SDK `env` (parsed from `window.__ENV__` via `/env.js`)
|
|
15
|
+
* 3. Environment-based default (local: 60s, staging: 5min, production: 15min)
|
|
14
16
|
*
|
|
15
17
|
* Returns 0 (disabled) when the explicit value is "0".
|
|
16
18
|
*/
|
|
17
|
-
export function resolvePollingInterval(environment) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
export function resolvePollingInterval(environment, explicitInterval) {
|
|
20
|
+
if (explicitInterval !== undefined) {
|
|
21
|
+
return explicitInterval;
|
|
22
|
+
}
|
|
23
|
+
const fromEnv = env.registryPollInterval;
|
|
24
|
+
if (fromEnv !== undefined) {
|
|
25
|
+
return fromEnv;
|
|
24
26
|
}
|
|
25
27
|
return ENVIRONMENT_DEFAULTS[environment] ?? ENVIRONMENT_DEFAULTS.local;
|
|
26
28
|
}
|
package/dist/shell/types.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { LocalizedField } from "../i18n/config.js";
|
|
|
5
5
|
/**
|
|
6
6
|
* Module status in the registry
|
|
7
7
|
*/
|
|
8
|
-
export type ModuleStatus = "active" | "
|
|
8
|
+
export type ModuleStatus = "active" | "disabled";
|
|
9
9
|
/**
|
|
10
10
|
* Command within a module that can be searched and invoked
|
|
11
11
|
*/
|
|
@@ -68,6 +68,8 @@ export interface Module {
|
|
|
68
68
|
};
|
|
69
69
|
/** Module status */
|
|
70
70
|
status: ModuleStatus;
|
|
71
|
+
/** Semver of @nsxbet/admin-sdk the module was built against */
|
|
72
|
+
sdkVersion: string;
|
|
71
73
|
/** Optional navigation order */
|
|
72
74
|
navigationOrder?: number;
|
|
73
75
|
/** Optional icon name */
|
package/dist/types/platform.d.ts
CHANGED
|
@@ -30,8 +30,8 @@ export interface PlatformAPI {
|
|
|
30
30
|
locale: string;
|
|
31
31
|
/** Authentication methods */
|
|
32
32
|
auth: {
|
|
33
|
-
/** Get the current access token (auto-refreshed by shell) */
|
|
34
|
-
getAccessToken: () => Promise<string>;
|
|
33
|
+
/** Get the current access token (auto-refreshed by shell). Null when using BFF HttpOnly cookie auth. */
|
|
34
|
+
getAccessToken: () => Promise<string | null>;
|
|
35
35
|
/** Check if user has a specific permission (realm role) */
|
|
36
36
|
hasPermission: (permission: string) => boolean;
|
|
37
37
|
/** Get current user information */
|
|
@@ -74,21 +74,12 @@ export interface PlatformAPI {
|
|
|
74
74
|
/** Authenticated fetch wrapper */
|
|
75
75
|
fetch: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
76
76
|
}
|
|
77
|
-
/**
|
|
78
|
-
* Keycloak configuration stored in window
|
|
79
|
-
*/
|
|
80
|
-
export interface KeycloakWindowConfig {
|
|
81
|
-
url: string;
|
|
82
|
-
realm: string;
|
|
83
|
-
clientId: string;
|
|
84
|
-
}
|
|
85
77
|
/**
|
|
86
78
|
* Extend Window interface to include platform API
|
|
87
79
|
*/
|
|
88
80
|
declare global {
|
|
89
81
|
interface Window {
|
|
90
82
|
__ADMIN_PLATFORM_API__?: PlatformAPI;
|
|
91
|
-
__KEYCLOAK_CONFIG__?: KeycloakWindowConfig;
|
|
92
83
|
}
|
|
93
84
|
}
|
|
94
85
|
export {};
|
package/dist/vite/config.d.ts
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
import { type Plugin } from "vite";
|
|
2
|
-
/**
|
|
3
|
-
* Default admin gateway URL (staging environment).
|
|
4
|
-
* Used by the env injection plugin when no explicit value is configured.
|
|
5
|
-
*/
|
|
6
|
-
export declare const ADMIN_GATEWAY_STAGING_URL = "https://admin-bff-stg.nsx.dev";
|
|
7
2
|
/**
|
|
8
3
|
* Shared dependencies that are externalized in module builds.
|
|
9
4
|
* These are provided by the shell via import maps.
|
|
@@ -35,11 +30,11 @@ export interface AdminModuleOptions {
|
|
|
35
30
|
buildMode?: "auto" | "always";
|
|
36
31
|
/**
|
|
37
32
|
* Admin gateway URL for BFF token integration (InMemory auth).
|
|
38
|
-
* - `undefined` (default):
|
|
39
|
-
* - `string`:
|
|
40
|
-
* - `null`: disable
|
|
33
|
+
* - `undefined` (default): use `ADMIN_GATEWAY_URL` from env, or {@link DEFAULT_GATEWAY_URL}
|
|
34
|
+
* - `string`: use this URL in `/env.js`
|
|
35
|
+
* - `null`: disable `/env.js` entirely (no runtime env injection)
|
|
41
36
|
*
|
|
42
|
-
* Explicit env vars (`.env` files, shell env)
|
|
37
|
+
* Explicit env vars (`.env` files, shell env) take precedence over the string option.
|
|
43
38
|
*/
|
|
44
39
|
gatewayUrl?: string | null;
|
|
45
40
|
}
|