depository-deploy 1.0.30 → 1.0.41

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.
@@ -317,6 +317,60 @@ kill_port_holder "$API_PORT"
317
317
  kill_port_holder "$AUTH_PORT"
318
318
  kill_port_holder "$GATHERER_PORT"
319
319
 
320
+ # ---- Map service name → executable path (for direct-run error capture) ----
321
+ get_svc_exe() {
322
+ case "$1" in
323
+ depository-auth) echo "$INSTALL_DIR/auth/DEPOSITORY.API.OATH" ;;
324
+ depository-api) echo "$INSTALL_DIR/api/DEPOSITORY.API.REST" ;;
325
+ depository-worker) echo "$INSTALL_DIR/worker/DEPOSITORY.CORE.WORKER" ;;
326
+ depository-gatherer) echo "$INSTALL_DIR/gatherer/DEPOSITORY.CORE.GATHERER" ;;
327
+ depository-digestor) echo "$INSTALL_DIR/digestor/DEPOSITORY.CORE.DIGESTOR" ;;
328
+ esac
329
+ }
330
+
331
+ # Capture and print detailed startup error for a failed service
332
+ capture_svc_error() {
333
+ local svc="$1"
334
+ warn " --- journalctl output (last 50 lines) ---"
335
+ journalctl -u "$svc" -n 50 --no-pager 2>/dev/null | while IFS= read -r line; do
336
+ warn " $line"
337
+ done
338
+
339
+ # Also try running the executable directly for 10 seconds to surface startup exceptions
340
+ local svc_exe
341
+ svc_exe="$(get_svc_exe "$svc")"
342
+ if [ -n "$svc_exe" ] && [ -f "$svc_exe" ]; then
343
+ warn " --- Running exe directly to capture startup error (10 s) ---"
344
+ local tmp_out="/tmp/${svc}-startup-$$.log"
345
+ local svc_dir
346
+ svc_dir="$(dirname "$svc_exe")"
347
+ # Run as depository user if available so appsettings.json is readable
348
+ if id -u depository &>/dev/null; then
349
+ timeout 10 sudo -u depository "$svc_exe" > "$tmp_out" 2>&1 || true
350
+ else
351
+ timeout 10 "$svc_exe" > "$tmp_out" 2>&1 || true
352
+ fi
353
+ if [ -s "$tmp_out" ]; then
354
+ local total_lines; total_lines=$(wc -l < "$tmp_out")
355
+ warn " --- Startup output: ${total_lines} line(s) captured (showing last 80) ---"
356
+ tail -n 80 "$tmp_out" | while IFS= read -r line; do
357
+ warn " $line"
358
+ done
359
+ else
360
+ warn " (no output captured from direct run)"
361
+ fi
362
+ rm -f "$tmp_out"
363
+ # Also show startup-error.log written by the .NET app itself
364
+ local err_log="$svc_dir/startup-error.log"
365
+ if [ -f "$err_log" ]; then
366
+ warn " --- $err_log ---"
367
+ while IFS= read -r line; do
368
+ warn " $line"
369
+ done < "$err_log"
370
+ fi
371
+ fi
372
+ }
373
+
320
374
  # ---- Enable and start services ----
321
375
  info "Enabling and starting services..."
322
376
  systemctl daemon-reload
@@ -331,18 +385,12 @@ for svc in $SERVICES; do
331
385
  echo -e " ${GREEN}[OK]${NC} Started: $svc"
332
386
  else
333
387
  warn " FAILED to start: $svc (did not reach active state)"
334
- warn " Run: journalctl -u $svc -n 30 --no-pager"
335
- journalctl -u "$svc" -n 8 --no-pager 2>/dev/null | grep -iE 'error|fail|exception|unhandled|crit' | head -5 | while read -r line; do
336
- warn " $line"
337
- done
388
+ capture_svc_error "$svc"
338
389
  START_FAILED="$START_FAILED $svc"
339
390
  fi
340
391
  else
341
392
  warn " FAILED to start: $svc"
