BastionGuard/webui/setup.php
specialworld83 f0f913a209 Release 2.0
2026-07-15 10:52:22 +02:00

430 lines
26 KiB
PHP

<?php
require_once __DIR__ . '/includes/users.php';
require_once __DIR__ . '/includes/config_manager.php';
csrf_start_session();
if (bg_config_exists()) { header('Location: login.php'); exit; }
$messages = [];
$errors = [];
$setupDone = false;
if (!empty($_SESSION['bg_setup_notice'])) {
$messages[] = (string)$_SESSION['bg_setup_notice'];
unset($_SESSION['bg_setup_notice']);
}
function bg_setup_normalize_driver($driver) {
$driver = strtolower(trim((string)$driver));
return in_array($driver, ['sqlite','mysql','pgsql'], true) ? $driver : 'sqlite';
}
function bg_setup_pdo_has_driver($driver) {
$drivers = class_exists('PDO') ? PDO::getAvailableDrivers() : [];
return in_array($driver, $drivers, true);
}
function bg_setup_post_db_config($driver) {
$driver = bg_setup_normalize_driver($driver);
$cfg = ['driver' => $driver];
if ($driver === 'sqlite') return $cfg;
if ($driver === 'pgsql') {
$cfg['db_host'] = trim((string)($_POST['db_host_pg'] ?? $_POST['db_host'] ?? 'localhost')) ?: 'localhost';
$cfg['db_port'] = trim((string)($_POST['db_port_pg'] ?? $_POST['db_port'] ?? '5432'));
$cfg['db_name'] = trim((string)($_POST['db_name_pg'] ?? $_POST['db_name'] ?? 'bastionguard_webui')) ?: 'bastionguard_webui';
$cfg['db_user'] = trim((string)($_POST['db_user_pg'] ?? $_POST['db_user'] ?? ''));
$cfg['db_pass'] = (string)($_POST['db_pass_pg'] ?? $_POST['db_pass'] ?? '');
$cfg['db_maintenance'] = trim((string)($_POST['db_maintenance_pg'] ?? $_POST['db_maintenance'] ?? 'postgres')) ?: 'postgres';
} else {
$cfg['db_host'] = trim((string)($_POST['db_host'] ?? 'localhost')) ?: 'localhost';
$cfg['db_port'] = trim((string)($_POST['db_port'] ?? '3306'));
$cfg['db_name'] = trim((string)($_POST['db_name'] ?? 'bastionguard_webui')) ?: 'bastionguard_webui';
$cfg['db_user'] = trim((string)($_POST['db_user'] ?? ''));
$cfg['db_pass'] = (string)($_POST['db_pass'] ?? '');
}
return $cfg;
}
function bg_setup_mysql_dsn(array $cfg, $withDb = false) {
$dsn = 'mysql:host=' . $cfg['db_host'];
if (!empty($cfg['db_port'])) $dsn .= ';port=' . (int)$cfg['db_port'];
if ($withDb) $dsn .= ';dbname=' . $cfg['db_name'];
return $dsn . ';charset=utf8mb4';
}
function bg_setup_pgsql_dsn(array $cfg, $dbname) {
$dsn = 'pgsql:host=' . $cfg['db_host'];
if (!empty($cfg['db_port'])) $dsn .= ';port=' . (int)$cfg['db_port'];
$dsn .= ';dbname=' . $dbname;
return $dsn;
}
function bg_setup_pg_ident($name) {
return '"' . str_replace('"', '""', (string)$name) . '"';
}
function bg_setup_validate_db(array $cfg) {
$driver = bg_setup_normalize_driver($cfg['driver'] ?? 'sqlite');
$cfg['driver'] = $driver;
if ($driver === 'sqlite') {
if (!bg_setup_pdo_has_driver('sqlite')) throw new RuntimeException(bg_t('setup_sqlite_unavailable'));
$dir = __DIR__ . '/db';
if (!is_dir($dir) && !@mkdir($dir, 0750, true)) throw new RuntimeException(bg_t('setup_sqlite_dir_error'));
if (!is_writable($dir)) throw new RuntimeException(bg_t('setup_sqlite_dir_not_writable'));
return $cfg;
}
if ($driver === 'mysql') {
if (!bg_setup_pdo_has_driver('mysql')) throw new RuntimeException(bg_t('setup_mysql_unavailable'));
if (($cfg['db_user'] ?? '') === '') throw new RuntimeException(bg_t('setup_missing_db_user'));
$pdo = new PDO(bg_setup_mysql_dsn($cfg, false), $cfg['db_user'], $cfg['db_pass'], [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
$quoted = '`' . str_replace('`', '``', $cfg['db_name']) . '`';
$pdo->exec('CREATE DATABASE IF NOT EXISTS ' . $quoted . ' CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci');
new PDO(bg_setup_mysql_dsn($cfg, true), $cfg['db_user'], $cfg['db_pass'], [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
return $cfg;
}
if (!bg_setup_pdo_has_driver('pgsql')) throw new RuntimeException(bg_t('setup_pgsql_unavailable'));
if (($cfg['db_user'] ?? '') === '') throw new RuntimeException(bg_t('setup_missing_db_user'));
$maintenance = $cfg['db_maintenance'] ?? 'postgres';
$createdOrFound = false;
try {
$admin = new PDO(bg_setup_pgsql_dsn($cfg, $maintenance), $cfg['db_user'], $cfg['db_pass'], [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
$stmt = $admin->prepare('SELECT 1 FROM pg_database WHERE datname = ?');
$stmt->execute([$cfg['db_name']]);
if (!$stmt->fetchColumn()) {
$admin->exec('CREATE DATABASE ' . bg_setup_pg_ident($cfg['db_name']) . " WITH ENCODING 'UTF8'");
}
$createdOrFound = true;
} catch (Throwable $e) {
// Some PostgreSQL roles cannot connect to the maintenance database or cannot create DBs.
// In that case an already-created target database is still valid.
}
try {
$target = new PDO(bg_setup_pgsql_dsn($cfg, $cfg['db_name']), $cfg['db_user'], $cfg['db_pass'], [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
$target->exec("SET client_encoding TO 'UTF8'");
return $cfg;
} catch (Throwable $e) {
if ($createdOrFound) throw new RuntimeException(bg_t('setup_pgsql_connect_created_error', ['message' => $e->getMessage()]));
throw new RuntimeException(bg_t('setup_pgsql_connect_error', ['message' => $e->getMessage()]));
}
}
function bg_setup_build_config(array $dbCfg, $webUser) {
$driver = bg_setup_normalize_driver($dbCfg['driver'] ?? 'sqlite');
$cfg = "<?php\n";
$cfg .= "define('AUTH_DRIVER', " . var_export($driver, true) . ");\n";
if ($driver === 'sqlite') {
$cfg .= "define('SQLITE_DB', dirname(__DIR__) . '/db/users.db');\n";
} else {
$cfg .= "define('DB_HOST', " . var_export((string)($dbCfg['db_host'] ?? 'localhost'), true) . ");\n";
$cfg .= "define('DB_PORT', " . var_export((string)($dbCfg['db_port'] ?? ($driver === 'pgsql' ? '5432' : '3306')), true) . ");\n";
$cfg .= "define('DB_USER', " . var_export((string)($dbCfg['db_user'] ?? ''), true) . ");\n";
$cfg .= "define('DB_PASS', " . var_export((string)($dbCfg['db_pass'] ?? ''), true) . ");\n";
$cfg .= "define('DB_NAME', " . var_export((string)($dbCfg['db_name'] ?? 'bastionguard_webui'), true) . ");\n";
if ($driver === 'pgsql') $cfg .= "define('DB_MAINTENANCE', " . var_export((string)($dbCfg['db_maintenance'] ?? 'postgres'), true) . ");\n";
}
$cfg .= "define('WEB_USER', " . var_export((string)$webUser, true) . ");\n";
return $cfg;
}
function bg_setup_driver_label($driver) {
$driver = bg_setup_normalize_driver($driver);
if ($driver === 'mysql') return bg_t('setup_mysql_mariadb');
if ($driver === 'pgsql') return bg_t('setup_postgresql');
return bg_t('setup_local_sqlite');
}
function bg_setup_db_summary(array $cfg) {
$driver = bg_setup_normalize_driver($cfg['driver'] ?? 'sqlite');
if ($driver === 'sqlite') return bg_setup_driver_label($driver);
$host = (string)($cfg['db_host'] ?? 'localhost');
$port = (string)($cfg['db_port'] ?? '');
$name = (string)($cfg['db_name'] ?? 'bastionguard_webui');
$user = (string)($cfg['db_user'] ?? '');
return bg_setup_driver_label($driver) . ' · ' . $host . ($port !== '' ? ':' . $port : '') . ' · ' . $name . ' · ' . $user;
}
$dbSession = isset($_SESSION['bg_setup_db']) && is_array($_SESSION['bg_setup_db']) ? $_SESSION['bg_setup_db'] : null;
$setupStep = $dbSession ? 2 : 1;
if (($_GET['step'] ?? '') === 'database') $setupStep = 1;
if (($_GET['step'] ?? '') === 'admin' && $dbSession) $setupStep = 2;
$driver = bg_setup_normalize_driver($_POST['auth_driver'] ?? ($dbSession['driver'] ?? 'sqlite'));
$desktop_user = trim((string)($_POST['desktop_user'] ?? bg_detect_desktop_user()));
$desktopMode = bg_desktop_mode_status($desktop_user ?: null);
$setupConfigScan = bg_config_scan_dir($desktop_user ?: null);
$setupWizardStatus = bg_wizard_config_status($desktop_user ?: null);
$setupWizardLock = bg_wizard_lock_status($desktop_user ?: null, $setupConfigScan, $setupWizardStatus, $desktopMode);
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
csrf_validate();
$action = (string)($_POST['action'] ?? '');
if ($action === 'back_database') {
unset($_SESSION['bg_setup_db']);
header('Location: setup.php?step=database');
exit;
}
if ($action === 'setup_database') {
$driver = bg_setup_normalize_driver($_POST['auth_driver'] ?? 'sqlite');
$dbCfg = bg_setup_post_db_config($driver);
try {
$dbCfg = bg_setup_validate_db($dbCfg);
$_SESSION['bg_setup_db'] = $dbCfg;
$_SESSION['bg_setup_notice'] = bg_t('setup_database_validated');
header('Location: setup.php?step=admin');
exit;
} catch (Throwable $e) {
$errors[] = bg_t('setup_database_error', ['message' => $e->getMessage()]);
$setupStep = 1;
$dbSession = $dbCfg;
}
} elseif ($action === 'complete_setup') {
$dbCfg = isset($_SESSION['bg_setup_db']) && is_array($_SESSION['bg_setup_db']) ? $_SESSION['bg_setup_db'] : null;
$setupStep = 2;
if (!$dbCfg) {
$errors[] = bg_t('setup_database_required');
$setupStep = 1;
} else {
$desktop_user = trim((string)($_POST['desktop_user'] ?? $desktop_user));
$username = trim((string)($_POST['username'] ?? ''));
$admin_email = trim((string)($_POST['admin_email'] ?? ''));
$password = (string)($_POST['password'] ?? '');
$confirm = (string)($_POST['confirm'] ?? '');
$web_user = trim((string)($_POST['web_user'] ?? 'www-data')) ?: 'www-data';
$desktopMode = bg_desktop_mode_status($desktop_user ?: null);
$setupConfigScan = bg_config_scan_dir($desktop_user ?: null);
$setupWizardStatus = bg_wizard_config_status($desktop_user ?: null);
$setupWizardLock = bg_wizard_lock_status($desktop_user ?: null, $setupConfigScan, $setupWizardStatus, $desktopMode);
if ($username === '' || $password === '' || $password !== $confirm) $errors[] = bg_t('setup_validation_admin_fields');
if ($admin_email !== '' && !filter_var($admin_email, FILTER_VALIDATE_EMAIL)) $errors[] = bg_t('invalid_email_address');
if (strlen($password) < 8) $errors[] = bg_t('setup_validation_password_len');
if (!$errors) {
try {
$dbCfg = bg_setup_validate_db($dbCfg);
$cfg = bg_setup_build_config($dbCfg, $web_user);
$authCfg = __DIR__ . '/includes/webui_config.php';
if (@file_put_contents($authCfg, $cfg, LOCK_EX) === false) throw new RuntimeException(bg_t('setup_write_config_error'));
@chmod($authCfg, 0640);
require $authCfg;
bg_create_user($username, $password, 'admin', $admin_email);
$_SESSION['user'] = $username;
$_SESSION['role'] = 'admin';
unset($_SESSION['bg_setup_db']);
$opts = bg_web_options();
if ($desktop_user !== '') $opts['desktop_user'] = $desktop_user;
unset($opts['language'], $opts['theme']);
bg_write_json('web_options.json', $opts);
$messages[] = bg_t('setup_success_config_created');
$sudoers = [];
$sudoers[] = '# BastionGuard WebUI - helper based sudoers. Check with: visudo -cf /etc/sudoers.d/bastionguard-webui';
$sudoers[] = '# Prefer running: sudo bash scripts/install-webui-helpers.sh "$USER"';
$sudoers[] = 'Cmnd_Alias BASTIONGUARD_WEBUI_SYSTEMCTL = /usr/local/sbin/bastionguard-webui-systemctl user ' . ($desktop_user ?: '<desktop-user>') . ' *';
$sudoers[] = 'Cmnd_Alias BASTIONGUARD_WEBUI_ADMIN = /usr/local/sbin/bastionguard-webui-admin *';
$sudoers[] = 'Cmnd_Alias BASTIONGUARD_WEBUI_WIZARD = /usr/local/sbin/bastionguard-webui-wizard *';
$sudoers[] = $web_user . ' ALL=(root) NOPASSWD: BASTIONGUARD_WEBUI_SYSTEMCTL, BASTIONGUARD_WEBUI_ADMIN, BASTIONGUARD_WEBUI_WIZARD';
$local = __DIR__ . '/bastionguard-webui.sudo';
@file_put_contents($local, implode("\n", $sudoers) . "\n", LOCK_EX);
@chmod($local, 0640);
$messages[] = bg_t('setup_success_sudoers_generated', ['path' => $local]);
$messages[] = bg_t('setup_success_next_wizard');
$setupDone = true;
$setupStep = 3;
$wizardStatus = bg_wizard_config_status($desktop_user ?: null);
$setupConfigScan = bg_config_scan_dir($desktop_user ?: null);
$setupWizardLock = bg_wizard_lock_status($desktop_user ?: null, $setupConfigScan, $wizardStatus, bg_desktop_mode_status($desktop_user ?: null));
$nextUrl = (!empty($setupWizardLock['locked']) || !empty($wizardStatus['exists'])) ? 'dashboard.php' : 'wizard.php?first=1&from_setup=1';
header('Refresh:2; url=' . $nextUrl);
} catch (Throwable $e) {
@unlink(__DIR__ . '/includes/webui_config.php');
$errors[] = bg_t('setup_error', ['message' => $e->getMessage()]);
}
}
}
}
}
$dbState = isset($_SESSION['bg_setup_db']) && is_array($_SESSION['bg_setup_db']) ? $_SESSION['bg_setup_db'] : ($dbSession ?: ['driver' => $driver]);
$driver = bg_setup_normalize_driver($dbState['driver'] ?? $driver);
$lang = bg_lang_attr();
$theme = bg_theme();
$bsTheme = $theme === 'dark' ? 'dark' : 'light';
$stepTitles = [bg_t('setup_step_database_ui'), bg_t('setup_step_admin_ui'), bg_t('setup_step_complete_ui')];
?>
<!DOCTYPE html>
<html lang="<?= bg_e($lang) ?>" data-theme="<?= bg_e($theme) ?>" data-bs-theme="<?= bg_e($bsTheme) ?>">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title><?= bg_e(bg_t('auth_setup_page_title')) ?></title>
<link href="assets/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="assets/css/style.css?v=20260507-setup-pgsql-steps" rel="stylesheet">
</head>
<body class="auth-page setup-auth-page">
<div class="auth-preferences" aria-label="<?= bg_e(bg_t('auth_language_switch')) ?>">
<a class="<?= bg_language()==='en'?'active':'' ?>" href="?bg_lang=en">EN</a>
<a class="<?= bg_language()==='it'?'active':'' ?>" href="?bg_lang=it">IT</a>
<span></span>
<a class="<?= bg_theme()==='system'?'active':'' ?>" href="?bg_theme=system"><?= bg_e(bg_t('system_theme')) ?></a>
<a class="<?= bg_theme()==='light'?'active':'' ?>" href="?bg_theme=light"><?= bg_e(bg_t('light')) ?></a>
<a class="<?= bg_theme()==='dark'?'active':'' ?>" href="?bg_theme=dark"><?= bg_e(bg_t('dark')) ?></a>
</div>
<main class="auth-shell">
<section class="auth-brand-panel" aria-label="BastionGuard WebPanel">
<div class="auth-brand-content">
<p class="auth-kicker"><?= bg_e(bg_t('auth_welcome_to')) ?></p>
<h1><?= str_replace(' ', '<br>', bg_e(bg_t('auth_webpanel_bastionguard'))) ?></h1>
<div class="auth-brand-lockup">
<?= bg_logo_html('auth-brand-logo') ?>
<div><strong>BastionGuard</strong><span><?= bg_e(bg_t('auth_tagline_total')) ?></span></div>
</div>
</div>
<div class="auth-shape auth-shape-one"></div>
<div class="auth-shape auth-shape-two"></div>
<div class="auth-dot-grid" aria-hidden="true"></div>
</section>
<section class="auth-form-panel">
<div class="auth-card-wrap setup-card-wrap">
<div class="auth-floating-logo"><?= bg_logo_html('auth-floating-logo-mark') ?></div>
<div class="auth-card setup-card wizard-card">
<div class="auth-card-header">
<h2><?= bg_e(bg_t('setup_title')) ?></h2>
<p><?= bg_e(bg_t('setup_subtitle')) ?></p>
</div>
<div class="wizard-stepper setup-stepper" aria-label="<?= bg_e(bg_t('setup_title')) ?>">
<?php foreach($stepTitles as $idx => $title): $num = $idx + 1; ?>
<button type="button" class="wizard-stepper-item <?= $setupStep === $num ? 'active' : '' ?> <?= $setupStep > $num ? 'done' : '' ?>" disabled><span><?= (int)$num ?></span><?= bg_e($title) ?></button>
<?php endforeach; ?>
</div>
<?php foreach($messages as $m): ?><div class="alert alert-success"><?= bg_e($m) ?></div><?php endforeach; ?>
<?php foreach($errors as $e): ?><div class="alert alert-danger"><?= bg_e($e) ?></div><?php endforeach; ?>
<?php if ($setupDone): ?>
<div class="auth-info-note mb-3"><?= bg_e(bg_t('setup_success_next_wizard')) ?></div>
<a class="btn btn-primary auth-submit w-100" href="dashboard.php"><?= bg_e(bg_t('setup_go_dashboard')) ?></a>
<?php elseif ($setupStep === 1): ?>
<form method="post" autocomplete="off" data-setup-db-form>
<?php csrf_field(); ?><input type="hidden" name="action" value="setup_database">
<h3 class="h5 mb-2"><?= bg_e(bg_t('setup_database_title')) ?></h3>
<p class="form-hint mb-3"><?= bg_e(bg_t('setup_database_subtitle')) ?></p>
<div class="mb-3">
<label class="form-label" for="auth_driver"><?= bg_e(bg_t('setup_user_backend')) ?></label>
<select class="form-select auth-input" id="auth_driver" name="auth_driver" data-db-driver>
<option value="sqlite" <?= $driver==='sqlite'?'selected':'' ?>><?= bg_e(bg_t('setup_local_sqlite')) ?></option>
<option value="mysql" <?= $driver==='mysql'?'selected':'' ?>><?= bg_e(bg_t('setup_mysql_mariadb')) ?></option>
<option value="pgsql" <?= $driver==='pgsql'?'selected':'' ?>><?= bg_e(bg_t('setup_postgresql')) ?></option>
</select>
</div>
<div class="auth-info-note mb-3" data-db-box="sqlite">
<?= bg_e(bg_t('setup_sqlite_note')) ?>
</div>
<div data-db-box="mysql">
<div class="row g-2 mb-3">
<div class="col-md-6"><label class="form-label"><?= bg_e(bg_t('setup_db_host')) ?></label><input class="form-control auth-input" name="db_host" value="<?= bg_e($_POST['db_host'] ?? ($dbState['db_host'] ?? 'localhost')) ?>"></div>
<div class="col-md-6"><label class="form-label"><?= bg_e(bg_t('setup_db_port')) ?></label><input class="form-control auth-input" name="db_port" value="<?= bg_e($_POST['db_port'] ?? ($dbState['db_port'] ?? '3306')) ?>"></div>
<div class="col-md-6"><label class="form-label"><?= bg_e(bg_t('setup_db_name')) ?></label><input class="form-control auth-input" name="db_name" value="<?= bg_e($_POST['db_name'] ?? ($dbState['db_name'] ?? 'bastionguard_webui')) ?>"></div>
<div class="col-md-6"><label class="form-label"><?= bg_e(bg_t('setup_db_user')) ?></label><input class="form-control auth-input" name="db_user" value="<?= bg_e($_POST['db_user'] ?? ($dbState['db_user'] ?? '')) ?>"></div>
<div class="col-md-12"><label class="form-label"><?= bg_e(bg_t('setup_db_password')) ?></label><input class="form-control auth-input" name="db_pass" type="password" value="<?= bg_e($_POST['db_pass'] ?? ($dbState['db_pass'] ?? '')) ?>"></div>
</div>
</div>
<div data-db-box="pgsql">
<div class="row g-2 mb-3">
<div class="col-md-6"><label class="form-label"><?= bg_e(bg_t('setup_db_host')) ?></label><input class="form-control auth-input" name="db_host_pg" data-db-field="db_host" value="<?= bg_e($_POST['db_host_pg'] ?? ($driver==='pgsql' ? ($dbState['db_host'] ?? 'localhost') : 'localhost')) ?>"></div>
<div class="col-md-6"><label class="form-label"><?= bg_e(bg_t('setup_db_port')) ?></label><input class="form-control auth-input" name="db_port_pg" data-db-field="db_port" value="<?= bg_e($_POST['db_port_pg'] ?? ($driver==='pgsql' ? ($dbState['db_port'] ?? '5432') : '5432')) ?>"></div>
<div class="col-md-6"><label class="form-label"><?= bg_e(bg_t('setup_db_name')) ?></label><input class="form-control auth-input" name="db_name_pg" data-db-field="db_name" value="<?= bg_e($_POST['db_name_pg'] ?? ($driver==='pgsql' ? ($dbState['db_name'] ?? 'bastionguard_webui') : 'bastionguard_webui')) ?>"></div>
<div class="col-md-6"><label class="form-label"><?= bg_e(bg_t('setup_db_user')) ?></label><input class="form-control auth-input" name="db_user_pg" data-db-field="db_user" value="<?= bg_e($_POST['db_user_pg'] ?? ($driver==='pgsql' ? ($dbState['db_user'] ?? '') : '')) ?>"></div>
<div class="col-md-6"><label class="form-label"><?= bg_e(bg_t('setup_db_password')) ?></label><input class="form-control auth-input" name="db_pass_pg" data-db-field="db_pass" type="password" value="<?= bg_e($_POST['db_pass_pg'] ?? ($driver==='pgsql' ? ($dbState['db_pass'] ?? '') : '')) ?>"></div>
<div class="col-md-6"><label class="form-label"><?= bg_e(bg_t('setup_db_maintenance')) ?></label><input class="form-control auth-input" name="db_maintenance_pg" data-db-field="db_maintenance" value="<?= bg_e($_POST['db_maintenance_pg'] ?? ($driver==='pgsql' ? ($dbState['db_maintenance'] ?? 'postgres') : 'postgres')) ?>"><div class="form-hint"><?= bg_e(bg_t('setup_db_maintenance_hint')) ?></div></div>
</div>
</div>
<button class="btn btn-primary auth-submit w-100 mt-3" type="submit"><?= bg_e(bg_t('setup_database_continue')) ?></button>
</form>
<?php else: ?>
<form method="post" autocomplete="off">
<?php csrf_field(); ?><input type="hidden" name="action" value="complete_setup">
<h3 class="h5 mb-2"><?= bg_e(bg_t('setup_admin_title')) ?></h3>
<p class="form-hint mb-3"><?= bg_e(bg_t('setup_admin_subtitle')) ?></p>
<div class="auth-info-note mb-3"><strong><?= bg_e(bg_t('setup_database_driver_summary')) ?>:</strong> <?= bg_e(bg_setup_db_summary($dbState)) ?></div>
<div class="mb-3">
<label class="form-label" for="desktop_user"><?= bg_e(bg_t('setup_desktop_user')) ?></label>
<input class="form-control auth-input" id="desktop_user" name="desktop_user" value="<?= bg_e($desktop_user) ?>" placeholder="$USER">
<div class="form-hint"><?= bg_e(bg_t('setup_desktop_user_hint')) ?></div>
</div>
<div class="mb-3">
<label class="form-label" for="web_user"><?= bg_e(bg_t('setup_web_server_user')) ?></label>
<input class="form-control auth-input" id="web_user" name="web_user" value="<?= bg_e($_POST['web_user'] ?? 'www-data') ?>">
<div class="form-hint"><?= bg_e(bg_t('setup_examples')) ?></div>
</div>
<div class="row g-2">
<div class="col-md-12"><label class="form-label" for="setup_username"><?= bg_e(bg_t('setup_admin_username')) ?></label><input class="form-control auth-input" id="setup_username" name="username" value="<?= bg_e($_POST['username'] ?? '') ?>" required></div>
<div class="col-md-12"><label class="form-label" for="setup_admin_email"><?= bg_e(bg_t('email_address')) ?></label><input class="form-control auth-input" id="setup_admin_email" name="admin_email" type="email" value="<?= bg_e($_POST['admin_email'] ?? '') ?>" placeholder="admin@example.com"><div class="form-hint"><?= bg_e(bg_t('admin_email_hint')) ?></div></div>
<div class="col-md-6"><label class="form-label" for="setup_password"><?= bg_e(bg_t('auth_password')) ?></label><input class="form-control auth-input" id="setup_password" name="password" type="password" required></div>
<div class="col-md-6"><label class="form-label" for="setup_confirm"><?= bg_e(bg_t('setup_confirm_password')) ?></label><input class="form-control auth-input" id="setup_confirm" name="confirm" type="password" required></div>
</div>
<div class="auth-info-note mt-3"><?= bg_e(bg_t('setup_info_note')) ?></div>
<div class="wizard-actions mt-4">
<button class="btn btn-outline-secondary" type="submit" name="action" value="back_database"><?= bg_e(bg_t('setup_back_database')) ?></button>
<button class="btn btn-primary auth-submit" type="submit" name="action" value="complete_setup"><?= bg_e(bg_t('setup_complete_set')) ?></button>
</div>
</form>
<?php endif; ?>
<div class="auth-version">v1.0</div>
</div>
</div>
</section>
</main>
<script>
(function(){
var driver = document.querySelector('[data-db-driver]');
var boxes = Array.prototype.slice.call(document.querySelectorAll('[data-db-box]'));
function setInputState(box, enabled) {
box.querySelectorAll('input,select,textarea').forEach(function(el){ el.disabled = !enabled; });
}
function syncPgFields(enabled) {
document.querySelectorAll('[data-db-field]').forEach(function(el){
if (!enabled) return;
var hidden = document.querySelector('input[type="hidden"][name="' + el.getAttribute('data-db-field') + '"]');
if (!hidden) {
hidden = document.createElement('input');
hidden.type = 'hidden';
hidden.name = el.getAttribute('data-db-field');
el.form.appendChild(hidden);
}
hidden.value = el.value;
});
}
function update() {
var value = driver ? driver.value : 'sqlite';
boxes.forEach(function(box){
var active = box.getAttribute('data-db-box') === value;
box.style.display = active ? '' : 'none';
setInputState(box, active);
});
syncPgFields(value === 'pgsql');
}
if (driver) driver.addEventListener('change', update);
document.querySelectorAll('[data-db-field]').forEach(function(el){ el.addEventListener('input', function(){ syncPgFields(driver && driver.value === 'pgsql'); }); });
update();
})();
</script>
</body>
</html>