@mostajs/setup 1.1.1 → 1.1.3

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.
@@ -0,0 +1,18 @@
1
+ export interface ReconfigHandlerConfig {
2
+ /** Callback to get current env values */
3
+ getCurrentConfig?: () => {
4
+ dialect: string;
5
+ uri: string;
6
+ modules: string[];
7
+ };
8
+ }
9
+ /**
10
+ * Creates handlers for the reconfiguration API.
11
+ *
12
+ * GET — returns current config (dialect, active modules)
13
+ * POST — applies new config (db change, module toggle)
14
+ */
15
+ export declare function createReconfigHandlers(config?: ReconfigHandlerConfig): {
16
+ GET: () => Promise<Response>;
17
+ POST: (req: Request) => Promise<Response>;
18
+ };
@@ -0,0 +1,74 @@
1
+ // @mostajs/setup — API Route factory for reconfiguration
2
+ // Author: Dr Hamid MADANI drmdh@msn.com
3
+ import { testDbConnection } from '../lib/db-test';
4
+ import { writeEnvLocal } from '../lib/env-writer';
5
+ import { composeDbUri } from '../lib/compose-uri';
6
+ /**
7
+ * Creates handlers for the reconfiguration API.
8
+ *
9
+ * GET — returns current config (dialect, active modules)
10
+ * POST — applies new config (db change, module toggle)
11
+ */
12
+ export function createReconfigHandlers(config) {
13
+ async function GET() {
14
+ const dialect = process.env.DB_DIALECT || 'mongodb';
15
+ const uri = process.env.SGBD_URI || '';
16
+ const modulesStr = process.env.MOSTAJS_MODULES || '';
17
+ const modules = modulesStr ? modulesStr.split(',').map((m) => m.trim()) : [];
18
+ return Response.json({
19
+ dialect,
20
+ uri,
21
+ modules,
22
+ });
23
+ }
24
+ async function POST(req) {
25
+ try {
26
+ const body = await req.json();
27
+ const { action } = body;
28
+ if (action === 'test-db') {
29
+ const { dialect, host, port, name, user, password } = body;
30
+ const result = await testDbConnection({ dialect, host, port, name, user, password });
31
+ return Response.json(result);
32
+ }
33
+ if (action === 'change-db') {
34
+ const { dialect, host, port, name, user, password } = body;
35
+ // Test first
36
+ const test = await testDbConnection({ dialect, host, port, name, user, password });
37
+ if (!test.ok) {
38
+ return Response.json({ ok: false, error: test.error || 'Connexion echouee' });
39
+ }
40
+ // Write .env.local
41
+ const uri = composeDbUri(dialect, { host, port, name, user, password });
42
+ const needsRestart = await writeEnvLocal({ dialect, uri });
43
+ // Update process.env in-memory
44
+ process.env.DB_DIALECT = dialect;
45
+ process.env.SGBD_URI = uri;
46
+ // Disconnect existing dialect
47
+ try {
48
+ const { disconnectDialect } = await import('@mostajs/orm');
49
+ await disconnectDialect();
50
+ }
51
+ catch { }
52
+ return Response.json({ ok: true, needsRestart });
53
+ }
54
+ if (action === 'update-modules') {
55
+ const { modules } = body;
56
+ const modulesStr = modules.join(',');
57
+ // Read current .env.local and update MOSTAJS_MODULES
58
+ const needsRestart = await writeEnvLocal({
59
+ dialect: (process.env.DB_DIALECT || 'mongodb'),
60
+ uri: process.env.SGBD_URI || '',
61
+ extraVars: { MOSTAJS_MODULES: modulesStr },
62
+ });
63
+ process.env.MOSTAJS_MODULES = modulesStr;
64
+ return Response.json({ ok: true, needsRestart, modules });
65
+ }
66
+ return Response.json({ ok: false, error: 'Action inconnue' }, { status: 400 });
67
+ }
68
+ catch (err) {
69
+ const message = err instanceof Error ? err.message : 'Erreur serveur';
70
+ return Response.json({ ok: false, error: message }, { status: 500 });
71
+ }
72
+ }
73
+ return { GET, POST };
74
+ }
@@ -0,0 +1,17 @@
1
+ export interface ReconfigPanelProps {
2
+ /** API endpoint for reconfiguration (default: '/api/setup/reconfig') */
3
+ apiEndpoint?: string;
4
+ /** API endpoint for module detection (default: '/api/setup/detect-modules') */
5
+ detectEndpoint?: string;
6
+ /** Translate function */
7
+ t?: (key: string) => string;
8
+ /** Called after successful DB change */
9
+ onDbChanged?: (needsRestart: boolean) => void;
10
+ /** Called after modules update */
11
+ onModulesChanged?: (modules: string[]) => void;
12
+ /** Whether to show seed option on DB change (default: true) */
13
+ showSeedOption?: boolean;
14
+ /** Callback to run seed after DB change */
15
+ onSeedRequested?: () => Promise<void>;
16
+ }
17
+ export default function ReconfigPanel({ apiEndpoint, detectEndpoint, t, onDbChanged, onModulesChanged, showSeedOption, onSeedRequested, }: ReconfigPanelProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,327 @@
1
+ // @mostajs/setup — Reconfiguration Panel
2
+ // Author: Dr Hamid MADANI drmdh@msn.com
3
+ 'use client';
4
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
5
+ import { useState, useEffect, useCallback } from 'react';
6
+ const DIALECTS = [
7
+ { key: 'mongodb', name: 'MongoDB', icon: '🍃', defaultPort: 27017, defaultUser: '', defaultHost: 'localhost', requiresAuth: false },
8
+ { key: 'sqlite', name: 'SQLite', icon: '📄', defaultPort: 0, defaultUser: '', defaultHost: '', requiresAuth: false },
9
+ { key: 'postgres', name: 'PostgreSQL', icon: '🐘', defaultPort: 5432, defaultUser: 'postgres', defaultHost: 'localhost', requiresAuth: true },
10
+ { key: 'mysql', name: 'MySQL', icon: '🐬', defaultPort: 3306, defaultUser: 'root', defaultHost: 'localhost', requiresAuth: true },
11
+ { key: 'mariadb', name: 'MariaDB', icon: '🦭', defaultPort: 3306, defaultUser: 'root', defaultHost: 'localhost', requiresAuth: true },
12
+ { key: 'mssql', name: 'SQL Server', icon: '🟦', defaultPort: 1433, defaultUser: 'sa', defaultHost: 'localhost', requiresAuth: true },
13
+ { key: 'oracle', name: 'Oracle', icon: '🔴', defaultPort: 1521, defaultUser: 'system', defaultHost: 'localhost', requiresAuth: true },
14
+ { key: 'cockroachdb', name: 'CockroachDB', icon: '🪳', defaultPort: 26257, defaultUser: 'root', defaultHost: 'localhost', requiresAuth: true },
15
+ ];
16
+ // ── Styles ────────────────────────────────────────────────────
17
+ const S = {
18
+ panel: { fontFamily: 'system-ui, sans-serif', fontSize: 14 },
19
+ section: { marginBottom: 32, padding: 24, border: '1px solid #e5e7eb', borderRadius: 12, backgroundColor: '#fff' },
20
+ sectionTitle: { fontSize: 18, fontWeight: 700, marginBottom: 16, display: 'flex', alignItems: 'center', gap: 8 },
21
+ sectionDesc: { fontSize: 13, color: '#6b7280', marginBottom: 16 },
22
+ grid: { display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))', gap: 12 },
23
+ moduleCard: (active, required) => ({
24
+ padding: 16,
25
+ border: `2px solid ${active ? '#0284c7' : '#e5e7eb'}`,
26
+ borderRadius: 8,
27
+ backgroundColor: active ? '#f0f9ff' : '#fafafa',
28
+ cursor: required ? 'not-allowed' : 'pointer',
29
+ opacity: required ? 0.8 : 1,
30
+ transition: 'all 0.2s',
31
+ }),
32
+ moduleHeader: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 },
33
+ moduleIcon: { fontSize: 20 },
34
+ moduleName: { fontWeight: 600, fontSize: 14 },
35
+ moduleDesc: { fontSize: 12, color: '#6b7280' },
36
+ moduleBadge: (type) => ({
37
+ display: 'inline-block',
38
+ padding: '1px 6px',
39
+ borderRadius: 4,
40
+ fontSize: 10,
41
+ fontWeight: 600,
42
+ backgroundColor: type === 'required' ? '#fef3c7' : type === 'active' ? '#d1fae5' : '#e0e7ff',
43
+ color: type === 'required' ? '#92400e' : type === 'active' ? '#065f46' : '#3730a3',
44
+ marginLeft: 4,
45
+ }),
46
+ dialectGrid: { display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(140px, 1fr))', gap: 8 },
47
+ dialectCard: (selected) => ({
48
+ padding: 12,
49
+ border: `2px solid ${selected ? '#0284c7' : '#e5e7eb'}`,
50
+ borderRadius: 8,
51
+ backgroundColor: selected ? '#f0f9ff' : '#fff',
52
+ cursor: 'pointer',
53
+ textAlign: 'center',
54
+ transition: 'all 0.2s',
55
+ }),
56
+ dialectIcon: { fontSize: 24, marginBottom: 4 },
57
+ dialectName: { fontSize: 12, fontWeight: 600 },
58
+ formGroup: { marginBottom: 12 },
59
+ label: { display: 'block', marginBottom: 4, fontWeight: 500, fontSize: 12, color: '#374151' },
60
+ input: { width: '100%', padding: '8px 12px', border: '1px solid #d1d5db', borderRadius: 6, fontSize: 13, boxSizing: 'border-box' },
61
+ formRow: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 },
62
+ btn: (variant = 'primary') => ({
63
+ padding: '8px 16px',
64
+ border: 'none',
65
+ borderRadius: 6,
66
+ fontSize: 13,
67
+ fontWeight: 600,
68
+ cursor: 'pointer',
69
+ display: 'inline-flex',
70
+ alignItems: 'center',
71
+ gap: 6,
72
+ backgroundColor: variant === 'primary' ? '#0284c7' :
73
+ variant === 'success' ? '#059669' :
74
+ variant === 'danger' ? '#dc2626' : '#e5e7eb',
75
+ color: variant === 'secondary' ? '#374151' : '#fff',
76
+ }),
77
+ alert: (type) => ({
78
+ padding: 12,
79
+ borderRadius: 8,
80
+ marginBottom: 16,
81
+ fontSize: 13,
82
+ backgroundColor: type === 'success' ? '#d1fae5' :
83
+ type === 'error' ? '#fee2e2' :
84
+ type === 'warning' ? '#fef3c7' : '#dbeafe',
85
+ color: type === 'success' ? '#065f46' :
86
+ type === 'error' ? '#991b1b' :
87
+ type === 'warning' ? '#92400e' : '#1e40af',
88
+ border: `1px solid ${type === 'success' ? '#a7f3d0' :
89
+ type === 'error' ? '#fecaca' :
90
+ type === 'warning' ? '#fde68a' : '#bfdbfe'}`,
91
+ }),
92
+ checkbox: { marginRight: 8, width: 16, height: 16, cursor: 'pointer' },
93
+ checkboxLabel: { display: 'flex', alignItems: 'center', fontSize: 13, cursor: 'pointer', padding: '8px 0' },
94
+ currentBadge: {
95
+ display: 'inline-block',
96
+ padding: '2px 8px',
97
+ borderRadius: 12,
98
+ fontSize: 11,
99
+ fontWeight: 600,
100
+ backgroundColor: '#d1fae5',
101
+ color: '#065f46',
102
+ marginLeft: 8,
103
+ },
104
+ };
105
+ // ── Component ────────────────────────────────────────────────
106
+ export default function ReconfigPanel({ apiEndpoint = '/api/setup/reconfig', detectEndpoint = '/api/setup/detect-modules', t = (k) => k, onDbChanged, onModulesChanged, showSeedOption = true, onSeedRequested, }) {
107
+ // --- State ---
108
+ const [loading, setLoading] = useState(true);
109
+ const [currentDialect, setCurrentDialect] = useState('');
110
+ const [currentModules, setCurrentModules] = useState([]);
111
+ const [allModules, setAllModules] = useState([]);
112
+ const [installedModules, setInstalledModules] = useState([]);
113
+ const [activeModules, setActiveModules] = useState(new Set());
114
+ // DB form
115
+ const [selectedDialect, setSelectedDialect] = useState('mongodb');
116
+ const [dbForm, setDbForm] = useState({ host: 'localhost', port: 27017, name: '', user: '', password: '' });
117
+ const [dbTesting, setDbTesting] = useState(false);
118
+ const [dbTestResult, setDbTestResult] = useState(null);
119
+ const [dbSaving, setDbSaving] = useState(false);
120
+ const [dbMessage, setDbMessage] = useState(null);
121
+ const [wantSeed, setWantSeed] = useState(true);
122
+ const [seeding, setSeeding] = useState(false);
123
+ // Module saving
124
+ const [moduleSaving, setModuleSaving] = useState(false);
125
+ const [moduleMessage, setModuleMessage] = useState(null);
126
+ // --- Load current config ---
127
+ const loadConfig = useCallback(async () => {
128
+ setLoading(true);
129
+ try {
130
+ const [configRes, modulesRes] = await Promise.all([
131
+ fetch(apiEndpoint),
132
+ fetch(detectEndpoint),
133
+ ]);
134
+ const config = await configRes.json();
135
+ const detected = await modulesRes.json();
136
+ setCurrentDialect(config.dialect);
137
+ setCurrentModules(config.modules || []);
138
+ setSelectedDialect(config.dialect);
139
+ setAllModules(detected.modules || []);
140
+ setInstalledModules(detected.installed || []);
141
+ // Active = modules in env or all installed if env empty
142
+ const active = config.modules?.length
143
+ ? new Set(config.modules)
144
+ : new Set(detected.installed);
145
+ setActiveModules(active);
146
+ // Set DB form defaults for current dialect
147
+ const dialectInfo = DIALECTS.find((d) => d.key === config.dialect);
148
+ if (dialectInfo) {
149
+ setDbForm({
150
+ host: dialectInfo.defaultHost,
151
+ port: dialectInfo.defaultPort,
152
+ name: '',
153
+ user: dialectInfo.defaultUser,
154
+ password: '',
155
+ });
156
+ }
157
+ }
158
+ catch {
159
+ // ignore
160
+ }
161
+ finally {
162
+ setLoading(false);
163
+ }
164
+ }, [apiEndpoint, detectEndpoint]);
165
+ useEffect(() => { loadConfig(); }, [loadConfig]);
166
+ // --- Dialect change ---
167
+ const handleDialectChange = (dialect) => {
168
+ setSelectedDialect(dialect);
169
+ setDbTestResult(null);
170
+ setDbMessage(null);
171
+ const info = DIALECTS.find((d) => d.key === dialect);
172
+ if (info) {
173
+ setDbForm({
174
+ host: info.defaultHost,
175
+ port: info.defaultPort,
176
+ name: '',
177
+ user: info.defaultUser,
178
+ password: '',
179
+ });
180
+ }
181
+ };
182
+ // --- Test DB ---
183
+ const handleTestDb = async () => {
184
+ setDbTesting(true);
185
+ setDbTestResult(null);
186
+ try {
187
+ const res = await fetch(apiEndpoint, {
188
+ method: 'POST',
189
+ headers: { 'Content-Type': 'application/json' },
190
+ body: JSON.stringify({
191
+ action: 'test-db',
192
+ dialect: selectedDialect,
193
+ ...dbForm,
194
+ }),
195
+ });
196
+ const result = await res.json();
197
+ setDbTestResult(result);
198
+ }
199
+ catch {
200
+ setDbTestResult({ ok: false, error: 'Erreur reseau' });
201
+ }
202
+ finally {
203
+ setDbTesting(false);
204
+ }
205
+ };
206
+ // --- Apply DB change ---
207
+ const handleApplyDb = async () => {
208
+ setDbSaving(true);
209
+ setDbMessage(null);
210
+ try {
211
+ const res = await fetch(apiEndpoint, {
212
+ method: 'POST',
213
+ headers: { 'Content-Type': 'application/json' },
214
+ body: JSON.stringify({
215
+ action: 'change-db',
216
+ dialect: selectedDialect,
217
+ ...dbForm,
218
+ }),
219
+ });
220
+ const result = await res.json();
221
+ if (result.ok) {
222
+ setCurrentDialect(selectedDialect);
223
+ // Seed if requested
224
+ if (wantSeed && showSeedOption && onSeedRequested) {
225
+ setSeeding(true);
226
+ try {
227
+ await onSeedRequested();
228
+ setDbMessage({ type: 'success', text: 'Base de donnees changee et seed effectue. ' + (result.needsRestart ? 'Redemarrage necessaire.' : '') });
229
+ }
230
+ catch {
231
+ setDbMessage({ type: 'warning', text: 'Base de donnees changee mais le seed a echoue. Vous pouvez relancer le seed manuellement.' });
232
+ }
233
+ finally {
234
+ setSeeding(false);
235
+ }
236
+ }
237
+ else {
238
+ setDbMessage({
239
+ type: result.needsRestart ? 'warning' : 'success',
240
+ text: result.needsRestart
241
+ ? 'Base de donnees changee. Redemarrez le serveur pour appliquer.'
242
+ : 'Base de donnees changee avec succes.',
243
+ });
244
+ }
245
+ onDbChanged?.(result.needsRestart);
246
+ }
247
+ else {
248
+ setDbMessage({ type: 'error', text: result.error || 'Erreur' });
249
+ }
250
+ }
251
+ catch {
252
+ setDbMessage({ type: 'error', text: 'Erreur reseau' });
253
+ }
254
+ finally {
255
+ setDbSaving(false);
256
+ }
257
+ };
258
+ // --- Toggle module ---
259
+ const toggleModule = (key) => {
260
+ const mod = allModules.find((m) => m.key === key);
261
+ if (mod?.required)
262
+ return;
263
+ setActiveModules((prev) => {
264
+ const next = new Set(prev);
265
+ if (next.has(key)) {
266
+ next.delete(key);
267
+ }
268
+ else {
269
+ next.add(key);
270
+ // Auto-add dependencies
271
+ if (mod?.dependsOn) {
272
+ for (const dep of mod.dependsOn)
273
+ next.add(dep);
274
+ }
275
+ }
276
+ return next;
277
+ });
278
+ };
279
+ // --- Save modules ---
280
+ const handleSaveModules = async () => {
281
+ setModuleSaving(true);
282
+ setModuleMessage(null);
283
+ try {
284
+ const modules = Array.from(activeModules);
285
+ const res = await fetch(apiEndpoint, {
286
+ method: 'POST',
287
+ headers: { 'Content-Type': 'application/json' },
288
+ body: JSON.stringify({ action: 'update-modules', modules }),
289
+ });
290
+ const result = await res.json();
291
+ if (result.ok) {
292
+ setCurrentModules(modules);
293
+ setModuleMessage({
294
+ type: 'success',
295
+ text: result.needsRestart
296
+ ? 'Modules mis a jour. Redemarrez le serveur pour appliquer.'
297
+ : 'Modules mis a jour avec succes.',
298
+ });
299
+ onModulesChanged?.(modules);
300
+ }
301
+ else {
302
+ setModuleMessage({ type: 'error', text: result.error || 'Erreur' });
303
+ }
304
+ }
305
+ catch {
306
+ setModuleMessage({ type: 'error', text: 'Erreur reseau' });
307
+ }
308
+ finally {
309
+ setModuleSaving(false);
310
+ }
311
+ };
312
+ // --- Render ---
313
+ if (loading) {
314
+ return _jsx("div", { style: { textAlign: 'center', padding: 40, color: '#6b7280' }, children: "Chargement de la configuration..." });
315
+ }
316
+ const dialectInfo = DIALECTS.find((d) => d.key === selectedDialect);
317
+ const isSqlite = selectedDialect === 'sqlite';
318
+ return (_jsxs("div", { style: S.panel, children: [_jsxs("div", { style: S.section, children: [_jsxs("div", { style: S.sectionTitle, children: [_jsx("span", { children: "\uD83D\uDCE6" }), " Modules actifs"] }), _jsx("div", { style: S.sectionDesc, children: "Activez ou desactivez les modules @mostajs. Les modules requis ne peuvent pas etre desactives. Seuls les modules installes (dans node_modules) peuvent etre actives." }), moduleMessage && (_jsx("div", { style: S.alert(moduleMessage.type), children: moduleMessage.text })), _jsx("div", { style: S.grid, children: allModules.map((mod) => {
319
+ const isInstalled = installedModules.includes(mod.key);
320
+ const isActive = activeModules.has(mod.key);
321
+ const isRequired = !!mod.required;
322
+ return (_jsxs("div", { style: {
323
+ ...S.moduleCard(isActive, isRequired || !isInstalled),
324
+ opacity: isInstalled ? 1 : 0.5,
325
+ }, onClick: () => isInstalled && toggleModule(mod.key), children: [_jsxs("div", { style: S.moduleHeader, children: [_jsx("span", { style: S.moduleIcon, children: mod.icon }), _jsx("span", { style: S.moduleName, children: mod.label }), isRequired && _jsx("span", { style: S.moduleBadge('required'), children: "requis" }), isActive && !isRequired && _jsx("span", { style: S.moduleBadge('active'), children: "actif" }), !isInstalled && _jsx("span", { style: S.moduleBadge('dep'), children: "non installe" })] }), _jsx("div", { style: S.moduleDesc, children: mod.description }), mod.dependsOn?.length ? (_jsxs("div", { style: { fontSize: 11, color: '#9ca3af', marginTop: 4 }, children: ["Depend de : ", mod.dependsOn.join(', ')] })) : null] }, mod.key));
326
+ }) }), _jsx("div", { style: { marginTop: 16, display: 'flex', gap: 8 }, children: _jsx("button", { style: S.btn('primary'), onClick: handleSaveModules, disabled: moduleSaving, children: moduleSaving ? 'Enregistrement...' : 'Enregistrer les modules' }) })] }), _jsxs("div", { style: S.section, children: [_jsxs("div", { style: S.sectionTitle, children: [_jsx("span", { children: "\uD83D\uDDC4\uFE0F" }), " Base de donnees", currentDialect && (_jsxs("span", { style: S.currentBadge, children: ["Actuelle : ", DIALECTS.find((d) => d.key === currentDialect)?.name || currentDialect] }))] }), _jsx("div", { style: S.sectionDesc, children: "Changez le dialecte ou les parametres de connexion. La connexion sera testee avant application." }), dbMessage && (_jsx("div", { style: S.alert(dbMessage.type), children: dbMessage.text })), _jsxs("div", { style: { marginBottom: 20 }, children: [_jsx("div", { style: { ...S.label, marginBottom: 8, fontSize: 13 }, children: "Dialecte" }), _jsx("div", { style: S.dialectGrid, children: DIALECTS.map((d) => (_jsxs("div", { style: S.dialectCard(selectedDialect === d.key), onClick: () => handleDialectChange(d.key), children: [_jsx("div", { style: S.dialectIcon, children: d.icon }), _jsxs("div", { style: S.dialectName, children: [d.name, d.key === currentDialect && _jsx("span", { style: { fontSize: 9, color: '#059669' }, children: " (actuel)" })] })] }, d.key))) })] }), !isSqlite && (_jsxs("div", { style: { maxWidth: 500 }, children: [_jsxs("div", { style: S.formRow, children: [_jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: "Hote" }), _jsx("input", { style: S.input, value: dbForm.host, onChange: (e) => setDbForm({ ...dbForm, host: e.target.value }), placeholder: "localhost" })] }), _jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: "Port" }), _jsx("input", { style: S.input, type: "number", value: dbForm.port, onChange: (e) => setDbForm({ ...dbForm, port: Number(e.target.value) }) })] })] }), _jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: "Nom de la base" }), _jsx("input", { style: S.input, value: dbForm.name, onChange: (e) => setDbForm({ ...dbForm, name: e.target.value }), placeholder: "ma_base_de_donnees" })] }), dialectInfo?.requiresAuth && (_jsxs("div", { style: S.formRow, children: [_jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: "Utilisateur" }), _jsx("input", { style: S.input, value: dbForm.user, onChange: (e) => setDbForm({ ...dbForm, user: e.target.value }) })] }), _jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: "Mot de passe" }), _jsx("input", { style: S.input, type: "password", value: dbForm.password, onChange: (e) => setDbForm({ ...dbForm, password: e.target.value }) })] })] }))] })), isSqlite && (_jsxs("div", { style: S.formGroup, children: [_jsx("label", { style: S.label, children: "Nom du fichier (sans extension)" }), _jsx("input", { style: { ...S.input, maxWidth: 300 }, value: dbForm.name, onChange: (e) => setDbForm({ ...dbForm, name: e.target.value }), placeholder: "ma_base" }), _jsxs("div", { style: { fontSize: 11, color: '#9ca3af', marginTop: 4 }, children: ["Le fichier sera cree dans ./data/", dbForm.name || 'ma_base', ".db"] })] })), dbTestResult && (_jsx("div", { style: S.alert(dbTestResult.ok ? 'success' : 'error'), children: dbTestResult.ok ? 'Connexion reussie !' : `Echec : ${dbTestResult.error}` })), showSeedOption && onSeedRequested && (selectedDialect !== currentDialect || dbForm.name) && (_jsx("div", { style: { marginBottom: 12, padding: 12, backgroundColor: '#fffbeb', border: '1px solid #fde68a', borderRadius: 8 }, children: _jsxs("label", { style: S.checkboxLabel, children: [_jsx("input", { type: "checkbox", checked: wantSeed, onChange: (e) => setWantSeed(e.target.checked), style: S.checkbox }), _jsxs("div", { children: [_jsx("div", { style: { fontWeight: 600, fontSize: 13 }, children: "Initialiser la nouvelle base (seed)" }), _jsx("div", { style: { fontSize: 12, color: '#92400e' }, children: "Cree les tables/collections, permissions, roles et categories dans la nouvelle base. Recommande si vous changez vers une base vide." })] })] }) })), _jsxs("div", { style: { display: 'flex', gap: 8, marginTop: 16 }, children: [_jsx("button", { style: S.btn('secondary'), onClick: handleTestDb, disabled: dbTesting || (!isSqlite && !dbForm.name), children: dbTesting ? 'Test en cours...' : 'Tester la connexion' }), _jsx("button", { style: S.btn('primary'), onClick: handleApplyDb, disabled: dbSaving || seeding || (!isSqlite && !dbForm.name) || (dbTestResult !== null && !dbTestResult.ok), children: seeding ? 'Seed en cours...' : dbSaving ? 'Application...' : 'Appliquer' })] })] })] }));
327
+ }
package/dist/index.d.ts CHANGED
@@ -10,5 +10,7 @@ export { createInstallHandler } from './api/install.route';
10
10
  export { createStatusHandler } from './api/status.route';
