@mostajs/setup 2.1.2 → 2.1.4
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.
|
@@ -28,6 +28,13 @@ export function createSetupJsonHandler(needsSetup) {
|
|
|
28
28
|
dbNamePrefix: json.app?.dbNamePrefix,
|
|
29
29
|
hasRbac: !!(json.rbac?.roles?.length || json.rbac?.permissions?.length),
|
|
30
30
|
seedCount: json.seeds?.length ?? 0,
|
|
31
|
+
seeds: (json.seeds ?? []).map(s => ({
|
|
32
|
+
key: s.key,
|
|
33
|
+
label: s.label,
|
|
34
|
+
description: s.description ?? '',
|
|
35
|
+
icon: s.icon,
|
|
36
|
+
default: s.default ?? false,
|
|
37
|
+
})),
|
|
31
38
|
modules: json.modules ?? [],
|
|
32
39
|
},
|
|
33
40
|
});
|
|
@@ -17,6 +17,8 @@ export interface SetupWizardProps {
|
|
|
17
17
|
preflight?: string;
|
|
18
18
|
/** Create database endpoint */
|
|
19
19
|
createDb?: string;
|
|
20
|
+
/** Setup JSON endpoint (GET returns seeds list) */
|
|
21
|
+
setupJson?: string;
|
|
20
22
|
};
|
|
21
23
|
/** Default database name prefix (e.g. 'secuaccessdb') */
|
|
22
24
|
dbNamePrefix?: string;
|
|
@@ -361,6 +361,7 @@ export default function SetupWizard({ t: tProp, onComplete, endpoints = {}, dbNa
|
|
|
361
361
|
seed: endpoints.seed || '',
|
|
362
362
|
preflight: endpoints.preflight || '/api/setup/preflight',
|
|
363
363
|
createDb: endpoints.createDb || '/api/setup/create-db',
|
|
364
|
+
setupJson: endpoints.setupJson || '/api/setup/setup-json',
|
|
364
365
|
};
|
|
365
366
|
// --- State ---
|
|
366
367
|
const [currentStep, setCurrentStep] = useState(0);
|
|
@@ -369,7 +370,8 @@ export default function SetupWizard({ t: tProp, onComplete, endpoints = {}, dbNa
|
|
|
369
370
|
const [dbTestResult, setDbTestResult] = useState(null);
|
|
370
371
|
const [dbTesting, setDbTesting] = useState(false);
|
|
371
372
|
const [adminConfig, setAdminConfig] = useState({ firstName: '', lastName: '', email: '', password: '', confirmPassword: '' });
|
|
372
|
-
const [seedOptions, setSeedOptions] = useState({
|
|
373
|
+
const [seedOptions, setSeedOptions] = useState({});
|
|
374
|
+
const [availableSeeds, setAvailableSeeds] = useState([]);
|
|
373
375
|
const [availableModules, setAvailableModules] = useState([]);
|
|
374
376
|
const [selectedModules, setSelectedModules] = useState([]);
|
|
375
377
|
const [detectedModules, setDetectedModules] = useState([]);
|
|
@@ -423,6 +425,27 @@ export default function SetupWizard({ t: tProp, onComplete, endpoints = {}, dbNa
|
|
|
423
425
|
}
|
|
424
426
|
catch { }
|
|
425
427
|
}, [hydrated, persistState, currentStep, dialect, dbConfig, adminConfig, seedOptions, selectedModules]);
|
|
428
|
+
// --- Load seeds from setup.json ---
|
|
429
|
+
useEffect(() => {
|
|
430
|
+
if (!hydrated)
|
|
431
|
+
return;
|
|
432
|
+
fetch(ep.setupJson)
|
|
433
|
+
.then(r => r.json())
|
|
434
|
+
.then((data) => {
|
|
435
|
+
const seeds = data.config?.seeds ?? [];
|
|
436
|
+
setAvailableSeeds(seeds);
|
|
437
|
+
// Initialize seedOptions from defaults (only if not already restored from session)
|
|
438
|
+
setSeedOptions(prev => {
|
|
439
|
+
if (Object.keys(prev).length > 0)
|
|
440
|
+
return prev;
|
|
441
|
+
const defaults = {};
|
|
442
|
+
for (const s of seeds)
|
|
443
|
+
defaults[s.key] = s.default;
|
|
444
|
+
return defaults;
|
|
445
|
+
});
|
|
446
|
+
})
|
|
447
|
+
.catch(() => { });
|
|
448
|
+
}, [hydrated]);
|
|
426
449
|
// --- Detect modules ---
|
|
427
450
|
useEffect(() => {
|
|
428
451
|
const declaredKeys = new Set((declaredModules ?? []).map(m => m.key));
|
|
@@ -777,7 +800,7 @@ export default function SetupWizard({ t: tProp, onComplete, endpoints = {}, dbNa
|
|
|
777
800
|
fontSize: 12, backgroundColor: '#f0f9ff', color: '#0369a1',
|
|
778
801
|
border: '1px solid #bae6fd', borderRadius: 16, padding: '4px 10px',
|
|
779
802
|
}, children: [mod.icon, " ", mod.label] }, key)) : null;
|
|
780
|
-
}) })] }), _jsxs("div", { style: S.summaryCard, children: [_jsx("div", { style: S.summaryTitle, children: t('setup.summary.seedTitle') }), _jsx("p", { style: { ...S.summaryText, marginBottom: 12 }, children: t('setup.summary.seedInfo') }),
|
|
803
|
+
}) })] }), availableSeeds.length > 0 && (_jsxs("div", { style: S.summaryCard, children: [_jsx("div", { style: S.summaryTitle, children: t('setup.summary.seedTitle') }), _jsx("p", { style: { ...S.summaryText, marginBottom: 12 }, children: t('setup.summary.seedInfo') }), availableSeeds.map(seed => (_jsxs("div", { style: S.checkRow, children: [_jsx("input", { type: "checkbox", style: S.checkbox, checked: seedOptions[seed.key] ?? false, onChange: e => setSeedOptions({ ...seedOptions, [seed.key]: e.target.checked }), disabled: installing || !!installResult?.ok }), _jsxs("div", { children: [_jsx("div", { style: { fontSize: 13, fontWeight: 500 }, children: seed.label }), _jsx("div", { style: { fontSize: 12, color: '#9ca3af' }, children: seed.description })] })] }, seed.key)))] })), installResult && (_jsx("div", { style: S.alert(installResult.ok ? 'success' : 'error'), children: installResult.ok ? (_jsxs(_Fragment, { children: [_jsxs("div", { style: { fontWeight: 600, marginBottom: 4 }, children: ["\u2705 ", t('setup.summary.success')] }), _jsx("div", { children: t('setup.summary.successDesc') }), installResult.needsRestart && (_jsx("div", { style: { color: '#92400e', fontWeight: 500, marginTop: 8 }, children: t('setup.summary.needsRestart') }))] })) : (_jsxs("div", { children: ["\u274C ", installResult.error] })) })), installResult?.ok && (_jsxs("div", { style: { ...S.summaryCard, marginTop: 16 }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12 }, children: [_jsx("span", { style: { fontSize: 18 }, children: "\uD83D\uDD0C" }), _jsx("div", { style: S.summaryTitle, children: "Cablage des modules" })] }), _jsx("div", { style: { fontSize: 13, color: '#6b7280', marginBottom: 12 }, children: "Cablez les modules pour injecter schemas, routes API, pages et permissions dans l'application." }), wireMessage && (_jsx("div", { style: S.alert(wireMessage.type), children: wireMessage.text })), wireLoading ? (_jsx("div", { style: { textAlign: 'center', padding: 12, color: '#6b7280' }, children: "Chargement..." })) : wireModules.length === 0 ? (_jsx("div", { style: { textAlign: 'center', padding: 12, color: '#9ca3af' }, children: "Aucun manifeste de cablage trouve" })) : (_jsx("div", { style: S.wireGrid, children: wireModules.map((mod) => (_jsxs("div", { style: S.wireCard(mod.installed), children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between' }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8 }, children: [_jsx("span", { style: S.wireStatus(mod.installed) }), _jsxs("span", { style: { fontWeight: 700, fontSize: 14 }, children: ["@mostajs/", mod.name] })] }), _jsx("span", { style: {
|
|
781
804
|
fontSize: 10, fontWeight: 600, padding: '2px 6px', borderRadius: 4,
|
|
782
805
|
backgroundColor: mod.type === 'business' ? '#dbeafe' : '#f3e8ff',
|
|
783
806
|
color: mod.type === 'business' ? '#1e40af' : '#6b21a8',
|
|
@@ -35,6 +35,11 @@ export interface SetupJsonSeed {
|
|
|
35
35
|
hashField?: string;
|
|
36
36
|
roleField?: string;
|
|
37
37
|
defaults?: Record<string, unknown>;
|
|
38
|
+
lookupFields?: Record<string, {
|
|
39
|
+
collection: string;
|
|
40
|
+
match: string;
|
|
41
|
+
value: string;
|
|
42
|
+
}>;
|
|
38
43
|
data: Record<string, unknown>[];
|
|
39
44
|
}
|
|
40
45
|
export interface SetupJson {
|
|
@@ -134,8 +134,18 @@ function buildSeedDefinition(seedDef, repoFactory) {
|
|
|
134
134
|
run: async () => {
|
|
135
135
|
const getRepo = repoFactory ?? defaultRepoFactory;
|
|
136
136
|
const repo = await getRepo(seedDef.collection);
|
|
137
|
+
// Resolve lookupFields once (e.g. createdBy → user where email = admin@test.dz → userId)
|
|
138
|
+
const resolved = {};
|
|
139
|
+
if (seedDef.lookupFields) {
|
|
140
|
+
for (const [field, lookup] of Object.entries(seedDef.lookupFields)) {
|
|
141
|
+
const lookupRepo = await getRepo(lookup.collection);
|
|
142
|
+
const found = await lookupRepo.findOne({ [lookup.match]: lookup.value });
|
|
143
|
+
if (found)
|
|
144
|
+
resolved[field] = found.id;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
137
147
|
for (const rawItem of seedDef.data) {
|
|
138
|
-
const item = { ...(seedDef.defaults ?? {}), ...rawItem };
|
|
148
|
+
const item = { ...(seedDef.defaults ?? {}), ...resolved, ...rawItem };
|
|
139
149
|
// Hash field if configured (e.g. password)
|
|
140
150
|
if (seedDef.hashField && item[seedDef.hashField]) {
|
|
141
151
|
const bcryptModule = await import('bcryptjs');
|
package/package.json
CHANGED