PDO::ERRMODE_EXCEPTION, PDO::ATTR_TIMEOUT => 3]);
} else {
$path = (string) ($db['sqlite_path'] ?? '');
if ($path === '' || !is_file($path)) return false;
$pdo = new PDO('sqlite:' . $path, null, null, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
}
return (int) $pdo->query('SELECT COUNT(*) FROM staff')->fetchColumn() > 0;
} catch (Throwable $e) {
return false; // keine nutzbare Datenbank: Erstinstallation ist in Ordnung
}
})();
$alreadyInstalled = file_exists(LOCK_FILE) || $existingInstallation;
$errors = [];
$success = false;
// ---------------------------------------------------------------
// Anforderungsprüfung
// ---------------------------------------------------------------
$checks = [
['label' => 'PHP-Version 8.1 oder neuer (gefunden: ' . PHP_VERSION . ')', 'ok' => version_compare(PHP_VERSION, '8.1.0', '>=')],
['label' => 'PDO-Erweiterung', 'ok' => extension_loaded('pdo')],
['label' => 'PDO MySQL-Treiber (für Produktivbetrieb)', 'ok' => extension_loaded('pdo_mysql'), 'optional' => true],
['label' => 'PDO SQLite-Treiber (optional, für lokale Tests)', 'ok' => extension_loaded('pdo_sqlite'), 'optional' => true],
['label' => 'mbstring-Erweiterung', 'ok' => extension_loaded('mbstring')],
['label' => 'IMAP-Erweiterung (optional, nur für E-Mail-zu-Ticket)', 'ok' => extension_loaded('imap'), 'optional' => true],
['label' => 'Verzeichnis config/ ist beschreibbar', 'ok' => is_writable(ROOT_DIR . '/config')],
['label' => 'Verzeichnis uploads/ ist beschreibbar', 'ok' => is_writable(ROOT_DIR . '/uploads')],
['label' => 'Verzeichnis database/ ist beschreibbar', 'ok' => is_writable(ROOT_DIR . '/database')],
];
$requiredOk = true;
foreach ($checks as $c) {
if (empty($c['optional']) && !$c['ok']) {
$requiredOk = false;
}
}
// ---------------------------------------------------------------
// Installation durchführen
// ---------------------------------------------------------------
if ($_SERVER['REQUEST_METHOD'] === 'POST' && !$alreadyInstalled && $requiredOk) {
if (empty($_SESSION['install_csrf']) || !hash_equals($_SESSION['install_csrf'], $_POST['csrf_token'] ?? '')) {
$errors[] = 'Ungültige Anfrage (Sitzung abgelaufen). Bitte die Seite neu laden und erneut versuchen.';
} else {
$driver = ($_POST['driver'] ?? '') === 'mysql' ? 'mysql' : 'sqlite';
$siteName = trim($_POST['site_name'] ?? '') ?: 'DeskHub Support';
$siteUrl = rtrim(trim($_POST['site_url'] ?? ''), '/');
$adminName = trim($_POST['admin_name'] ?? '');
$adminEmail = trim($_POST['admin_email'] ?? '');
$adminPass = $_POST['admin_password'] ?? '';
$adminPass2 = $_POST['admin_password2'] ?? '';
if ($siteUrl === '') $errors[] = 'Bitte die URL der Installation angeben (z.B. https://support.firma.de).';
if ($adminName === '') $errors[] = 'Bitte einen Namen für das Admin-Konto angeben.';
if (!filter_var($adminEmail, FILTER_VALIDATE_EMAIL)) $errors[] = 'Bitte eine gültige Admin-E-Mail-Adresse angeben.';
if (strlen($adminPass) < 8) $errors[] = 'Das Admin-Passwort muss mindestens 8 Zeichen lang sein.';
if ($adminPass !== $adminPass2) $errors[] = 'Die beiden Passwörter stimmen nicht überein.';
if (!extension_loaded('pdo_' . $driver)) $errors[] = "Die PHP-Erweiterung pdo_{$driver} ist nicht installiert.";
$dbConfig = [
'driver' => $driver,
'host' => 'localhost',
'name' => 'deskhub',
'user' => 'deskhub_user',
'pass' => 'change_me',
'sqlite_path' => ROOT_DIR . '/database/deskhub.sqlite',
];
if ($driver === 'mysql') {
$dbConfig['host'] = trim($_POST['db_host'] ?? 'localhost') ?: 'localhost';
$dbConfig['name'] = trim($_POST['db_name'] ?? '');
$dbConfig['user'] = trim($_POST['db_user'] ?? '');
$dbConfig['pass'] = $_POST['db_pass'] ?? '';
if ($dbConfig['name'] === '' || $dbConfig['user'] === '') {
$errors[] = 'Bitte Datenbankname und Datenbankbenutzer angeben.';
}
}
$pdo = null;
if (!$errors) {
try {
if ($driver === 'mysql') {
$dsn = sprintf('mysql:host=%s;dbname=%s;charset=utf8mb4', $dbConfig['host'], $dbConfig['name']);
$pdo = new PDO($dsn, $dbConfig['user'], $dbConfig['pass']);
} else {
@unlink($dbConfig['sqlite_path']);
$pdo = new PDO('sqlite:' . $dbConfig['sqlite_path']);
$pdo->exec('PRAGMA foreign_keys = ON;');
}
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (Throwable $e) {
$errors[] = 'Datenbankverbindung fehlgeschlagen: ' . $e->getMessage();
}
}
if (!$errors && $pdo) {
try {
$sql = file_get_contents(ROOT_DIR . '/database/schema.sql');
if ($driver === 'sqlite') {
$sql = convert_schema_to_sqlite($sql);
}
$pdo->exec($sql);
// Vordefiniertes Demo-Admin-Konto entfernen und durch das hier
// eingegebene, echte Admin-Konto ersetzen.
$pdo->exec("DELETE FROM staff WHERE email = 'admin@example.com'");
$stmt = $pdo->prepare(
'INSERT INTO staff (name, email, password_hash, role, department_id, is_active, created_at) VALUES (?,?,?,?,?,1,?)'
);
$stmt->execute([$adminName, $adminEmail, password_hash($adminPass, PASSWORD_DEFAULT), 'admin', 1, date('Y-m-d H:i:s')]);
// ── Initiale Einstellungen in die DB seeden ───────────────────────
// Alle Kerneinstellungen sofort in der settings-Tabelle anlegen,
// damit sie Updates überleben (config.php wird beim Update nie
// überschrieben, aber so sind die Werte auch bei Neuinstallationen
// sofort über get_setting() abrufbar).
// Sicherer, zufälliger App-Key – direkt hier generieren, damit er
// sowohl in die DB (überlebt Updates) als auch in config.php geschrieben wird.
$appKey = bin2hex(random_bytes(32));
$installSettings = [
'app_key' => $appKey, // ← in DB: überlebt Updates
'display_name' => $siteName,
'site_url' => $siteUrl,
'timezone' => 'Europe/Berlin',
'smtp_from_email' => $adminEmail,
'smtp_from_name' => $siteName,
'mail_enabled' => '0',
'helpdesk_status' => 'online',
'kb_enabled' => '1',
'ticket_ratings_enabled' => '1',
'staff_session_timeout' => '30',
'retention_enabled' => '0',
'retention_months' => '24',
'ip_anonymize' => '0',
'upload_allowed_extensions' => 'png, jpg, jpeg, gif, pdf, txt, log, docx, xlsx, zip',
'upload_max_size_mb' => '10',
];
$upsertSql = $driver === 'sqlite'
? 'INSERT OR REPLACE INTO settings (setting_key, setting_value) VALUES (?, ?)'
: 'INSERT INTO settings (setting_key, setting_value) VALUES (?, ?) ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value)';
foreach ($installSettings as $k => $v) {
$pdo->prepare($upsertSql)->execute([$k, $v]);
}
// Alle vorhandenen Migrationsdateien sofort als "eingespielt" markieren,
// da das Schema gerade frisch erstellt wurde.
$migrTable = $driver === 'sqlite'
? "CREATE TABLE IF NOT EXISTS schema_migrations (migration VARCHAR(255) PRIMARY KEY, applied_at DATETIME NOT NULL DEFAULT (datetime('now')))"
: "CREATE TABLE IF NOT EXISTS schema_migrations (migration VARCHAR(255) PRIMARY KEY, applied_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4";
$pdo->exec($migrTable);
$insertMigr = $driver === 'sqlite'
? 'INSERT OR IGNORE INTO schema_migrations (migration) VALUES (?)'
: 'INSERT IGNORE INTO schema_migrations (migration) VALUES (?)';
foreach (glob(ROOT_DIR . '/database/migrations/*.sql') ?: [] as $mf) {
$pdo->prepare($insertMigr)->execute([basename($mf)]);
}
} catch (Throwable $e) {
$errors[] = 'Fehler beim Einrichten der Datenbank: ' . $e->getMessage();
}
}
if (!$errors) {
$configArray = [
'app_key' => $appKey,
'db' => $dbConfig,
'site' => [
'name' => $siteName,
'url' => $siteUrl,
'admin_email' => $adminEmail,
'timezone' => 'Europe/Berlin',
],
'mail' => [
'enabled' => false,
'from_email' => $adminEmail,
'from_name' => $siteName,
],
'imap' => [
'enabled' => false,
'mailbox' => '{imap.example.com:993/imap/ssl}INBOX',
'user' => 'support@example.com',
'pass' => 'change_me',
'default_department_id' => 1,
'default_priority_id' => 2,
],
'uploads' => [
'max_size_bytes' => 10 * 1024 * 1024,
'allowed_extensions' => ['png','jpg','jpeg','gif','pdf','txt','log','docx','xlsx','zip'],
'path' => ROOT_DIR . '/uploads',
],
];
$php = "
DeskHub – Installation
🔒
Bereits installiert
Eingerichtet am = h(is_file(LOCK_FILE) ? (string) file_get_contents(LOCK_FILE) : "bereits vorhandener Konfiguration") ?>
⚠️
Bitte das Verzeichnis install/ vom Server löschen, um diese Seite zu deaktivieren.
Zum Wiederholen: install/.installed löschen
✅
Installation abgeschlossen!
DeskHub wurde erfolgreich eingerichtet.
🗑️
Wichtig: Löschen Sie jetzt das Verzeichnis install/ vom Server, damit niemand die Installation wiederholen kann.
1 Systemvoraussetzungen
-
= $ico ?>
= h($c['label']) ?>
✗
Nicht alle Pflichtvoraussetzungen erfüllt. Bitte die rot markierten Punkte beheben und die Seite neu laden.
✗= h($err) ?>