11
11
  export { createDetectModulesHandler } from './api/detect-modules.route';
12
12
  export { createInstallModulesHandler } from './api/install-modules.route';
13
+ export { createReconfigHandlers } from './api/reconfig.route';
14
+ export { default as ReconfigPanel } from './components/ReconfigPanel';
13
15
  export { setupMenuContribution } from './lib/menu';
14
16
  export type { DialectType, DialectInfo, DbConfig, InstallConfig, SeedOptions, SeedDefinition, MostaSetupConfig, ModuleDefinition, } from './types/index';
package/dist/index.js CHANGED
@@ -16,5 +16,8 @@ export { createInstallHandler } from './api/install.route';
16
16
  export { createStatusHandler } from './api/status.route';
17
17
  export { createDetectModulesHandler } from './api/detect-modules.route';
18
18
  export { createInstallModulesHandler } from './api/install-modules.route';
19
+ export { createReconfigHandlers } from './api/reconfig.route';
20
+ // Components
21
+ export { default as ReconfigPanel } from './components/ReconfigPanel';
19
22
  // Menu contribution
20
23
  export { setupMenuContribution } from './lib/menu';
package/dist/lib/menu.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // @mostajs/setup — Menu contribution
2
2
  // Author: Dr Hamid MADANI drmdh@msn.com
