depository-deploy 1.0.40 → 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.
@@ -336,24 +336,26 @@ capture_svc_error() {
336
336
  warn " $line"
337
337
  done
338
338
 
339
- # Also try running the executable directly for 5 seconds to surface startup exceptions
339
+ # Also try running the executable directly for 10 seconds to surface startup exceptions
340
340
  local svc_exe
341
341
  svc_exe="$(get_svc_exe "$svc")"
342
342
  if [ -n "$svc_exe" ] && [ -f "$svc_exe" ]; then
343
- warn " --- Running exe directly to capture startup error (5 s) ---"
343
+ warn " --- Running exe directly to capture startup error (10 s) ---"
344
344
  local tmp_out="/tmp/${svc}-startup-$$.log"
345
345
  local svc_dir
346
346
  svc_dir="$(dirname "$svc_exe")"
347
347
  # Run as depository user if available so appsettings.json is readable
348
348
  if id -u depository &>/dev/null; then
349
- timeout 5 sudo -u depository "$svc_exe" 2>&1 | head -60 > "$tmp_out" || true
349
+ timeout 10 sudo -u depository "$svc_exe" > "$tmp_out" 2>&1 || true
350
350
  else
351
- timeout 5 "$svc_exe" 2>&1 | head -60 > "$tmp_out" || true
351
+ timeout 10 "$svc_exe" > "$tmp_out" 2>&1 || true
352
352
  fi
353
353
  if [ -s "$tmp_out" ]; then
354
- while IFS= read -r line; do
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
355
357
  warn " $line"
356
- done < "$tmp_out"
358
+ done
357
359
  else
358
360
  warn " (no output captured from direct run)"
359
361
  fi
@@ -347,14 +347,17 @@ foreach ($def in $svcDefs) {
347
347
  -RedirectStandardOutput "$env:TEMP\$($def.Name)-stdout.txt" `
348
348
  -RedirectStandardError "$env:TEMP\$($def.Name)-stderr.txt" `
349
349
  -NoNewWindow -PassThru -ErrorAction SilentlyContinue
350
- Start-Sleep -Seconds 5
350
+ Start-Sleep -Seconds 10
351
351
  if (-not $proc.HasExited) { $proc.Kill() }
352
352
  $stdout = Get-Content "$env:TEMP\$($def.Name)-stdout.txt" -ErrorAction SilentlyContinue
353
353
  $stderr = Get-Content "$env:TEMP\$($def.Name)-stderr.txt" -ErrorAction SilentlyContinue
354
354
  $combined = @($stdout; $stderr) | Where-Object { $_ -and $_.Trim() }
355
355
  if ($combined) {
356
- Write-Warn " --- Startup output (stdout + stderr) ---"
357
- $combined | Select-Object -First 30 | 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 " $_" }
358
361
  } else {
359
362
  Write-Warn " (no output captured -- check Event Viewer > Windows Logs > Application)"
360
363
  }
@@ -362,7 +365,7 @@ foreach ($def in $svcDefs) {
362
365
  $errLog = Join-Path $exeDir "startup-error.log"
363
366
  if (Test-Path $errLog) {
364
367
  Write-Warn " --- $errLog ---"
365
- Get-Content $errLog -ErrorAction SilentlyContinue | Select-Object -First 50 | ForEach-Object { Write-Warn " $_" }
368
+ Get-Content $errLog -ErrorAction SilentlyContinue | ForEach-Object { Write-Warn " $_" }
366
369
  }
367
370
  } catch {
368
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.40",
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.40",
29
- "depository-deploy-macos": "1.0.40",
30
- "depository-deploy-windows": "1.0.40"
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 = '';