@mostajs/setup 1.1.4 → 1.3.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.
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Creates handlers for JAR file management API.
3
+ *
4
+ * GET — list uploaded JARs and JDBC dialect status
5
+ * POST — upload a new JAR file (multipart/form-data)
6
+ * DELETE — remove a JAR file
7
+ */
8
+ export declare function createUploadJarHandlers(): {
9
+ GET: () => Promise<Response>;
10
+ POST: (req: Request) => Promise<Response>;
11
+ DELETE: (req: Request) => Promise<Response>;
12
+ };
@@ -0,0 +1,73 @@
1
+ // @mostajs/setup — API Route factory for JAR file upload/list/delete
2
+ // Delegates to @mostajs/orm jar-upload module
3
+ // Author: Dr Hamid MADANI drmdh@msn.com
4
+ /**
5
+ * Creates handlers for JAR file management API.
6
+ *
7
+ * GET — list uploaded JARs and JDBC dialect status
8
+ * POST — upload a new JAR file (multipart/form-data)
9
+ * DELETE — remove a JAR file
10
+ */
11
+ export function createUploadJarHandlers() {
12
+ async function GET() {
13
+ try {
14
+ const { listJarFiles, getJdbcDialectStatus } = await import('@mostajs/orm');
15
+ return Response.json({
16
+ ok: true,
17
+ jars: listJarFiles(),
18
+ dialects: getJdbcDialectStatus(),
19
+ });
20
+ }
21
+ catch (err) {
22
+ const message = err instanceof Error ? err.message : 'Erreur serveur';
23
+ return Response.json({ ok: false, error: message }, { status: 500 });
24
+ }
25
+ }
26
+ async function POST(req) {
27
+ try {
28
+ const { saveJarFile } = await import('@mostajs/orm');
29
+ const formData = await req.formData();
30
+ const file = formData.get('jar');
31
+ if (!file) {
32
+ return Response.json({ ok: false, error: 'Aucun fichier JAR fourni. Utilisez le champ "jar".' }, { status: 400 });
33
+ }
34
+ if (!file.name.endsWith('.jar')) {
35
+ return Response.json({ ok: false, error: 'Le fichier doit etre un .jar' }, { status: 400 });
36
+ }
37
+ // Max 50MB
38
+ if (file.size > 50 * 1024 * 1024) {
39
+ return Response.json({ ok: false, error: 'Le fichier JAR depasse la limite de 50 MB' }, { status: 400 });
40
+ }
41
+ const arrayBuffer = await file.arrayBuffer();
42
+ const buffer = Buffer.from(arrayBuffer);
43
+ const result = saveJarFile(file.name, buffer);
44
+ if (!result.ok) {
45
+ return Response.json(result, { status: 400 });
46
+ }
47
+ return Response.json(result);
48
+ }
49
+ catch (err) {
50
+ const message = err instanceof Error ? err.message : 'Erreur serveur';
51
+ return Response.json({ ok: false, error: message }, { status: 500 });
52
+ }
53
+ }
54
+ async function DELETE(req) {
55
+ try {
56
+ const { deleteJarFile } = await import('@mostajs/orm');
57
+ const { fileName } = await req.json();
58
+ if (!fileName) {
59
+ return Response.json({ ok: false, error: 'fileName requis' }, { status: 400 });
60
+ }
61
+ const result = deleteJarFile(fileName);
62
+ if (!result.ok) {
63
+ return Response.json(result, { status: 404 });
64
+ }
65
+ return Response.json(result);
66
+ }
67
+ catch (err) {
68
+ const message = err instanceof Error ? err.message : 'Erreur serveur';
69
+ return Response.json({ ok: false, error: message }, { status: 500 });
70
+ }
71
+ }
72
+ return { GET, POST, DELETE };
73
+ }
@@ -3,6 +3,8 @@ export interface ReconfigPanelProps {
3
3
  apiEndpoint?: string;
4
4
  /** API endpoint for module detection (default: '/api/setup/detect-modules') */
5
5
  detectEndpoint?: string;
6
+ /** API endpoint for JAR upload (default: '/api/setup/upload-jar') */
7
+ jarEndpoint?: string;
6
8
  /** Translate function */
7
9
  t?: (key: string) => string;
8
10
  /** Called after successful DB change */
@@ -14,4 +16,4 @@ export interface ReconfigPanelProps {
14
16
  /** Callback to run seed after DB change */
15
17
  onSeedRequested?: () => Promise<void>;
16
18
  }
17
- export default function ReconfigPanel({ apiEndpoint, detectEndpoint, t, onDbChanged, onModulesChanged, showSeedOption, onSeedRequested, }: ReconfigPanelProps): import("react/jsx-runtime").JSX.Element;
19
+ export default function ReconfigPanel({ apiEndpoint, detectEndpoint, jarEndpoint, t, onDbChanged, onModulesChanged, showSeedOption, onSeedRequested, }: ReconfigPanelProps): import("react/jsx-runtime").JSX.Element;
@@ -3,6 +3,8 @@
3
3
  'use client';
4
4
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
5
5
  import { useState, useEffect, useCallback } from 'react';
6
+ /** Dialects that require a JDBC bridge JAR */
7
+ const JDBC_DIALECTS = ['hsqldb', 'oracle', 'db2', 'sybase', 'hana'];
6
8
  const DIALECTS = [
7
9
  { key: 'mongodb', name: 'MongoDB', icon: '🍃', defaultPort: 27017, defaultUser: '', defaultHost: 'localhost', requiresAuth: false },
8
10
  { key: 'sqlite', name: 'SQLite', icon: '📄', defaultPort: 0, defaultUser: '', defaultHost: '', requiresAuth: false },
@@ -10,8 +12,13 @@ const DIALECTS = [
10
12
  { key: 'mysql', name: 'MySQL', icon: '🐬', defaultPort: 3306, defaultUser: 'root', defaultHost: 'localhost', requiresAuth: true },
11
13
  { key: 'mariadb', name: 'MariaDB', icon: '🦭', defaultPort: 3306, defaultUser: 'root', defaultHost: 'localhost', requiresAuth: true },
12
14
  { 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 },
15
+ { key: 'oracle', name: 'Oracle', icon: '🔴', defaultPort: 1521, defaultUser: 'system', defaultHost: 'localhost', requiresAuth: true, premium: true, jdbc: true },
14
16
  { key: 'cockroachdb', name: 'CockroachDB', icon: '🪳', defaultPort: 26257, defaultUser: 'root', defaultHost: 'localhost', requiresAuth: true },
17
+ { key: 'db2', name: 'IBM DB2', icon: '🏢', defaultPort: 50000, defaultUser: 'db2inst1', defaultHost: 'localhost', requiresAuth: true, premium: true, jdbc: true },
18
+ { key: 'hana', name: 'SAP HANA', icon: '💎', defaultPort: 39013, defaultUser: 'SYSTEM', defaultHost: 'localhost', requiresAuth: true, premium: true, jdbc: true },
19
+ { key: 'hsqldb', name: 'HyperSQL', icon: '⚡', defaultPort: 9001, defaultUser: 'SA', defaultHost: 'localhost', requiresAuth: false, jdbc: true },
20
+ { key: 'spanner', name: 'Cloud Spanner', icon: '☁️', defaultPort: 0, defaultUser: '', defaultHost: '', requiresAuth: false, premium: true },
21
+ { key: 'sybase', name: 'Sybase ASE', icon: '🔷', defaultPort: 5000, defaultUser: 'sa', defaultHost: 'localhost', requiresAuth: true, premium: true, jdbc: true },
15
22
  ];
16
23
  // ── Styles ────────────────────────────────────────────────────
17
24
  const S = {
@@ -103,7 +110,7 @@ const S = {
103
110
  },
104
111
  };
105
112
  // ── Component ────────────────────────────────────────────────
106
- export default function ReconfigPanel({ apiEndpoint = '/api/setup/reconfig', detectEndpoint = '/api/setup/detect-modules', t = (k) => k, onDbChanged, onModulesChanged, showSeedOption = true, onSeedRequested, }) {
113
+ export default function ReconfigPanel({ apiEndpoint = '/api/setup/reconfig', detectEndpoint = '/api/setup/detect-modules', jarEndpoint = '/api/setup/upload-jar', t = (k) => k, onDbChanged, onModulesChanged, showSeedOption = true, onSeedRequested, }) {
107
114
  // --- State ---
108
115
  const [loading, setLoading] = useState(true);
109
116
  const [currentDialect, setCurrentDialect] = useState('');
@@ -120,6 +127,11 @@ export default function ReconfigPanel({ apiEndpoint = '/api/setup/reconfig', det
120
127
  const [dbMessage, setDbMessage] = useState(null);
121
128
  const [wantSeed, setWantSeed] = useState(true);
122
129
  const [seeding, setSeeding] = useState(false);
130
+ // JAR upload
131
+ const [jarUploading, setJarUploading] = useState(false);
132
+ const [jarMessage, setJarMessage] = useState(null);
133
+ const [jarFiles, setJarFiles] = useState([]);
134
+ const [jdbcStatus, setJdbcStatus] = useState([]);
123
135
  // Module saving
124
136
  const [moduleSaving, setModuleSaving] = useState(false);
125
137
  const [moduleMessage, setModuleMessage] = useState(null);
@@ -154,6 +166,18 @@ export default function ReconfigPanel({ apiEndpoint = '/api/setup/reconfig', det
154
166
  password: '',
155
167
  });
156
168
  }
169
+ // Load JAR status
170
+ try {
171
+ const jarRes = await fetch(jarEndpoint);
172
+ const jarData = await jarRes.json();
173
+ if (jarData.ok) {
174
+ setJarFiles(jarData.jars || []);
175
+ setJdbcStatus(jarData.dialects || []);
176
+ }
177
+ }
178
+ catch {
179
+ // JAR endpoint may not exist — ignore
180
+ }
157
181
  }
158
182
  catch {
159
183
  // ignore
@@ -161,8 +185,72 @@ export default function ReconfigPanel({ apiEndpoint = '/api/setup/reconfig', det
161
185
  finally {
162
186
  setLoading(false);
163
187
  }
164
- }, [apiEndpoint, detectEndpoint]);
188
+ }, [apiEndpoint, detectEndpoint, jarEndpoint]);
165
189
  useEffect(() => { loadConfig(); }, [loadConfig]);
190
+ // --- JAR upload ---
191
+ const handleJarUpload = async (e) => {
192
+ const file = e.target.files?.[0];
193
+ if (!file)
194
+ return;
195
+ if (!file.name.endsWith('.jar')) {
196
+ setJarMessage({ type: 'error', text: 'Le fichier doit etre un .jar' });
197
+ return;
198
+ }
199
+ setJarUploading(true);
200
+ setJarMessage(null);
201
+ try {
202
+ const formData = new FormData();
203
+ formData.append('jar', file);
204
+ const res = await fetch(jarEndpoint, { method: 'POST', body: formData });
205
+ const result = await res.json();
206
+ if (result.ok) {
207
+ const msg = result.replaced
208
+ ? `${result.fileName} uploade (remplace ${result.replaced})`
209
+ : `${result.fileName} uploade`;
210
+ setJarMessage({ type: 'success', text: result.dialect ? `${msg} — dialect: ${result.dialect}` : msg });
211
+ // Refresh JAR list
212
+ const jarRes = await fetch(jarEndpoint);
213
+ const jarData = await jarRes.json();
214
+ if (jarData.ok) {
215
+ setJarFiles(jarData.jars || []);
216
+ setJdbcStatus(jarData.dialects || []);
217
+ }
218
+ }
219
+ else {
220
+ setJarMessage({ type: 'error', text: result.error || 'Erreur upload' });
221
+ }
222
+ }
223
+ catch {
224
+ setJarMessage({ type: 'error', text: 'Erreur reseau' });
225
+ }
226
+ finally {
227
+ setJarUploading(false);
228
+ // Reset input
229
+ e.target.value = '';
230
+ }
231
+ };
232
+ const handleJarDelete = async (fileName) => {
233
+ try {
234
+ const res = await fetch(jarEndpoint, {
235
+ method: 'DELETE',
236
+ headers: { 'Content-Type': 'application/json' },
237
+ body: JSON.stringify({ fileName }),
238
+ });
239
+ const result = await res.json();
240
+ if (result.ok) {
241
+ setJarMessage({ type: 'success', text: `${fileName} supprime` });
242
+ const jarRes = await fetch(jarEndpoint);
243
+ const jarData = await jarRes.json();
244
+ if (jarData.ok) {
245
+ setJarFiles(jarData.jars || []);
246
+ setJdbcStatus(jarData.dialects || []);
247
+ }
248
+ }
249
+ }
250
+ catch {
251
+ // ignore
252
+ }
253
+ };
166
254
  // --- Dialect change ---
167
255
  const handleDialectChange = (dialect) => {
168
256
  setSelectedDialect(dialect);
@@ -323,5 +411,21 @@ export default function ReconfigPanel({ apiEndpoint = '/api/setup/reconfig', det
323
411
  ...S.moduleCard(isActive, isRequired || !isInstalled),
324
412
  opacity: isInstalled ? 1 : 0.5,
325
413
  }, 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' })] })] })] }));
414
+ }) }), _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: "\u2615" }), " Drivers JDBC"] }), _jsx("div", { style: S.sectionDesc, children: "Uploadez les fichiers JAR des drivers JDBC pour les bases de donnees enterprise. Les dialects JDBC (HyperSQL, Oracle, DB2, SAP HANA, Sybase) utilisent un bridge Java pour se connecter." }), jarMessage && (_jsx("div", { style: S.alert(jarMessage.type), children: jarMessage.text })), jdbcStatus.length > 0 && (_jsx("div", { style: { marginBottom: 16 }, children: _jsxs("table", { style: { width: '100%', borderCollapse: 'collapse', fontSize: 13 }, children: [_jsx("thead", { children: _jsxs("tr", { style: { borderBottom: '2px solid #e5e7eb', textAlign: 'left' }, children: [_jsx("th", { style: { padding: '8px 12px', fontWeight: 600 }, children: "Dialect" }), _jsx("th", { style: { padding: '8px 12px', fontWeight: 600 }, children: "Statut" }), _jsx("th", { style: { padding: '8px 12px', fontWeight: 600 }, children: "Fichier JAR" }), _jsx("th", { style: { padding: '8px 12px', fontWeight: 600 } })] }) }), _jsx("tbody", { children: jdbcStatus.map((s) => (_jsxs("tr", { style: { borderBottom: '1px solid #f3f4f6' }, children: [_jsx("td", { style: { padding: '8px 12px', fontWeight: 500 }, children: s.label }), _jsx("td", { style: { padding: '8px 12px' }, children: s.hasJar ? (_jsx("span", { style: { color: '#059669', fontWeight: 600, fontSize: 12 }, children: "Pret" })) : (_jsx("span", { style: { color: '#9ca3af', fontSize: 12 }, children: "Non installe" })) }), _jsx("td", { style: { padding: '8px 12px', fontSize: 12, color: '#6b7280', fontFamily: 'monospace' }, children: s.jarFile || '' }), _jsx("td", { style: { padding: '8px 12px' }, children: s.hasJar && s.jarFile && (_jsx("button", { style: { ...S.btn('danger'), padding: '3px 8px', fontSize: 11 }, onClick: () => handleJarDelete(s.jarFile), children: "Supprimer" })) })] }, s.dialect))) })] }) })), _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 12 }, children: [_jsxs("label", { style: {
415
+ ...S.btn('primary'),
416
+ cursor: jarUploading ? 'wait' : 'pointer',
417
+ opacity: jarUploading ? 0.6 : 1,
418
+ }, children: [jarUploading ? 'Upload en cours...' : 'Uploader un fichier .jar', _jsx("input", { type: "file", accept: ".jar", onChange: handleJarUpload, disabled: jarUploading, style: { display: 'none' } })] }), _jsx("span", { style: { fontSize: 12, color: '#9ca3af' }, children: "Formats acceptes : hsqldb*.jar, ojdbc*.jar, db2jcc*.jar, ngdbc*.jar, jconn*.jar" })] })] }), _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: {
419
+ ...S.dialectCard(selectedDialect === d.key && !d.premium),
420
+ ...(d.premium ? { opacity: 0.45, cursor: 'not-allowed', filter: 'grayscale(0.5)' } : {}),
421
+ }, onClick: () => !d.premium && handleDialectChange(d.key), title: d.premium ? `${d.name} — disponible en version Premium` : d.name, 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.premium && (_jsx("div", { style: {
422
+ fontSize: 9,
423
+ fontWeight: 700,
424
+ color: '#b45309',
425
+ backgroundColor: '#fef3c7',
426
+ padding: '1px 5px',
427
+ borderRadius: 4,
428
+ marginTop: 3,
429
+ display: 'inline-block',
430
+ }, children: "Premium" }))] })] }, 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
431
  }
