255 lines
22 KiB
PHP
255 lines
22 KiB
PHP
<?php
|
||
// Seite „Synology NAS Backup“ (Menüpunkt). Verfügbar: $staff, $pdo. Nur für Administratoren (Berechtigung "manage_nas_backup").
|
||
$message = null;
|
||
$error = null;
|
||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||
verify_csrf();
|
||
$do = (string) ($_POST['do'] ?? '');
|
||
$r = nasbk_page_action($do, $_POST, $staff);
|
||
audit_log($staff['id'], 'Synology NAS Backup: ' . $do . ' (' . ($r['ok'] ? 'erfolgreich' : 'fehlgeschlagen') . ')');
|
||
// Post/Redirect/Get: Nach einem Klick auf die Seite selbst weiterleiten. Sonst schickt das automatische Neuladen (alle 10 Sekunden, solange ein
|
||
// Vorgang läuft) oder F5 den Klick noch einmal ab: „läuft bereits“ oder, sobald der erste fertig ist, ein ZWEITES Backup bzw. ein zweiter Upload.
|
||
// Plugin-Seiten werden erst nach der Kopfzeile des Layouts ausgegeben, eine Weiterleitung per Header ist dann nicht mehr möglich:
|
||
// die Seite ersetzt sich deshalb selbst durch die normale (GET-)Adresse. Die Meldung zeigt der Kern oben auf der nächsten Seite.
|
||
flash($r['ok'] ? 'success' : 'error', $r['message']);
|
||
$to = nasbk_page_url();
|
||
echo '<p class="nb-hint">Einen Moment …</p><script>location.replace(' . json_encode($to) . ');</script><noscript><meta http-equiv="refresh" content="0;url=' . e($to) . '"><a href="' . e($to) . '">Weiter</a></noscript>';
|
||
return;
|
||
}
|
||
$cfg = nasbk_config();
|
||
$hasPassword = $cfg['password'] !== '';
|
||
$running = nasbk_running();
|
||
$lastRun = (string) plugin_setting(NASBK, 'last_run', '');
|
||
$lastOk = plugin_setting(NASBK, 'last_ok', '') === '1';
|
||
$lastMsg = (string) plugin_setting(NASBK, 'last_message', '');
|
||
$lastOkAt = (string) plugin_setting(NASBK, 'last_ok_at', '');
|
||
$history = json_decode((string) plugin_setting(NASBK, 'history', '[]'), true);
|
||
$history = is_array($history) ? $history : [];
|
||
$remote = nasbk_config_error($cfg) === null ? nasbk_remote_cached($cfg) : null;
|
||
$fmtSize = fn(int $b) => $b >= 1048576 ? round($b / 1048576, 1) . ' MB' : max(1, round($b / 1024)) . ' KB';
|
||
$lastRunTs = $lastRun !== '' ? $lastRun : '';
|
||
$localDirReal = realpath(nasbk_local_dir());
|
||
$rows = [];
|
||
foreach (($localDirReal ? glob($localDirReal . '/backup_*.zip') : []) ?: [] as $p) {
|
||
if (!nasbk_valid_name(basename($p))) continue;
|
||
$rows[basename($p)] = ['name' => basename($p), 'size' => filesize($p), 'date' => filemtime($p), 'local' => true, 'remote' => null];
|
||
}
|
||
if ($remote !== null && $remote['ok']) {
|
||
foreach ($remote['items'] as $it) {
|
||
if (isset($rows[$it['name']])) { $rows[$it['name']]['remote'] = $it['size']; continue; }
|
||
$rows[$it['name']] = ['name' => $it['name'], 'size' => $it['size'], 'date' => strtotime($it['modified']) ?: 0, 'local' => false, 'remote' => $it['size']];
|
||
}
|
||
}
|
||
uasort($rows, fn($a, $b) => strcmp($b['name'], $a['name']));
|
||
$checkMap = json_decode((string) get_setting('backup_check_map', ''), true);
|
||
$checkMap = is_array($checkMap) ? $checkMap : [];
|
||
?>
|
||
<style>
|
||
.nb-card { margin-bottom:16px; }
|
||
.nb-card h3 { margin-top:0; padding-bottom:8px; border-bottom:1px solid var(--border); }
|
||
.nb-row { display:grid; grid-template-columns:200px minmax(0,1fr); gap:6px 18px; padding:11px 0; align-items:start; }
|
||
.nb-row > label.nb-l { font-weight:600; margin:8px 0 0; }
|
||
.nb-hint { font-size:.8rem; color:var(--muted); margin:4px 0 0; }
|
||
.nb-rule { background:#fffbeb; border:1px solid #fde68a; color:#92400e; border-radius:8px; padding:7px 11px; font-size:.85rem; margin-bottom:6px; }
|
||
.nb-radio { display:flex; align-items:flex-start; gap:8px; font-weight:400; margin:0 0 6px; }
|
||
.nb-radio input { width:auto; margin-top:3px; }
|
||
@media (max-width:760px) { .nb-row { grid-template-columns:1fr; } }
|
||
</style>
|
||
|
||
<?php if ($running): ?><script>setTimeout(function () { location.reload(); }, 10000);</script><?php endif; ?>
|
||
<h1>Synology NAS Backup</h1>
|
||
<p class="muted">Kopiert die Backups dieses Servers (Zeitplan und Knopf der Seite „Backup“) automatisch auf deine Synology NAS oder einen anderen WebDAV-Server und kann zusätzlich eigene Backups nach Zeitplan erstellen.</p>
|
||
<?php if ($message): ?><div class="flash success"><?= e($message) ?></div><?php endif; ?>
|
||
<?php if ($error): ?><div class="flash error"><?= e($error) ?></div><?php endif; ?>
|
||
|
||
<div class="card nb-card">
|
||
<h3>Status</h3>
|
||
<?php if ($running): ?>
|
||
<p style="margin:0"><strong style="color:#b45309">⏳ Ein Backup läuft gerade …</strong> <span class="muted" style="font-size:.85rem">Die Seite lädt sich von selbst neu.</span></p>
|
||
<?php elseif ($lastRun === ''): ?>
|
||
<p class="muted" style="margin:0">Noch kein Vorgang gelaufen.</p>
|
||
<?php else: ?>
|
||
<p style="margin:0"><strong style="color:<?= $lastOk ? '#15803d' : '#b91c1c' ?>"><?= $lastOk ? '✓ Letzter Vorgang erfolgreich' : '✗ Letzter Vorgang fehlgeschlagen' ?></strong> · <?= e($lastRun) ?></p>
|
||
<p class="muted" style="margin:4px 0 0;font-size:.88rem"><?= e($lastMsg) ?></p>
|
||
<?php endif; ?>
|
||
<?php if (!$cfg['enabled']): ?><p class="muted" style="margin:8px 0 0;font-size:.85rem">Das NAS-Backup ist ausgeschaltet.</p>
|
||
<?php elseif ($remote !== null && !$remote['ok']): ?><p style="margin:8px 0 0;font-size:.85rem;color:#b91c1c">✗ NAS nicht erreichbar: <?= e($remote['error']) ?></p>
|
||
<?php elseif ($remote !== null): ?><p class="muted" style="margin:8px 0 0;font-size:.85rem">✓ NAS erreichbar · <?= count($remote['items']) ?> Sicherung(en) dort<?= $cfg['mirror'] ? ' · neue Backups des Servers werden automatisch kopiert' : '' ?></p><?php endif; ?>
|
||
</div>
|
||
|
||
<div class="card nb-card">
|
||
<h3>Backups</h3>
|
||
<?php if (!$rows): ?>
|
||
<p class="muted" style="margin:0">Noch keine Backups. Sie entstehen auf der Seite „Backup“ (Zeitplan oder „Backup jetzt erstellen“) und werden dann hierher kopiert.</p>
|
||
<?php else: ?>
|
||
<table>
|
||
<thead><tr><th>Datei</th><th>Größe</th><th>Erstellt</th><th>Status</th><th></th></tr></thead>
|
||
<tbody>
|
||
<?php foreach ($rows as $b): $chk = $checkMap[$b['name']] ?? null; $onNas = $b['remote'] !== null; $same = $onNas && $b['local'] && $b['remote'] === $b['size']; ?>
|
||
<tr>
|
||
<td style="font-family:monospace;font-size:.82rem"><?= e($b['name']) ?></td>
|
||
<td class="muted"><?= e($fmtSize((int) $b['size'])) ?></td>
|
||
<td class="muted"><?= $b['date'] ? e(date('d.m.Y H:i', (int) $b['date'])) : '–' ?></td>
|
||
<td style="white-space:nowrap">
|
||
<?php if ($b['local']): ?><span class="muted" style="font-size:.78rem">auf dem Server</span> <?= $chk ? ($chk['ok'] ? '<span style="color:#15803d;font-size:.78rem">✔ geprüft</span>' : '<span style="color:#b91c1c;font-size:.78rem">✖ defekt</span>') : '<span class="muted" style="font-size:.78rem">nicht geprüft</span>' ?><br><?php endif; ?>
|
||
<?php if ($same): ?><span style="color:#1d4ed8;font-size:.78rem">☁ auf der NAS</span>
|
||
<?php elseif ($onNas && $b['local']): ?><span style="color:#b91c1c;font-size:.78rem">☁ unvollständig auf der NAS</span>
|
||
<?php elseif ($onNas): ?><span style="color:#1d4ed8;font-size:.78rem">☁ nur auf der NAS</span>
|
||
<?php elseif ($remote !== null && $remote['ok']): ?><span class="muted" style="font-size:.78rem">noch nicht auf der NAS</span><?php endif; ?>
|
||
</td>
|
||
<td><div style="display:flex;gap:5px;justify-content:flex-end">
|
||
<?php if ($b['local'] && !$same && $remote !== null && $remote['ok']) echo nasbk_action_form('upload', '☁ Auf NAS', ['file' => $b['name']]); ?>
|
||
<?php if (!$b['local']) echo nasbk_action_form('fetch', '⬇ Auf Server holen', ['file' => $b['name']], 'Diese Sicherung von der NAS auf den Server laden? Danach kannst du sie auf der Seite „Backup“ wiederherstellen.') . nasbk_action_form('delete_remote', '🗑', ['file' => $b['name']], 'Diese Sicherung von der NAS löschen?', 'secondary', 'border-color:var(--danger);color:var(--danger)'); ?>
|
||
</div></td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
<p class="nb-hint">Wiederherstellen und Löschen auf dem Server machst du auf der Seite „Backup“. Sicherungen, die nur auf der NAS liegen, holst du hier zuerst auf den Server.</p>
|
||
<?php endif; ?>
|
||
</div>
|
||
<form method="post" id="nb-form">
|
||
<?= csrf_field() ?>
|
||
<input type="hidden" name="do" value="save">
|
||
|
||
<div class="card nb-card">
|
||
<h3>🔌 Verbindung zur NAS</h3>
|
||
<label style="display:flex;align-items:center;gap:8px;margin:0 0 6px"><input type="checkbox" name="enabled" value="1" style="width:auto" <?= $cfg['enabled'] ? 'checked' : '' ?>> <strong>NAS-Backup verwenden</strong></label>
|
||
<div class="nb-row"><label class="nb-l" for="host">IP-Adresse oder Domain</label>
|
||
<div><input type="text" id="host" name="host" value="<?= e($cfg['host']) ?>" placeholder="nas.meinedomain.de" autocomplete="off">
|
||
<p class="nb-hint">Von außen erreichbar: Domain oder DynDNS, zum Beispiel <code>meine-nas.synology.me</code>. Dein Server läuft nicht in deinem Netz, die NAS muss also aus dem Internet erreichbar sein.</p></div></div>
|
||
<div class="nb-row"><label class="nb-l" for="port">Port</label>
|
||
<div><input type="number" id="port" name="port" value="<?= (int) $cfg['port'] ?>" min="1" max="65535" style="max-width:130px">
|
||
<p class="nb-hint">Standard Synology WebDAV: <strong>5005</strong> (HTTP) | <strong>5006</strong> (HTTPS). Über einen Reverse-Proxy: 443.</p></div></div>
|
||
<div class="nb-row"><label class="nb-l" for="user">Benutzername</label><div><input type="text" id="user" name="user" value="<?= e($cfg['user']) ?>" autocomplete="off"></div></div>
|
||
<div class="nb-row"><label class="nb-l" for="password">Passwort</label>
|
||
<div><input type="password" id="password" name="password" autocomplete="new-password" placeholder="<?= $hasPassword ? '•••••••• (gespeichert, leer lassen = behalten)' : '' ?>">
|
||
<p class="nb-hint">Am besten ein eigener NAS-Benutzer, der nur auf den Backup-Ordner zugreifen darf. Das Passwort wird verschlüsselt gespeichert.</p></div></div>
|
||
<div class="nb-row"><label class="nb-l">HTTPS (SSL)</label>
|
||
<div><label class="nb-radio" style="margin:0"><input type="checkbox" id="https" name="https" value="1" <?= $cfg['https'] ? 'checked' : '' ?>> Verschlüsselte Verbindung (HTTPS) verwenden</label>
|
||
<p class="nb-hint">Ohne HTTPS ist nur eine Adresse im eigenen Netz erlaubt, sonst würden Passwort und Backup unverschlüsselt durchs Internet gehen.</p></div></div>
|
||
<div class="nb-row"><label class="nb-l">Server-URL (Vorschau)</label><div><code id="nb-preview" style="display:inline-block;padding:6px 10px;border:1px solid var(--border);border-radius:6px">—</code></div></div>
|
||
<div class="nb-row"><label class="nb-l" for="target">Zielverzeichnis auf der NAS</label>
|
||
<div><input type="text" id="target" name="target" value="<?= e($cfg['target']) ?>" placeholder="/backup/DeskHub/">
|
||
<p class="nb-hint">Beginnt mit dem Namen des freigegebenen Ordners, zum Beispiel <code>/usbshare2/Geschichten/</code>. Unterordner legt das Plugin an, die Freigabe selbst muss es auf der NAS schon geben.</p></div></div>
|
||
<div class="nb-row"><label class="nb-l">Zertifikat</label>
|
||
<div><label class="nb-radio" style="margin:0"><input type="checkbox" name="verify_tls" value="1" <?= $cfg['verify_tls'] ? 'checked' : '' ?>> Zertifikat der NAS prüfen (empfohlen)</label>
|
||
<p class="nb-hint">Nur abschalten, wenn die NAS ein selbst erstelltes Zertifikat hat. Dann ist die Verbindung nicht mehr gegen Mithören und Fälschung geschützt. Besser: kostenloses Zertifikat von Let’s Encrypt in der NAS einrichten.</p></div></div>
|
||
</div>
|
||
|
||
<div class="card nb-card">
|
||
<h3>🔁 Zusammenarbeit mit der Backup-Seite</h3>
|
||
<label class="nb-radio"><input type="checkbox" name="mirror" value="1" <?= $cfg['mirror'] ? 'checked' : '' ?>>
|
||
<span><strong>Jedes Backup von DeskHub automatisch auf die NAS kopieren</strong><br><span class="nb-hint">Gilt für den Zeitplan der Seite „Backup“, für manuelle Backups und für Backups vor Updates. Die Kopie läuft nach dem Backup im Hintergrund und bremst es nicht. Auf der Seite „Backup“ siehst du bei jeder Sicherung, ob sie auf der NAS liegt, kannst Sicherungen auf die NAS legen und Sicherungen, die nur dort liegen, auf den Server holen und wiederherstellen.</span></span></label>
|
||
</div>
|
||
|
||
<div class="card nb-card">
|
||
<h3>📦 Backup-Inhalt <span class="muted" style="font-size:.8rem;font-weight:400">(für die eigenen Backups des Plugins)</span></h3>
|
||
<p class="nb-hint" style="margin:0 0 6px">Datenbank und Dateien werden in <strong>ein einziges ZIP-Archiv</strong> (<code>backup_DATUM_UHRZEIT.zip</code>) gepackt und auf die NAS hochgeladen. Das Archiv lässt sich auf der Seite „Backup“ wiederherstellen.</p>
|
||
<div class="nb-row"><label class="nb-l">Was soll gesichert werden?</label>
|
||
<div>
|
||
<label class="nb-radio"><input type="checkbox" name="db" value="1" <?= $cfg['with_database'] ? 'checked' : '' ?>><span><strong>Datenbank</strong> – alle Tabellen</span></label>
|
||
<label class="nb-radio"><input type="checkbox" name="files" value="1" <?= $cfg['with_files'] ? 'checked' : '' ?>><span><strong>Dateien</strong></span></label>
|
||
</div></div>
|
||
<div class="nb-row"><label class="nb-l">Datei-Umfang</label>
|
||
<div>
|
||
<label class="nb-radio"><input type="radio" name="scope" value="important" <?= $cfg['scope'] === 'important' ? 'checked' : '' ?>><span><strong>Wichtigste Dateien</strong> – <code>uploads/</code>, <code>plugins/</code>, <code>config/</code>, <code>assets/branding/</code>, <code>.htaccess</code></span></label>
|
||
<label class="nb-radio"><input type="radio" name="scope" value="full" <?= $cfg['scope'] === 'full' ? 'checked' : '' ?>><span><strong>Vollständige DeskHub-Installation</strong> (alle Programmdateien)</span></label>
|
||
</div></div>
|
||
<div class="nb-row"><label class="nb-l" for="excludes">Pfade ausschließen</label>
|
||
<div><textarea id="excludes" name="excludes" rows="4" placeholder="uploads/2020 app_downloads"><?= e(implode("\n", $cfg['excludes'])) ?></textarea>
|
||
<p class="nb-hint">Ein Pfad pro Zeile, relativ zum DeskHub-Hauptordner. Beispiel: <code>uploads/2020</code> oder <code>app_downloads</code>.</p></div></div>
|
||
</div>
|
||
|
||
<div class="card nb-card">
|
||
<h3>🕐 Backup-Zeitplan <span class="muted" style="font-size:.8rem;font-weight:400">(optional, zusätzlich)</span></h3>
|
||
<label class="nb-radio"><input type="checkbox" name="own_schedule" value="1" <?= $cfg['own_schedule'] ? 'checked' : '' ?>>
|
||
<span><strong>Das Plugin erstellt zusätzlich eigene Backups nach diesem Zeitplan</strong><br><span class="nb-hint">Nur nötig, wenn du auf der Seite „Backup“ keinen Zeitplan nutzt oder die NAS häufiger sichern soll. Diese Backups liegen nur auf der NAS, nicht auf dem Server.</span></span></label>
|
||
<div class="nb-row"><label class="nb-l" for="interval">Intervall</label>
|
||
<div><select id="interval" name="interval" style="max-width:220px"><?php foreach (nasbk_intervals() as $k => [$label]): ?><option value="<?= e($k) ?>" <?= $cfg['interval'] === $k ? 'selected' : '' ?>><?= e($label) ?></option><?php endforeach; ?></select></div></div>
|
||
<div class="nb-row"><label class="nb-l" for="time">Uhrzeit</label>
|
||
<div><input type="time" id="time" name="time" value="<?= e($cfg['time']) ?>" style="max-width:160px">
|
||
<p class="nb-hint">Gilt für tägliche, wöchentliche und monatliche Backups (Serverzeit). Bei stündlichen und mehrstündigen Intervallen wird die Uhrzeit ignoriert.<br>Der Zeitplan läuft mit, sobald jemand im Adminportal aktiv ist oder ein Cronjob DeskHub aufruft.</p></div></div>
|
||
</div>
|
||
|
||
<div class="card nb-card">
|
||
<h3>🗑 Aufbewahrung & Bereinigung</h3>
|
||
<p class="nb-hint" style="margin:0 0 6px">Beide Regeln gelten gleichzeitig – ein Backup wird gelöscht, sobald <em>eine</em> davon zutrifft. Es werden nur Dateien gelöscht, die dieses Plugin selbst angelegt hat (<code>backup_….zip</code>), eigene Dateien im Ordner bleiben unberührt.</p>
|
||
<div class="nb-row"><label class="nb-l" for="keep_days">Maximales Alter</label>
|
||
<div><input type="number" id="keep_days" name="keep_days" min="0" max="3650" value="<?= (int) $cfg['keep_days'] ?>" style="max-width:110px"> Tage
|
||
<p class="nb-hint">Backups älter als diese Anzahl Tage werden gelöscht. <strong>0 = deaktiviert.</strong></p></div></div>
|
||
<div class="nb-row"><label class="nb-l" for="keep">Maximale Anzahl</label>
|
||
<div><input type="number" id="keep" name="keep" min="0" max="365" value="<?= (int) $cfg['keep'] ?>" style="max-width:110px"> Backups
|
||
<p class="nb-hint">Es werden maximal so viele <code>backup_*.zip</code>-Dateien auf der NAS behalten. Überschüssige werden – älteste zuerst – gelöscht. <strong>0 = deaktiviert.</strong></p></div></div>
|
||
<div class="nb-row"><label class="nb-l">Aktuell aktive Regeln</label><div id="nb-rules"></div></div>
|
||
</div>
|
||
|
||
<div class="card nb-card">
|
||
<h3>📦 Große Backups in Stücken hochladen</h3>
|
||
<p class="nb-hint" style="margin:0 0 6px">Manche Server oder Proxys vor der NAS nehmen nur Dateien bis zu einer Größe an (zum Beispiel Cloudflare: 100 MB, Fehler „HTTP 413“). Dann wird das Backup in Stücken hochgeladen. <strong>Bevorzugt als eine einzige Datei</strong>: Jedes Stück sagt der NAS per <code>Content-Range</code>, wohin es gehört (kann z. B. Synology DSM 7), und auf der NAS liegt am Ende direkt die fertige <code>.zip</code>. Nach jedem Stück wird die Größe geprüft. Kann die NAS das nicht, lädt das Plugin ersatzweise Teile (<code>backup_….zip.part001</code> …) hoch und legt erst danach eine kleine Abschlussdatei an; beim Zurückholen setzt es sie wieder zusammen und prüft die Prüfsumme.</p>
|
||
<div class="nb-row"><label class="nb-l" for="split">Wann in Stücken?</label>
|
||
<div><select id="split" name="split" style="max-width:360px">
|
||
<option value="auto" <?= $cfg['split'] === 'auto' ? 'selected' : '' ?>>Automatisch (erst ganz, bei Fehler in Stücken)</option>
|
||
<option value="always" <?= $cfg['split'] === 'always' ? 'selected' : '' ?>>Immer, wenn größer als die Stückgröße</option>
|
||
<option value="never" <?= $cfg['split'] === 'never' ? 'selected' : '' ?>>Nie (immer als eine Datei)</option>
|
||
</select>
|
||
<p class="nb-hint">„Automatisch“ lässt kleine und mittlere Backups als normale <code>.zip</code> auf der NAS. Scheitert der Upload an einer Größengrenze, lädt das Plugin die Datei in Stücken hoch und merkt sich die Größe, damit solche Backups künftig gleich in Stücken gehen.</p></div></div>
|
||
<div class="nb-row"><label class="nb-l" for="part_mb">Stückgröße</label>
|
||
<div><input type="number" id="part_mb" name="part_mb" min="10" max="4000" value="<?= (int) $cfg['part_mb'] ?>" style="max-width:110px"> MB
|
||
<p class="nb-hint">Größe eines Stücks, zwischen 10 und 4000 MB (Standard 200 MB). Die Stücke werden direkt aus dem Backup gesendet (keine Zwischenkopie). Die Gesamtdauer hängt vor allem von der Upload-Geschwindigkeit ab; größere Stücke sparen nur wenige Anfragen, kleinere verlieren bei einem Verbindungsabbruch weniger. Lehnt der Server ein Stück als zu groß ab (HTTP 413), halbiert das Plugin die Größe selbst, bis es klappt, und merkt sich den Wert. Hinter Cloudflare (Gratis-Tarif) sind höchstens 100 MB je Stück möglich, dort 50 MB einstellen.</p></div></div>
|
||
</div>
|
||
|
||
<div class="btn-row"><button type="submit" class="btn">Einstellungen speichern</button></div>
|
||
</form>
|
||
|
||
<div class="card nb-card" style="margin-top:16px">
|
||
<h3>Ausprobieren</h3>
|
||
<div style="display:flex;gap:10px;flex-wrap:wrap">
|
||
<form method="post"><?= csrf_field() ?><input type="hidden" name="do" value="test"><button type="submit" class="btn secondary">🔍 Verbindung testen</button></form>
|
||
<form method="post"><?= csrf_field() ?><input type="hidden" name="do" value="sync"><button type="submit" class="btn secondary">☁ Fehlende Backups jetzt kopieren</button></form>
|
||
<form method="post"><?= csrf_field() ?><input type="hidden" name="do" value="run"><button type="submit" class="btn" <?= $running ? 'disabled' : '' ?>>▶ Eigenes Backup jetzt starten</button></form>
|
||
</div>
|
||
<p class="nb-hint">Speichere zuerst. Der Verbindungstest legt den Ordner an, schreibt eine kleine Testdatei und löscht sie wieder. „Eigenes Backup jetzt starten“ erzeugt ein Backup mit den Einstellungen oben, lädt es hoch und läuft im Hintergrund. Die vorhandenen Backups des Servers verwaltest du auf der Seite „Backup“.</p>
|
||
</div>
|
||
|
||
|
||
<?php if ($history): ?>
|
||
<div class="card nb-card">
|
||
<h3>Verlauf</h3>
|
||
<?php foreach ($history as $h): ?>
|
||
<div style="font-size:.85rem;padding:4px 0;border-bottom:1px solid var(--border)">
|
||
<strong style="color:<?= !empty($h['ok']) ? '#15803d' : '#b91c1c' ?>"><?= !empty($h['ok']) ? '✓' : '✗' ?></strong>
|
||
<?= e((string) ($h['time'] ?? '')) ?><?= !empty($h['manual']) ? ' <span class="muted">(manuell)</span>' : '' ?> – <span class="muted"><?= e((string) ($h['message'] ?? '')) ?></span>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<script>
|
||
(function () {
|
||
var $ = function (id) { return document.getElementById(id); };
|
||
function preview() {
|
||
var host = $('host').value.trim(), ssl = $('https').checked, port = $('port').value.trim();
|
||
var def = ssl ? '443' : '80';
|
||
$('nb-preview').textContent = host ? (ssl ? 'https' : 'http') + '://' + host + (port && port !== def ? ':' + port : '') + ($('target').value ? '/' + $('target').value.replace(/^\/+|\/+$/g, '') + '/' : '/') : '—';
|
||
}
|
||
function rules() {
|
||
var days = parseInt($('keep_days').value, 10) || 0, count = parseInt($('keep').value, 10) || 0, out = '';
|
||
if (days > 0) {
|
||
var d = new Date(Date.now() - days * 86400000);
|
||
out += '<div class="nb-rule">📅 Backups älter als ' + d.toLocaleDateString('de-DE') + ' (' + days + ' Tage) werden gelöscht</div>';
|
||
}
|
||
if (count > 0) out += '<div class="nb-rule">🔢 Es werden maximal ' + count + ' Backups auf der NAS behalten</div>';
|
||
if (!out) out = '<div class="nb-hint" style="margin:0">Keine Regel aktiv – Backups werden nie automatisch von der NAS gelöscht.</div>';
|
||
$('nb-rules').innerHTML = out;
|
||
}
|
||
['host', 'port', 'https', 'target'].forEach(function (id) { $(id).addEventListener('input', preview); $(id).addEventListener('change', preview); });
|
||
['keep_days', 'keep'].forEach(function (id) { $(id).addEventListener('input', rules); });
|
||
$('https').addEventListener('change', function () {
|
||
var p = $('port'); if (p.value === '80' && this.checked) p.value = '443'; else if (p.value === '443' && !this.checked) p.value = '80';
|
||
preview();
|
||
});
|
||
preview(); rules();
|
||
})();
|
||
</script>
|