3
- import { Wrench } from 'lucide-react';
3
+ import { Wrench, Settings2 } from 'lucide-react';
4
4
  export const setupMenuContribution = {
5
5
  moduleKey: 'setup',
6
6
  mergeIntoGroup: 'Administration',
@@ -12,5 +12,11 @@ export const setupMenuContribution = {
12
12
  icon: Wrench,
13
13
  permission: 'admin:access',
14
14
  },
15
+ {
16
+ label: 'setup.reconfig.title',
17
+ href: '/dashboard/settings/reconfig',
18
+ icon: Settings2,
19
+ permission: 'admin:settings',
20
+ },
15
21
  ],
16
22
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mostajs/setup",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "Reusable setup wizard module — multi-dialect DB configuration, .env.local writer, seed runner",
5
5
  "author": "Dr Hamid MADANI <drmdh@msn.com>",
6
6
  "license": "MIT",
@@ -32,6 +32,21 @@
32
32
  "types": "./dist/types/index.d.ts",
33
33
  "import": "./dist/types/index.js",
34
34
  "default": "./dist/types/index.js"
35
+ },
36
+ "./lib/menu": {
37
+ "types": "./dist/lib/menu.d.ts",
38
+ "import": "./dist/lib/menu.js",
39
+ "default": "./dist/lib/menu.js"
40
+ },
41
+ "./components/ReconfigPanel": {
42
+ "types": "./dist/components/ReconfigPanel.d.ts",
43
+ "import": "./dist/components/ReconfigPanel.js",
44
+ "default": "./dist/components/ReconfigPanel.js"
45
+ },
46
+ "./api/reconfig": {
47
+ "types": "./dist/api/reconfig.route.d.ts",
48
+ "import": "./dist/api/reconfig.route.js",
49
+ "default": "./dist/api/reconfig.route.js"
35
50
  }
36
51
  },
37
52
  "files": [
@@ -68,14 +83,19 @@
68
83
  "@mostajs/menu": "^1.0.2",
69
84
  "@types/bcryptjs": "^2.4.0",
70
85
  "@types/node": "^25.3.3",
86
+ "@types/react": "^19.2.14",
71
87
  "typescript": "^5.6.0"
72
88
  },
73
89
  "peerDependencies": {
74
- "@mostajs/menu": ">=1.0.2"
90
+ "@mostajs/menu": ">=1.0.2",
91
+ "react": ">=18.0.0"
75
92
  },
76
93
  "peerDependenciesMeta": {
77
94
  "@mostajs/menu": {
78
95
  "optional": true
96
+ },
97
+ "react": {
98
+ "optional": true
79
99
  }
80
100
  }
81
101
  }