API-Schlüssel, Integration & Wartung, Voucher-Import, Backup & Restore, Reporting, Sicherheit (2FA) und Audit-Log waren fest auf Deutsch verdrahtet, obwohl in der Kopfzeile ein DE/EN-Umschalter sitzt. Diese Seiten laufen jetzt komplett über lang/de.php bzw. lang/en.php. - rund 200 neue Sprachschlüssel, beide Dateien deckungsgleich - Aktionsnamen im Audit-Log werden übersetzt statt fest ausgegeben - Bestätigungsdialoge und Statusmeldungen in JavaScript ebenfalls - admin/security.php initialisiert jetzt I18n und setzt <html lang> passend zur Auswahl Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
151 lines
7.5 KiB
PHP
151 lines
7.5 KiB
PHP
<?php
|
||
error_reporting(E_ALL);
|
||
ini_set('display_errors', 0);
|
||
ini_set('log_errors', 1);
|
||
|
||
require_once __DIR__ . '/../config.php';
|
||
require_once __DIR__ . '/../includes/Database.php';
|
||
require_once __DIR__ . '/../includes/Auth.php';
|
||
require_once __DIR__ . '/../includes/I18n.php';
|
||
|
||
$auth = new Auth();
|
||
$auth->requireAdmin();
|
||
I18n::init();
|
||
|
||
$db = Database::getInstance();
|
||
$appTitle = $db->getSetting('app_title', 'UniFi Voucher System');
|
||
|
||
$error = '';
|
||
$success = '';
|
||
|
||
// Export: JSON-Download
|
||
if (isset($_GET['export']) && isset($_GET['token']) && $auth->validateCsrfToken($_GET['token'])) {
|
||
$export = [
|
||
'meta' => [
|
||
'app' => 'unifi-voucher-tool',
|
||
'version' => '2.2.0',
|
||
'exported_at'=> date('c'),
|
||
'note' => 'Site-Passwörter sind mit dem APP_KEY dieser Installation verschlüsselt.',
|
||
],
|
||
'settings' => $db->fetchAll("SELECT setting_key, setting_value FROM settings"),
|
||
'sites' => $db->fetchAll("SELECT name, site_id, unifi_controller_url, unifi_username, unifi_password, is_active, public_access FROM sites"),
|
||
'voucher_templates' => $db->fetchAll("SELECT name, max_uses, expire_minutes, description, qos_rate_max_down, qos_rate_max_up, qos_usage_quota, is_active FROM voucher_templates"),
|
||
];
|
||
$auth->writeAuditLog($_SESSION['user_id'], 'config_export', 'config', null, 'Konfiguration exportiert');
|
||
header('Content-Type: application/json');
|
||
header('Content-Disposition: attachment; filename="voucher-config-' . date('Y-m-d') . '.json"');
|
||
echo json_encode($export, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||
exit;
|
||
}
|
||
|
||
// Import
|
||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['import'])) {
|
||
if (!$auth->validateCsrfToken($_POST['csrf_token'] ?? '')) {
|
||
$error = __('error_csrf');
|
||
} elseif (empty($_FILES['backup']['tmp_name'])) {
|
||
$error = __('backup_choose_file');
|
||
} else {
|
||
$raw = file_get_contents($_FILES['backup']['tmp_name']);
|
||
$data = json_decode($raw, true);
|
||
if (!is_array($data) || ($data['meta']['app'] ?? '') !== 'unifi-voucher-tool') {
|
||
$error = __('backup_invalid_file');
|
||
} else {
|
||
$importSites = isset($_POST['import_sites']);
|
||
$importTemplates = isset($_POST['import_templates']);
|
||
$importSettings = isset($_POST['import_settings']);
|
||
$counts = ['settings' => 0, 'sites' => 0, 'templates' => 0];
|
||
|
||
try {
|
||
if ($importSettings && !empty($data['settings'])) {
|
||
foreach ($data['settings'] as $s) {
|
||
// Cron-Token NICHT überschreiben (Sicherheit der Ziel-Installation)
|
||
if (($s['setting_key'] ?? '') === 'cron_token') continue;
|
||
$db->setSetting($s['setting_key'], $s['setting_value']);
|
||
$counts['settings']++;
|
||
}
|
||
}
|
||
if ($importSites && !empty($data['sites'])) {
|
||
foreach ($data['sites'] as $s) {
|
||
$exists = $db->fetchOne("SELECT id FROM sites WHERE name = ? AND site_id = ?", [$s['name'], $s['site_id']]);
|
||
if ($exists) {
|
||
$db->query(
|
||
"UPDATE sites SET unifi_controller_url=?, unifi_username=?, unifi_password=?, is_active=?, public_access=? WHERE id=?",
|
||
[$s['unifi_controller_url'], $s['unifi_username'], $s['unifi_password'], (int)$s['is_active'], (int)$s['public_access'], $exists['id']]
|
||
);
|
||
} else {
|
||
$db->query(
|
||
"INSERT INTO sites (name, site_id, unifi_controller_url, unifi_username, unifi_password, is_active, public_access) VALUES (?,?,?,?,?,?,?)",
|
||
[$s['name'], $s['site_id'], $s['unifi_controller_url'], $s['unifi_username'], $s['unifi_password'], (int)$s['is_active'], (int)$s['public_access']]
|
||
);
|
||
}
|
||
$counts['sites']++;
|
||
}
|
||
}
|
||
if ($importTemplates && !empty($data['voucher_templates'])) {
|
||
foreach ($data['voucher_templates'] as $t) {
|
||
$exists = $db->fetchOne("SELECT id FROM voucher_templates WHERE name = ?", [$t['name']]);
|
||
if (!$exists) {
|
||
$db->query(
|
||
"INSERT INTO voucher_templates (name, max_uses, expire_minutes, description, qos_rate_max_down, qos_rate_max_up, qos_usage_quota, is_active) VALUES (?,?,?,?,?,?,?,?)",
|
||
[$t['name'], (int)$t['max_uses'], (int)$t['expire_minutes'], $t['description'] ?? null,
|
||
$t['qos_rate_max_down'] ?? null, $t['qos_rate_max_up'] ?? null, $t['qos_usage_quota'] ?? null, (int)($t['is_active'] ?? 1)]
|
||
);
|
||
$counts['templates']++;
|
||
}
|
||
}
|
||
}
|
||
$auth->writeAuditLog($_SESSION['user_id'], 'config_import', 'config', null, 'Konfiguration importiert');
|
||
$success = str_replace(['{settings}', '{sites}', '{templates}'],
|
||
[(string)$counts['settings'], (string)$counts['sites'], (string)$counts['templates']],
|
||
__('backup_imported'));
|
||
} catch (Exception $e) {
|
||
$error = 'Import-Fehler: ' . $e->getMessage();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
$csrf = $auth->getCsrfToken();
|
||
$currentPage = 'backup';
|
||
$adminBase = '';
|
||
?>
|
||
<!DOCTYPE html>
|
||
<html lang="<?= I18n::getLanguage() ?>">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title><?= __('backup_title') ?> – <?= htmlspecialchars($appTitle) ?></title>
|
||
<?php require __DIR__ . '/../includes/admin_nav.php'; ?>
|
||
<div class="page-header">
|
||
<div>
|
||
<h1 class="page-title"><?= __('backup_title') ?></h1>
|
||
<p class="page-subtitle"><?= __('backup_subtitle') ?></p>
|
||
</div>
|
||
</div>
|
||
|
||
<?php if ($error): ?><div class="alert alert-error"><?= htmlspecialchars($error) ?></div><?php endif; ?>
|
||
<?php if ($success): ?><div class="alert alert-ok"><?= htmlspecialchars($success) ?></div><?php endif; ?>
|
||
|
||
<div class="card">
|
||
<h2><?= __('backup_export') ?></h2>
|
||
<p class="muted"><?= __('backup_export_hint') ?> <code>APP_KEY</code> <?= __('backup_export_hint2') ?></p>
|
||
<a class="btn btn-primary" href="?export=1&token=<?= urlencode($csrf) ?>"><?= __('backup_export_btn') ?></a>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<h2><?= __('backup_import') ?></h2>
|
||
<p class="muted"><?= __('backup_import_hint') ?></p>
|
||
<form method="post" enctype="multipart/form-data">
|
||
<input type="hidden" name="csrf_token" value="<?= htmlspecialchars($csrf) ?>">
|
||
<input type="file" name="backup" accept="application/json,.json" required><br>
|
||
<label class="chk"><input type="checkbox" name="import_settings" checked> <?= __('backup_opt_settings') ?></label>
|
||
<label class="chk"><input type="checkbox" name="import_sites" checked> <?= __('backup_opt_sites') ?></label>
|
||
<label class="chk"><input type="checkbox" name="import_templates" checked> <?= __('backup_opt_templates') ?></label>
|
||
<button class="btn btn-primary" type="submit" name="import" style="margin-top:12px;" onclick="return confirm('<?= __('backup_import_confirm') ?>');"><?= __('import_submit') ?></button>
|
||
</form>
|
||
</div>
|
||
|
||
</main>
|
||
<script src="../assets/global.js"></script>
|
||
</body>
|
||
</html>
|