Some checks are pending
CI / PHP Lint (push) Waiting to run
CI / PHP Lint-1 (push) Waiting to run
CI / Unit Tests & Static Analysis (push) Waiting to run
CI / PHP Lint (pull_request) Waiting to run
CI / PHP Lint-1 (pull_request) Waiting to run
CI / Unit Tests & Static Analysis (pull_request) Waiting to run
Zentral in Ui::credit() gepflegt und dezent im Seitenfuß eingebunden:
- Admin-Bereich: unter dem Benutzerbereich in der Sidebar
- öffentliche Voucher-Seite: unter der Karte
- Anmeldung, Installer und Updater: am Ende der jeweiligen Karte
- Wartungsseite: direkt im Markup, da sie bewusst eigenständig bleibt
- README: im Kopf und in der Fußzeile
Der Hinweis ist übersetzt ("Entwickelt von" / "Developed by") und stört
das Branding der Betreiber nicht – die konfigurierbare Fußzeile der
Login-Seite bleibt unverändert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
381 lines
No EOL
17 KiB
PHP
381 lines
No EOL
17 KiB
PHP
<?php
|
||
session_start();
|
||
|
||
// Prüfen ob bereits installiert.
|
||
// Wenn eine config.php existiert, darf der Installer NICHT mehr ohne Weiteres
|
||
// erreichbar sein – sonst koennte jeder die Konfiguration ueberschreiben und
|
||
// einen neuen Admin anlegen. Reinstall ist nur fuer angemeldete Admins erlaubt.
|
||
if (file_exists(__DIR__ . '/config.php')) {
|
||
if (!isset($_GET['reinstall'])) {
|
||
die('System bereits installiert. Eine Neuinstallation ist nur fuer angemeldete Administratoren ueber install.php?reinstall=1 moeglich.');
|
||
}
|
||
|
||
// Reinstall angefordert -> Admin-Authentifizierung erzwingen
|
||
require_once __DIR__ . '/config.php';
|
||
require_once __DIR__ . '/includes/Database.php';
|
||
require_once __DIR__ . '/includes/Auth.php';
|
||
try {
|
||
$reinstallAuth = new Auth();
|
||
if (!$reinstallAuth->isAdmin()) {
|
||
die('Neuinstallation nicht erlaubt: Bitte zuerst als Administrator <a href="login.php">anmelden</a>.');
|
||
}
|
||
} catch (Exception $e) {
|
||
die('Neuinstallation nicht moeglich (Konfigurationsfehler).');
|
||
}
|
||
}
|
||
|
||
$step = isset($_POST['step']) ? (int)$_POST['step'] : 1;
|
||
$errors = [];
|
||
$success = false;
|
||
|
||
// Step 1: Datenbankverbindung testen
|
||
if ($step === 2 && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||
$db_host = $_POST['db_host'] ?? '';
|
||
$db_name = $_POST['db_name'] ?? '';
|
||
$db_user = $_POST['db_user'] ?? '';
|
||
$db_pass = $_POST['db_pass'] ?? '';
|
||
|
||
try {
|
||
$pdo = new PDO("mysql:host=$db_host;charset=utf8mb4", $db_user, $db_pass);
|
||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||
|
||
// Datenbank erstellen falls nicht vorhanden
|
||
$pdo->exec("CREATE DATABASE IF NOT EXISTS `$db_name` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
|
||
$pdo->exec("USE `$db_name`");
|
||
|
||
// Tabellen erstellen
|
||
$sql = file_get_contents(__DIR__ . '/database.sql');
|
||
$pdo->exec($sql);
|
||
|
||
$_SESSION['install_db'] = [
|
||
'host' => $db_host,
|
||
'name' => $db_name,
|
||
'user' => $db_user,
|
||
'pass' => $db_pass
|
||
];
|
||
|
||
} catch (PDOException $e) {
|
||
$errors[] = "Datenbankfehler: " . $e->getMessage();
|
||
$step = 1;
|
||
}
|
||
}
|
||
|
||
// Step 2: Admin-Account erstellen
|
||
if ($step === 3 && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||
$admin_email = filter_var($_POST['admin_email'] ?? '', FILTER_VALIDATE_EMAIL);
|
||
$admin_name = $_POST['admin_name'] ?? '';
|
||
$admin_password = $_POST['admin_password'] ?? '';
|
||
$admin_password_confirm = $_POST['admin_password_confirm'] ?? '';
|
||
|
||
if (!$admin_email) {
|
||
$errors[] = "Ungültige E-Mail-Adresse";
|
||
$step = 2;
|
||
} elseif (strlen($admin_password) < 8) {
|
||
$errors[] = "Passwort muss mindestens 8 Zeichen lang sein";
|
||
$step = 2;
|
||
} elseif ($admin_password !== $admin_password_confirm) {
|
||
$errors[] = "Passwörter stimmen nicht überein";
|
||
$step = 2;
|
||
} else {
|
||
$_SESSION['install_admin'] = [
|
||
'email' => $admin_email,
|
||
'name' => $admin_name,
|
||
'password' => password_hash($admin_password, PASSWORD_DEFAULT)
|
||
];
|
||
}
|
||
}
|
||
|
||
// Step 3: Allgemeine Einstellungen
|
||
if ($step === 4 && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||
$app_title = $_POST['app_title'] ?? 'UniFi Voucher System';
|
||
$logo_url = $_POST['logo_url'] ?? '';
|
||
$instruction_header = $_POST['instruction_header'] ?? '';
|
||
$instruction_text = $_POST['instruction_text'] ?? '';
|
||
$public_access = isset($_POST['public_access']) ? 1 : 0;
|
||
|
||
// Microsoft 365 OAuth (optional)
|
||
$m365_client_id = $_POST['m365_client_id'] ?? '';
|
||
$m365_client_secret = $_POST['m365_client_secret'] ?? '';
|
||
$m365_tenant_id = $_POST['m365_tenant_id'] ?? '';
|
||
|
||
$_SESSION['install_settings'] = [
|
||
'app_title' => $app_title,
|
||
'logo_url' => $logo_url,
|
||
'instruction_header' => $instruction_header,
|
||
'instruction_text' => $instruction_text,
|
||
'public_access' => $public_access,
|
||
'm365_client_id' => $m365_client_id,
|
||
'm365_client_secret' => $m365_client_secret,
|
||
'm365_tenant_id' => $m365_tenant_id
|
||
];
|
||
}
|
||
|
||
// Step 4: Installation abschließen
|
||
if ($step === 5 && $_SERVER['REQUEST_METHOD'] === 'POST') {
|
||
try {
|
||
$db = $_SESSION['install_db'];
|
||
$admin = $_SESSION['install_admin'];
|
||
$settings = $_SESSION['install_settings'];
|
||
|
||
$pdo = new PDO("mysql:host={$db['host']};dbname={$db['name']};charset=utf8mb4", $db['user'], $db['pass']);
|
||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||
|
||
// Admin-User erstellen
|
||
$stmt = $pdo->prepare("INSERT INTO users (email, name, password_hash, is_admin, is_active) VALUES (?, ?, ?, 1, 1)");
|
||
$stmt->execute([$admin['email'], $admin['name'], $admin['password']]);
|
||
|
||
// Settings speichern
|
||
$settingsData = [
|
||
'app_title' => $settings['app_title'],
|
||
'logo_url' => $settings['logo_url'],
|
||
'instruction_header' => $settings['instruction_header'],
|
||
'instruction_text' => $settings['instruction_text'],
|
||
'public_access' => $settings['public_access'],
|
||
'm365_client_id' => $settings['m365_client_id'],
|
||
'm365_client_secret' => $settings['m365_client_secret'],
|
||
'm365_tenant_id' => $settings['m365_tenant_id']
|
||
];
|
||
|
||
$stmt = $pdo->prepare("INSERT INTO settings (setting_key, setting_value) VALUES (?, ?) ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value)");
|
||
foreach ($settingsData as $key => $value) {
|
||
$stmt->execute([$key, $value]);
|
||
}
|
||
|
||
// config.php erstellen
|
||
$configContent = "<?php\n";
|
||
$configContent .= "// UniFi Voucher Management System - Configuration\n\n";
|
||
$configContent .= "define('DB_HOST', '{$db['host']}');\n";
|
||
$configContent .= "define('DB_NAME', '{$db['name']}');\n";
|
||
$configContent .= "define('DB_USER', '{$db['user']}');\n";
|
||
$configContent .= "define('DB_PASS', '" . addslashes($db['pass']) . "');\n\n";
|
||
$configContent .= "// Anwendungs-Schluessel fuer Verschluesselung-at-rest (z.B. UniFi-Passwoerter)\n";
|
||
$configContent .= "// NICHT aendern, sonst koennen bestehende verschluesselte Werte nicht mehr gelesen werden.\n";
|
||
$configContent .= "define('APP_KEY', '" . base64_encode(random_bytes(32)) . "');\n\n";
|
||
$configContent .= "// Sitzungs-Einstellungen\n";
|
||
$configContent .= "define('SESSION_LIFETIME', 3600); // 1 Stunde\n\n";
|
||
$configContent .= "// Zeitzone\n";
|
||
$configContent .= "date_default_timezone_set('Europe/Berlin');\n";
|
||
|
||
file_put_contents(__DIR__ . '/config.php', $configContent);
|
||
|
||
// .htaccess erstellen (ohne Rewrite Rules die Probleme machen)
|
||
$htaccess = "# UniFi Voucher System\n\n";
|
||
$htaccess .= "# Security\n";
|
||
$htaccess .= "<FilesMatch \"(config\\.php|database\\.sql|install\\.php|test\\.php|m365_debug\\.php|\\.md)$\">\n";
|
||
$htaccess .= " Order Allow,Deny\n";
|
||
$htaccess .= " Deny from all\n";
|
||
$htaccess .= "</FilesMatch>\n\n";
|
||
$htaccess .= "DirectoryIndex index.php\n";
|
||
file_put_contents(__DIR__ . '/.htaccess', $htaccess);
|
||
|
||
$success = true;
|
||
|
||
// Session-Daten löschen
|
||
unset($_SESSION['install_db'], $_SESSION['install_admin'], $_SESSION['install_settings']);
|
||
|
||
} catch (Exception $e) {
|
||
$errors[] = "Fehler bei der Installation: " . $e->getMessage();
|
||
$step = 4;
|
||
}
|
||
}
|
||
?>
|
||
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>UniFi Voucher System - Installation</title>
|
||
<?php require_once __DIR__ . '/includes/Ui.php'; ?>
|
||
<?= Ui::head(null) ?>
|
||
<style>
|
||
/* Installer-spezifisch: Fortschrittsanzeige und Abschnitte */
|
||
.install-head { display: flex; align-items: center; gap: 14px; margin-bottom: 4px; }
|
||
.progress { position: relative; display: flex; justify-content: space-between; margin: 26px 0 28px; }
|
||
.progress::before {
|
||
content: ''; position: absolute; top: 14px; left: 14px; right: 14px;
|
||
height: 2px; background: var(--border-color); z-index: 0;
|
||
}
|
||
.progress-step {
|
||
position: relative; z-index: 1;
|
||
width: 28px; height: 28px; border-radius: 50%;
|
||
display: flex; align-items: center; justify-content: center;
|
||
background: var(--bg-card); border: 1px solid var(--border-color);
|
||
color: var(--text-muted); font-size: 12px; font-weight: 650;
|
||
}
|
||
.progress-step.active { background: var(--accent); border-color: var(--accent); color: #fff; box-shadow: var(--ring); }
|
||
.progress-step.completed { background: var(--success); border-color: var(--success); color: #fff; }
|
||
.section {
|
||
padding: 18px;
|
||
margin-bottom: 20px;
|
||
background: var(--bg-subtle);
|
||
border: 1px solid var(--border-color);
|
||
border-radius: var(--r-md);
|
||
}
|
||
.section h3 { font-size: 14px; margin-bottom: 14px; }
|
||
.error, .success {
|
||
padding: 12px 15px; margin-bottom: 20px;
|
||
border-radius: var(--r-md); font-size: 13.5px;
|
||
border: 1px solid var(--danger-border); background: var(--danger-soft); color: var(--danger);
|
||
}
|
||
.success { border-color: var(--success-border); background: var(--success-soft); color: var(--success); }
|
||
form > h3 { font-size: 15px; margin-bottom: 16px; }
|
||
</style>
|
||
</head>
|
||
<body class="app-body focus-page">
|
||
<div class="focus-card card">
|
||
<div class="install-head">
|
||
<span class="focus-icon"><i class="fas fa-rocket" aria-hidden="true"></i></span>
|
||
<div>
|
||
<h1 style="font-size:20px;">UniFi Voucher System</h1>
|
||
<p class="sub">Installation in fünf Schritten</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="progress">
|
||
<div class="progress-step <?= $step >= 1 ? 'completed' : '' ?>">1</div>
|
||
<div class="progress-step <?= $step >= 2 ? 'completed' : ($step === 1 ? 'active' : '') ?>">2</div>
|
||
<div class="progress-step <?= $step >= 3 ? 'completed' : ($step === 2 ? 'active' : '') ?>">3</div>
|
||
<div class="progress-step <?= $step >= 4 ? 'completed' : ($step === 3 ? 'active' : '') ?>">4</div>
|
||
<div class="progress-step <?= $step >= 5 ? 'active' : '' ?>">5</div>
|
||
</div>
|
||
|
||
<?php if (!empty($errors)): ?>
|
||
<div class="error">
|
||
<?php foreach ($errors as $error): ?>
|
||
<div><?= htmlspecialchars($error) ?></div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<?php if ($success): ?>
|
||
<div class="success">
|
||
<strong>✓ Installation erfolgreich abgeschlossen!</strong><br>
|
||
Sie können sich jetzt mit Ihren Admin-Zugangsdaten anmelden.
|
||
</div>
|
||
<a href="index.php" class="btn btn-primary btn-lg btn-block">Zum Login</a>
|
||
<?php elseif ($step === 1): ?>
|
||
<form method="post">
|
||
<input type="hidden" name="step" value="2">
|
||
<h3>Schritt 1: Datenbank-Konfiguration</h3>
|
||
|
||
<div class="form-group">
|
||
<label>Datenbank-Host</label>
|
||
<input type="text" name="db_host" value="localhost" required>
|
||
<div class="help-text">Meist "localhost"</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>Datenbankname</label>
|
||
<input type="text" name="db_name" value="unifi_voucher" required>
|
||
<div class="help-text">Name der Datenbank (wird erstellt falls nicht vorhanden)</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>Datenbank-Benutzer</label>
|
||
<input type="text" name="db_user" required>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>Datenbank-Passwort</label>
|
||
<input type="password" name="db_pass">
|
||
</div>
|
||
|
||
<button type="submit" class="btn btn-primary btn-lg btn-block">Weiter <i class="fas fa-arrow-right" aria-hidden="true"></i></button>
|
||
</form>
|
||
|
||
<?php elseif ($step === 2): ?>
|
||
<form method="post">
|
||
<input type="hidden" name="step" value="3">
|
||
<h3>Schritt 2: Administrator-Account</h3>
|
||
|
||
<div class="form-group">
|
||
<label>Name</label>
|
||
<input type="text" name="admin_name" required>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>E-Mail</label>
|
||
<input type="email" name="admin_email" required>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>Passwort</label>
|
||
<input type="password" name="admin_password" required minlength="8">
|
||
<div class="help-text">Mindestens 8 Zeichen</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>Passwort bestätigen</label>
|
||
<input type="password" name="admin_password_confirm" required>
|
||
</div>
|
||
|
||
<button type="submit" class="btn btn-primary btn-lg btn-block">Weiter <i class="fas fa-arrow-right" aria-hidden="true"></i></button>
|
||
</form>
|
||
|
||
<?php elseif ($step === 3): ?>
|
||
<form method="post">
|
||
<input type="hidden" name="step" value="4">
|
||
<h3>Schritt 3: Allgemeine Einstellungen</h3>
|
||
|
||
<div class="form-group">
|
||
<label>Anwendungs-Titel</label>
|
||
<input type="text" name="app_title" value="UniFi Voucher System" required>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>Logo-URL (optional)</label>
|
||
<input type="text" name="logo_url" placeholder="https://example.com/logo.png">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>Anleitung Überschrift</label>
|
||
<input type="text" name="instruction_header" value="So verwenden Sie Ihren Code">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>Anleitung Text</label>
|
||
<textarea name="instruction_text">Verbinden Sie sich mit dem WLAN und geben Sie den Code auf der Anmeldeseite ein.</textarea>
|
||
</div>
|
||
|
||
<div class="form-group checkbox-group">
|
||
<input type="checkbox" name="public_access" id="public_access">
|
||
<label for="public_access" style="margin: 0;">Öffentlicher Zugriff auf Code-Erstellung</label>
|
||
</div>
|
||
|
||
<div class="section">
|
||
<h3>Microsoft 365 Login (Optional)</h3>
|
||
<div class="form-group">
|
||
<label>Client ID</label>
|
||
<input type="text" name="m365_client_id">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Client Secret</label>
|
||
<input type="password" name="m365_client_secret">
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Tenant ID</label>
|
||
<input type="text" name="m365_tenant_id">
|
||
</div>
|
||
<div class="help-text">Leer lassen, wenn M365-Login nicht verwendet werden soll</div>
|
||
</div>
|
||
|
||
<button type="submit" class="btn btn-primary btn-lg btn-block">Weiter <i class="fas fa-arrow-right" aria-hidden="true"></i></button>
|
||
</form>
|
||
|
||
<?php elseif ($step === 4): ?>
|
||
<form method="post">
|
||
<input type="hidden" name="step" value="5">
|
||
<h3>Schritt 4: Installation abschließen</h3>
|
||
|
||
<p style="margin-bottom: 20px; color: #666;">
|
||
Klicken Sie auf "Installation abschließen", um die Einrichtung zu beenden.
|
||
Die Datenbank und alle notwendigen Dateien werden erstellt.
|
||
</p>
|
||
|
||
<button type="submit" class="btn btn-primary btn-lg btn-block">Installation abschließen</button>
|
||
</form>
|
||
<?php endif; ?>
|
||
<?= Ui::credit() ?>
|
||
</div>
|
||
</body>
|
||
</html>
|