Unifi-Voucher-Tool/admin/backup.php
Friederich Loheide 850a04d628 Englische Übersetzungen für die restlichen Admin-Seiten
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>
2026-09-23 06:34:25 +00:00

151 lines
7.5 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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>