342
- warn " Run: journalctl -u $svc -n 30 --no-pager"
343
- journalctl -u "$svc" -n 8 --no-pager 2>/dev/null | grep -iE 'error|fail|exception|unhandled|crit' | head -5 | while read -r line; do
344
- warn " $line"
345
- done
393
+ capture_svc_error "$svc"
346
394
  START_FAILED="$START_FAILED $svc"
347
395
  fi
348
396
  done
@@ -262,22 +262,13 @@ $svcDefs = @(
262
262
  )
263
263
 
264
264
  foreach ($def in $svcDefs) {
265
- # Create a wrapper batch that sets the working directory (so appsettings.json is found)
266
- # and optionally sets environment variables (e.g. ASPNETCORE_URLS for port binding).
267
- $exeDir = Split-Path -Parent $def.Exe
268
- $wrapperPath = Join-Path $exeDir "start-service.bat"
269
- $batLines = "@echo off"
270
- $batLines += "`r`ncd /d `"$exeDir`""
271
- if ($def.Env -ne "") {
272
- $batLines += "`r`nset $($def.Env)"
273
- }
274
- $batLines += "`r`n`"$($def.Exe)`""
275
- Set-Content -Path $wrapperPath -Value $batLines -Encoding ASCII
276
-
277
- & sc.exe create $def.Name binPath= "`"$wrapperPath`"" start= auto DisplayName= $def.Desc | Out-Null
265
+ # Register the exe directly as the Windows Service binary.
266
+ # UseWindowsService() in each app handles the SCM protocol and sets the content root
267
+ # to the exe's directory so appsettings.json is found automatically.
268
+ & sc.exe create $def.Name binPath= "`"$($def.Exe)`"" start= auto DisplayName= $def.Desc | Out-Null
278
269
  & sc.exe description $def.Name $def.Desc | Out-Null
279
270
 
280
- # Also set environment via registry as a fallback
271
+ # Pass environment variables (e.g. ASPNETCORE_URLS) via the service registry entry
281
272
  if ($def.Env -ne "") {
282
273
  $regPath = "HKLM:\SYSTEM\CurrentControlSet\Services\$($def.Name)"
283
274
  Set-ItemProperty -Path $regPath -Name "Environment" -Value @($def.Env) -Type MultiString -ErrorAction SilentlyContinue
@@ -356,15 +347,25 @@ foreach ($def in $svcDefs) {
356
347
  -RedirectStandardOutput "$env:TEMP\$($def.Name)-stdout.txt" `
357
348
  -RedirectStandardError "$env:TEMP\$($def.Name)-stderr.txt" `
358
349
  -NoNewWindow -PassThru -ErrorAction SilentlyContinue
359
- Start-Sleep -Seconds 3
350
+ Start-Sleep -Seconds 10
360
351
  if (-not $proc.HasExited) { $proc.Kill() }
361
352
  $stdout = Get-Content "$env:TEMP\$($def.Name)-stdout.txt" -ErrorAction SilentlyContinue
362
353
  $stderr = Get-Content "$env:TEMP\$($def.Name)-stderr.txt" -ErrorAction SilentlyContinue
363
- $combined = @($stdout; $stderr) | Where-Object { $_ -match 'error|fail|exception|unhandled|crit' -and $_ }
354
+ $combined = @($stdout; $stderr) | Where-Object { $_ -and $_.Trim() }
364
355
  if ($combined) {
365
- $combined | Select-Object -First 8 | ForEach-Object { Write-Warn " $_" }
356
+ $total = $combined.Count
357
+ Write-Warn " --- Startup output: $total line(s) captured (showing last 80) ---"
358
+ # Show LAST 80 lines — the actual crash/exception always appears at the end,
359
+ # after EF Core model-validation warnings that fill the beginning.
360
+ $combined | Select-Object -Last 80 | ForEach-Object { Write-Warn " $_" }
366
361
  } else {
367
- Write-Warn " (no error output captured -- check Event Viewer)"
362
+ Write-Warn " (no output captured -- check Event Viewer > Windows Logs > Application)"
363
+ }
364
+ # Also surface startup-error.log written by the .NET app itself
365
+ $errLog = Join-Path $exeDir "startup-error.log"
366
+ if (Test-Path $errLog) {
367
+ Write-Warn " --- $errLog ---"
368
+ Get-Content $errLog -ErrorAction SilentlyContinue | ForEach-Object { Write-Warn " $_" }
368
369
  }
369
370
  } catch {
370
371
  Write-Warn " Could not run exe directly: $_"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "depository-deploy",
3
- "version": "1.0.30",
3
+ "version": "1.0.41",
4
4
  "description": "Depository document management system – deployment wizard and installers",
5
5
  "license": "UNLICENSED",
6
6
  "publishConfig": {
@@ -25,9 +25,9 @@
25
25
  "scripts/publish.mjs"
26
26
  ],
27
27
  "optionalDependencies": {
28
- "depository-deploy-linux": "1.0.30",
29
- "depository-deploy-macos": "1.0.30",
30
- "depository-deploy-windows": "1.0.30"
28
+ "depository-deploy-linux": "1.0.41",
29
+ "depository-deploy-macos": "1.0.41",
30
+ "depository-deploy-windows": "1.0.41"
31
31
  },
32
32
  "scripts": {
33
33
  "start": "node wizard-server.mjs"
package/wizard-server.mjs CHANGED
@@ -16,7 +16,7 @@
16
16
  // GET /api/install-stream/:id → SSE log stream
17
17
  // =============================================================
18
18
 
19
- import { createServer } from 'http';
19
+ import { createServer, request as httpRequest } from 'http';
20
20
  import { readFileSync, writeFileSync, existsSync, statSync } from 'fs';
21
21
  import { spawn, execSync } from 'child_process';
22
22
  import { join, dirname } from 'path';
@@ -452,6 +452,64 @@ const server = createServer((req, res) => {
452
452
  return;
453
453
  }
454
454
 
455
+ // ── Create first admin user: POST /api/create-admin { authPort, user }
456
+ if (req.method === 'POST' && url.pathname === '/api/create-admin') {
457
+ let body = '';
458
+ req.on('data', d => body += d);
459
+ req.on('end', async () => {
460
+ try {
461
+ const { authPort, user } = JSON.parse(body);
462
+ const port = Number(authPort) || 5238;
463
+ const payload = JSON.stringify(user);
464
+
465
+ // Retry up to 5 times with 3 s gaps — auth service may still be starting
466
+ let lastErr = 'no attempts made';
467
+ for (let attempt = 0; attempt < 5; attempt++) {
468
+ try {
469
+ const result = await new Promise((resolve, reject) => {
470
+ const opts = {
471
+ hostname: '127.0.0.1', port,
472
+ path: '/api/ApplicationUser/Register',
473
+ method: 'POST',
474
+ headers: {
475
+ 'Content-Type': 'application/json',
476
+ 'Content-Length': Buffer.byteLength(payload),
477
+ },
478
+ };
479
+ const r2 = httpRequest(opts, resp => {
480
+ let data = '';
481
+ resp.on('data', d => data += d);
482
+ resp.on('end', () => resolve({ status: resp.statusCode, body: data }));
483
+ });
484
+ r2.on('error', reject);
485
+ r2.setTimeout(8000, () => { r2.destroy(); reject(new Error('timeout')); });
486
+ r2.write(payload);
487
+ r2.end();
488
+ });
489
+
490
+ if (result.status >= 200 && result.status < 300) {
491
+ res.writeHead(200, { 'Content-Type': 'application/json' });
492
+ res.end(JSON.stringify({ ok: true }));
493
+ return;
494
+ }
495
+ lastErr = `HTTP ${result.status}: ${result.body}`;
496
+ break; // non-2xx from auth service — no point retrying
497
+ } catch (e) {
498
+ lastErr = e.message;
499
+ if (attempt < 4) await new Promise(r => setTimeout(r, 3000));
500
+ }
501
+ }
502
+
503
+ res.writeHead(200, { 'Content-Type': 'application/json' });
504
+ res.end(JSON.stringify({ ok: false, error: lastErr }));
505
+ } catch (e) {
506
+ res.writeHead(400, { 'Content-Type': 'application/json' });
507
+ res.end(JSON.stringify({ ok: false, error: e.message }));
508
+ }
509
+ });
510
+ return;
511
+ }
512
+
455
513
  res.writeHead(404); res.end('Not found');
456
514
  });
457
515
 
package/wizard.html CHANGED
@@ -895,8 +895,41 @@ input.err { border-color: var(--err) !important; box-shadow: 0 0 0 3px rgba(220,
895
895
  </div>
896
896
  </div>
897
897
 
898
- <!-- Step 6: Review -->
898
+ <!-- Step 6: Admin User -->
899
899
  <div class="step" id="step-6">
900
+ <div class="step-title" data-i18n="admin_title">Administrator Account</div>
901
+ <div class="step-sub" data-i18n="admin_sub">Create the first admin user. This account will be used to log in to Depository after installation.</div>
902
+ <div class="field-row">
903
+ <div class="field">
904
+ <label data-i18n="admin_email_lbl">Email</label>
905
+ <input type="email" id="admin_email" placeholder="admin@yourcompany.com">
906
+ </div>
907
+ <div class="field">
908
+ <label data-i18n="admin_name_lbl">Full Name</label>
909
+ <input type="text" id="admin_name" placeholder="Admin User">
910
+ </div>
911
+ </div>
912
+ <div class="field-row">
913
+ <div class="field">
914
+ <label data-i18n="admin_pass_lbl">Password</label>
915
+ <input type="password" id="admin_pass" placeholder="••••••••">
916
+ </div>
917
+ <div class="field">
918
+ <label data-i18n="admin_pass2_lbl">Confirm Password</label>
919
+ <input type="password" id="admin_pass2" placeholder="••••••••">
920
+ </div>
921
+ </div>
922
+ <div class="field-row">
923
+ <div class="field">
924
+ <label data-i18n="admin_org_lbl">Organization</label>
925
+ <input type="text" id="admin_org" placeholder="Your Company">
926
+ </div>
927
+ </div>
928
+ <p class="field-note" data-i18n="admin_note">This admin account will be created automatically after installation completes.</p>
929
+ </div>
930
+
931
+ <!-- Step 7: Review -->
932
+ <div class="step" id="step-7">
900
933
  <div class="step-title" data-i18n="review_title">Review &amp; Download</div>
901
934
  <div class="step-sub" data-i18n="review_sub">Check your settings below, then download the configuration file and run the installer.</div>
902
935
  <div class="review-grid" id="reviewGrid"></div>
@@ -984,7 +1017,7 @@ input.err { border-color: var(--err) !important; box-shadow: 0 0 0 3px rgba(220,
984
1017
  const T = {
985
1018
  en: {
986
1019
  wizard_subtitle:"Setup Wizard", step_of:"Step {0} of {1}",
987
- steps:["Welcome","Database","User Accounts","Network & Ports","Folders","Active Directory","Download"],
1020
+ steps:["Welcome","Database","User Accounts","Network & Ports","Folders","Active Directory","Admin User","Download"],
988
1021
  welcome_title:"Welcome to Depository Setup",
989
1022
  welcome_hero_sub:"This wizard collects your settings and generates a ready-to-use configuration file for your server.",
990
1023
  welcome_before:"Before you begin, make sure you have:",
@@ -1079,10 +1112,25 @@ const T = {
1079
1112
  update_done:"Updated! Restart the wizard server to apply.",
1080
1113
  update_failed:"Update failed. Run manually: npm install -g depository-deploy@latest",
1081
1114
  update_current:"v{0}",
1115
+ admin_title:"Administrator Account",
1116
+ admin_sub:"Create the first admin user. This account will be used to log in to Depository after installation.",
1117
+ admin_email_lbl:"Email",
1118
+ admin_pass_lbl:"Password",
1119
+ admin_pass2_lbl:"Confirm Password",
1120
+ admin_name_lbl:"Full Name",
1121
+ admin_org_lbl:"Organization",
1122
+ admin_note:"This admin account will be created automatically after installation completes.",
1123
+ err_admin_email:"Admin email is required.",
1124
+ err_admin_pass:"Password is required.",
1125
+ err_admin_pass2:"Passwords do not match.",
1126
+ err_admin_name:"Full name is required.",
1127
+ admin_creating:"Creating admin user…",
1128
+ admin_created:"✓ Admin user created successfully.",
1129
+ admin_failed:"Admin user creation failed — you can create the account manually via the auth API.",
1082
1130
  },
1083
1131
  tr: {
1084
1132
  wizard_subtitle:"Kurulum Sihirbazı", step_of:"Adım {0} / {1}",
1085
- steps:["Hoş Geldiniz","Veritabanı","Kullanıcı Hesapları","Ağ ve Portlar","Klasörler","Active Directory","İndir"],
1133
+ steps:["Hoş Geldiniz","Veritabanı","Kullanıcı Hesapları","Ağ ve Portlar","Klasörler","Active Directory","Yönetici Kullanıcı","İndir"],
1086
1134
  welcome_title:"Depository Kurulumuna Hoş Geldiniz",
1087
1135
  welcome_hero_sub:"Bu sihirbaz ayarlarınızı toplayarak sunucunuz için hazır bir yapılandırma dosyası oluşturur.",
1088
1136
  welcome_before:"Başlamadan önce şunlara sahip olduğunuzdan emin olun:",
@@ -1177,10 +1225,25 @@ const T = {
1177
1225
  update_done:"Güncellendi! Uygulamak için sihirbaz sunucusunu yeniden başlatın.",
1178
1226
  update_failed:"Güncelleme başarısız. Manuel çalıştırın: npm install -g depository-deploy@latest",
1179
1227
  update_current:"v{0}",
1228
+ admin_title:"Yönetici Hesabı",
1229
+ admin_sub:"İlk yönetici kullanıcıyı oluşturun. Bu hesap, kurulum sonrasında Depository'ye giriş yapmak için kullanılacaktır.",
1230
+ admin_email_lbl:"E-posta",
1231
+ admin_pass_lbl:"Şifre",
1232
+ admin_pass2_lbl:"Şifreyi Onayla",
1233
+ admin_name_lbl:"Tam Ad",
1234
+ admin_org_lbl:"Organizasyon",
1235
+ admin_note:"Bu yönetici hesabı, kurulum tamamlandıktan sonra otomatik olarak oluşturulacaktır.",
1236
+ err_admin_email:"Yönetici e-postası zorunludur.",
1237
+ err_admin_pass:"Şifre zorunludur.",
1238
+ err_admin_pass2:"Şifreler eşleşmiyor.",
1239
+ err_admin_name:"Tam ad zorunludur.",
1240
+ admin_creating:"Yönetici kullanıcı oluşturuluyor…",
1241
+ admin_created:"✓ Yönetici kullanıcı başarıyla oluşturuldu.",
1242
+ admin_failed:"Yönetici kullanıcı oluşturulamadı — auth API üzerinden manuel olarak oluşturabilirsiniz.",
1180
1243
  },
1181
1244
  fr: {
1182
1245
  wizard_subtitle:"Assistant d'installation", step_of:"Étape {0} sur {1}",
1183
- steps:["Bienvenue","Base de données","Comptes utilisateurs","Réseau & Ports","Dossiers","Active Directory","Télécharger"],
1246
+ steps:["Bienvenue","Base de données","Comptes utilisateurs","Réseau & Ports","Dossiers","Active Directory","Compte Admin","Télécharger"],
1184
1247
  welcome_title:"Bienvenue dans l'assistant Depository",
1185
1248
  welcome_hero_sub:"Cet assistant collecte vos paramètres et génère un fichier de configuration prêt à l'emploi.",
1186
1249
  welcome_before:"Avant de commencer, assurez-vous d'avoir :",
@@ -1275,10 +1338,25 @@ const T = {
1275
1338
  update_done:"Mis à jour ! Redémarrez le serveur du wizard pour appliquer.",
1276
1339
  update_failed:"Échec de la mise à jour. Exécutez manuellement : npm install -g depository-deploy@latest",
1277
1340
  update_current:"v{0}",
1341
+ admin_title:"Compte Administrateur",
1342
+ admin_sub:"Créez le premier utilisateur administrateur. Ce compte sera utilisé pour se connecter à Depository après l'installation.",
1343
+ admin_email_lbl:"E-mail",
1344
+ admin_pass_lbl:"Mot de passe",
1345
+ admin_pass2_lbl:"Confirmer le mot de passe",
1346
+ admin_name_lbl:"Nom complet",
1347
+ admin_org_lbl:"Organisation",
1348
+ admin_note:"Ce compte administrateur sera créé automatiquement à la fin de l'installation.",
1349
+ err_admin_email:"L'e-mail administrateur est obligatoire.",
1350
+ err_admin_pass:"Le mot de passe est obligatoire.",
1351
+ err_admin_pass2:"Les mots de passe ne correspondent pas.",
1352
+ err_admin_name:"Le nom complet est obligatoire.",
1353
+ admin_creating:"Création de l'utilisateur admin…",
1354
+ admin_created:"✓ Utilisateur admin créé avec succès.",
1355
+ admin_failed:"Échec de la création de l'admin — créez le compte manuellement via l'API auth.",
1278
1356
  },
1279
1357
  de: {
1280
1358
  wizard_subtitle:"Installations-Assistent", step_of:"Schritt {0} von {1}",
1281
- steps:["Willkommen","Datenbank","Benutzerkonten","Netzwerk & Ports","Ordner","Active Directory","Herunterladen"],
1359
+ steps:["Willkommen","Datenbank","Benutzerkonten","Netzwerk & Ports","Ordner","Active Directory","Admin-Benutzer","Herunterladen"],
1282
1360
  welcome_title:"Willkommen beim Depository-Setup",
1283
1361
  welcome_hero_sub:"Dieser Assistent erfasst Ihre Einstellungen und erstellt eine fertige Konfigurationsdatei für Ihren Server.",
1284
1362
  welcome_before:"Stellen Sie vor Beginn sicher, dass Sie Folgendes haben:",
@@ -1373,10 +1451,25 @@ const T = {
1373
1451
  update_done:"Aktualisiert! Starten Sie den Wizard-Server neu.",
1374
1452
  update_failed:"Aktualisierung fehlgeschlagen. Manuell ausführen: npm install -g depository-deploy@latest",
1375
1453
  update_current:"v{0}",
1454
+ admin_title:"Administrator-Konto",
1455
+ admin_sub:"Erstellen Sie den ersten Admin-Benutzer. Dieses Konto wird nach der Installation zur Anmeldung bei Depository verwendet.",
1456
+ admin_email_lbl:"E-Mail",
1457
+ admin_pass_lbl:"Passwort",
1458
+ admin_pass2_lbl:"Passwort bestätigen",
1459
+ admin_name_lbl:"Vollständiger Name",
1460
+ admin_org_lbl:"Organisation",
1461
+ admin_note:"Dieses Admin-Konto wird automatisch nach Abschluss der Installation erstellt.",
1462
+ err_admin_email:"Admin-E-Mail ist erforderlich.",
1463
+ err_admin_pass:"Passwort ist erforderlich.",
1464
+ err_admin_pass2:"Passwörter stimmen nicht überein.",
1465
+ err_admin_name:"Vollständiger Name ist erforderlich.",
1466
+ admin_creating:"Admin-Benutzer wird erstellt…",
1467
+ admin_created:"✓ Admin-Benutzer erfolgreich erstellt.",
1468
+ admin_failed:"Admin-Benutzer konnte nicht erstellt werden — erstellen Sie das Konto manuell über die Auth-API.",
1376
1469
  },
1377
1470
  es: {
1378
1471
  wizard_subtitle:"Asistente de instalación", step_of:"Paso {0} de {1}",
1379
- steps:["Bienvenido","Base de datos","Cuentas de usuario","Red y puertos","Carpetas","Active Directory","Descargar"],
1472
+ steps:["Bienvenido","Base de datos","Cuentas de usuario","Red y puertos","Carpetas","Active Directory","Usuario Admin","Descargar"],
1380
1473
  welcome_title:"Bienvenido a la configuración de Depository",
1381
1474
  welcome_hero_sub:"Este asistente recopila sus ajustes y genera un archivo de configuración listo para usar en su servidor.",
1382
1475
  welcome_before:"Antes de comenzar, asegúrese de tener:",
@@ -1471,10 +1564,25 @@ const T = {
1471
1564
  update_done:"Actualizado. Reinicie el servidor del wizard para aplicar.",
1472
1565
  update_failed:"Actualización fallida. Ejecute manualmente: npm install -g depository-deploy@latest",
1473
1566
  update_current:"v{0}",
1567
+ admin_title:"Cuenta de Administrador",
1568
+ admin_sub:"Cree el primer usuario administrador. Esta cuenta se usará para iniciar sesión en Depository tras la instalación.",
1569
+ admin_email_lbl:"Correo electrónico",
1570
+ admin_pass_lbl:"Contraseña",
1571
+ admin_pass2_lbl:"Confirmar contraseña",
1572
+ admin_name_lbl:"Nombre completo",
1573
+ admin_org_lbl:"Organización",
1574
+ admin_note:"Esta cuenta de administrador se creará automáticamente al finalizar la instalación.",
1575
+ err_admin_email:"El correo del administrador es obligatorio.",
1576
+ err_admin_pass:"La contraseña es obligatoria.",
1577
+ err_admin_pass2:"Las contraseñas no coinciden.",
1578
+ err_admin_name:"El nombre completo es obligatorio.",
1579
+ admin_creating:"Creando usuario administrador…",
1580
+ admin_created:"✓ Usuario administrador creado correctamente.",
1581
+ admin_failed:"No se pudo crear el usuario admin — créelo manualmente a través de la API de autenticación.",
1474
1582
  },
1475
1583
  ar: {
1476
1584
  wizard_subtitle:"معالج الإعداد", step_of:"الخطوة {0} من {1}",
1477
- steps:["مرحباً","قاعدة البيانات","حسابات المستخدمين","الشبكة والمنافذ","المجلدات","Active Directory","تنزيل"],
1585
+ steps:["مرحباً","قاعدة البيانات","حسابات المستخدمين","الشبكة والمنافذ","المجلدات","Active Directory","مستخدم مسؤول","تنزيل"],
1478
1586
  welcome_title:"مرحباً بك في إعداد Depository",
1479
1587
  welcome_hero_sub:"يجمع هذا المعالج إعداداتك ويُنشئ ملف تهيئة جاهزاً للاستخدام على خادمك.",
1480
1588
  welcome_before:"قبل البدء، تأكد من توفر ما يلي:",
@@ -1569,6 +1677,21 @@ const T = {
1569
1677
  update_done:"تم التحديث! أعد تشغيل خادم المعالج للتطبيق.",
1570
1678
  update_failed:"فشل التحديث. شغّل يدويًا: npm install -g depository-deploy@latest",
1571
1679
  update_current:"v{0}",
1680
+ admin_title:"حساب المسؤول",
1681
+ admin_sub:"أنشئ أول مستخدم مسؤول. سيُستخدم هذا الحساب لتسجيل الدخول إلى Depository بعد التثبيت.",
1682
+ admin_email_lbl:"البريد الإلكتروني",
1683
+ admin_pass_lbl:"كلمة المرور",
1684
+ admin_pass2_lbl:"تأكيد كلمة المرور",
1685
+ admin_name_lbl:"الاسم الكامل",
1686
+ admin_org_lbl:"المؤسسة",
1687
+ admin_note:"سيتم إنشاء حساب المسؤول تلقائيًا بعد اكتمال التثبيت.",
1688
+ err_admin_email:"البريد الإلكتروني للمسؤول مطلوب.",
1689
+ err_admin_pass:"كلمة المرور مطلوبة.",
1690
+ err_admin_pass2:"كلمتا المرور غير متطابقتين.",
1691
+ err_admin_name:"الاسم الكامل مطلوب.",
1692
+ admin_creating:"جارٍ إنشاء المستخدم المسؤول…",
1693
+ admin_created:"✓ تم إنشاء المستخدم المسؤول بنجاح.",
1694
+ admin_failed:"فشل إنشاء المستخدم المسؤول — يمكنك إنشاء الحساب يدويًا عبر API المصادقة.",
1572
1695
  },
1573
1696
  };
1574
1697
 
@@ -1639,7 +1762,7 @@ function showStep(n) {
1639
1762
  renderSidebar();
1640
1763
  updateNav();
1641
1764
  document.getElementById('errBanner').textContent = '';
1642
- if (n === 6) populateReview();
1765
+ if (n === 7) populateReview();
1643
1766
  if (n === 4) checkPrevInstall();
1644
1767
  }
1645
1768
 
@@ -1727,6 +1850,12 @@ function validateStep(s) {
1727
1850
  return {msg:t('err_overwrite'), field:'overwriteOk'};
1728
1851
  }
1729
1852
  break;
1853
+ case 6:
1854
+ if (!val('admin_email')) return {msg:t('err_admin_email'), field:'admin_email'};
1855
+ if (!val('admin_name')) return {msg:t('err_admin_name'), field:'admin_name'};
1856
+ if (!val('admin_pass')) return {msg:t('err_admin_pass'), field:'admin_pass'};
1857
+ if (val('admin_pass') !== val('admin_pass2')) return {msg:t('err_admin_pass2'), field:'admin_pass2'};
1858
+ break;
1730
1859
  }
1731
1860
  return null;
1732
1861
  }
@@ -2381,6 +2510,7 @@ async function runInstall() {
2381
2510
  setRunStatus('ok', 'run_success');
2382
2511
  result.className = 'run-result ok';
2383
2512
  result.textContent = t('run_success');
2513
+ createAdminUser(result);
2384
2514
  } else {
2385
2515
  document.getElementById('terminalTitle').textContent = 'Installation Log — Failed';
2386
2516
  setRunStatus('err', 'run_failed');
@@ -2415,6 +2545,44 @@ async function runInstall() {
2415
2545
  }
2416
2546
  }
2417
2547
 
2548
+ async function createAdminUser(resultEl) {
2549
+ const notice = document.createElement('div');
2550
+ notice.style.cssText = 'margin-top:10px;font-size:13px;color:#2563eb;padding:6px 0';
2551
+ notice.textContent = t('admin_creating');
2552
+ resultEl.appendChild(notice);
2553
+
2554
+ try {
2555
+ const authPort = val('auth_svc_port') || '5238';
2556
+ const body = {
2557
+ userName: val('admin_email'),
2558
+ email: val('admin_email'),
2559
+ password: val('admin_pass'),
2560
+ fullName: val('admin_name'),
2561
+ Organization: val('admin_org') || '',
2562
+ role: 'DIXI_ADMIN',
2563
+ phoneNumber: '', doorNo: '', blockNo: '', country: '',
2564
+ isStaffRegistration: false,
2565
+ consentRecords: [], id: null,
2566
+ };
2567
+ const r = await fetch(SERVER_BASE + '/api/create-admin', {
2568
+ method: 'POST',
2569
+ headers: { 'Content-Type': 'application/json' },
2570
+ body: JSON.stringify({ authPort, user: body }),
2571
+ });
2572
+ const data = await r.json();
2573
+ if (data.ok) {
2574
+ notice.style.color = '#16a34a';
2575
+ notice.textContent = t('admin_created');
2576
+ } else {
2577
+ notice.style.color = '#dc2626';
2578
+ notice.textContent = t('admin_failed') + (data.error ? ' (' + data.error + ')' : '');
2579
+ }
2580
+ } catch(e) {
2581
+ notice.style.color = '#dc2626';
2582
+ notice.textContent = t('admin_failed');
2583
+ }
2584
+ }
2585
+
2418
2586
  function resetRun() {
2419
2587
  if (_evtSource) { _evtSource.close(); _evtSource = null; }
2420
2588
  document.getElementById('logPane').innerHTML = '';