package/dist/index.d.ts CHANGED
@@ -11,6 +11,7 @@ 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
13
  export { createReconfigHandlers } from './api/reconfig.route';
14
+ export { createUploadJarHandlers } from './api/upload-jar.route';
14
15
  export { default as ReconfigPanel } from './components/ReconfigPanel';
15
16
  export { setupMenuContribution } from './lib/menu';
16
17
  export type { DialectType, DialectInfo, DbConfig, InstallConfig, SeedOptions, SeedDefinition, MostaSetupConfig, ModuleDefinition, } from './types/index';
package/dist/index.js CHANGED
@@ -17,6 +17,7 @@ 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
19
  export { createReconfigHandlers } from './api/reconfig.route';
20
+ export { createUploadJarHandlers } from './api/upload-jar.route';
20
21
  // Components
21
22
  export { default as ReconfigPanel } from './components/ReconfigPanel';
22
23
  // Menu contribution
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mostajs/setup",
3
- "version": "1.1.4",
3
+ "version": "1.3.0",
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",
@@ -47,6 +47,11 @@
47
47
  "types": "./dist/api/reconfig.route.d.ts",
48
48
  "import": "./dist/api/reconfig.route.js",
49
49
  "default": "./dist/api/reconfig.route.js"
50
+ },
51
+ "./api/upload-jar": {
52
+ "types": "./dist/api/upload-jar.route.d.ts",
53
+ "import": "./dist/api/upload-jar.route.js",
54
+ "default": "./dist/api/upload-jar.route.js"
50
55
  }
51
56
  },
52
57
  "files": [
@@ -76,7 +81,7 @@
76
81
  "prepublishOnly": "npm run build"
77
82
  },
78
83
  "dependencies": {
79
- "@mostajs/orm": "^1.0.0",
84
+ "@mostajs/orm": "^1.3.0",
80
85
  "bcryptjs": "^2.4.3"
81
86
  },
82
87
  "devDependencies": {