feat: eine Startseite, und die Sicherung geht in die Nextcloud
STARTSEITE. Wer das Dashboard oeffnet, landete bisher in der Galerie -- mit 228 Kacheln und ohne Antwort auf die erste Frage, die man beim Oeffnen hat: laeuft alles? Jetzt zuerst eine Uebersicht: Zahlen, was heute dazukam, ein Bilderstreifen der letzten Uploads, die letzten acht Schritte aus dem Verlauf, die groessten Ordner, die Zugaenge -- und die ADRESSVORLAGE zum Kopieren, weil das die haeufigste Frage ueberhaupt ist. Die Galerie zieht auf /galerie um. Alles in EINER Antwort (/api/dash/uebersicht, gemessen 14 ms). Vier Einzelabfragen waeren vier Runden ueber den Proxy und koennten auseinander- laufen: Dateizahl aus der einen und letzte Ereignisse aus der naechsten Antwort sind zwei verschiedene Zeitpunkte. Der Router zog dabei nach src/router.ts um. Der Grund ist mehr als Geschmack: die Startseite braucht eine Navigationsfunktion, die anderen Seiten nicht -- und sobald EINE Seite eine Eigenschaft mehr hat, passt die gemeinsame Liste in App.tsx nicht mehr. Genau daran ist der erste Versuch gescheitert (TypeScript hat es gefangen). Als Modulfunktion hat wieder jede Seite dieselbe Signatur. SICHERUNG IN DIE NEXTCLOUD. Bisher lagen die Archive auf derselben Platte wie die Daten -- gegen einen Plattenausfall half das gar nicht. Jetzt gehen sie per WebDAV hinauf, mit curl statt rclone: Nextcloud spricht WebDAV, curl spricht WebDAV, und curl ist ohnehin da. Auch dort wird NACHGEZAEHLT: die Kopie wird zurueckgeholt und die Pruefsumme verglichen. Ein PUT, der 201 sagt, hat nichts bewiesen -- abgeschnittene Uebertragungen sehen genauso aus. Und ausgeduennt wird ebenfalls, sonst waechst die Nextcloud still voll. Die Zugangsdaten liegen NEBEN den Sicherungen (C:\backup\d4rk_media\ nextcloud.txt), nicht im Repo: sie gehoeren zur Maschine, nicht zum Code. Fehlt die Datei, sagt das Skript das einmal und sichert lokal weiter -- eine Sicherung, die wegen eines fehlenden Passworts GAR NICHT stattfindet, waere der schlechtere Tausch. Dabei einmal reingefallen: die Pruefung brach mit "sh: set: Illegal option -" ab, nachdem ein Werkzeug die Datei mit CRLF gespeichert hatte -- die Shell im Container liest dann "set -e\r". Sieht aus wie ein Tippfehler und ist keiner. Das Skript entfernt die Wagenruecklaeufe jetzt selbst. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+115
-6
@@ -16,7 +16,16 @@
|
||||
# 3. Das Archiv wird GEPRUEFT: ausgepackt, die Datenbank geoeffnet, die
|
||||
# Eintraege gezaehlt und mit dem laufenden Dienst verglichen. Eine
|
||||
# Sicherung, die niemand nachzaehlt, ist eine Vermutung.
|
||||
# 4. Alte Archive fliegen raus, sobald mehr als -Behalten da sind.
|
||||
# 4. Das Archiv geht in die NEXTCLOUD, wenn Zugangsdaten hinterlegt sind.
|
||||
# Bis dahin lag die Sicherung auf derselben Platte wie die Daten — gegen
|
||||
# einen Plattenausfall half sie damit gar nicht.
|
||||
# 5. Alte Archive fliegen raus, hier wie dort, sobald mehr als -Behalten da
|
||||
# sind.
|
||||
#
|
||||
# WARUM CURL UND NICHT RCLONE. Nextcloud spricht WebDAV, curl spricht WebDAV,
|
||||
# und curl ist ohnehin da. rclone waere ein weiteres Programm, das installiert,
|
||||
# konfiguriert und aktuell gehalten werden will — fuer PUT, PROPFIND und DELETE
|
||||
# lohnt das nicht.
|
||||
#
|
||||
# NICHT MIT DRIN sind die Vorschaubilder unter /data/thumbs, und das ist
|
||||
# Absicht: sie lassen sich aus den Bildern jederzeit neu rechnen (Dashboard →
|
||||
@@ -88,9 +97,12 @@ Gut "$name — $([math]::Round($groesse/1MB, 1)) MB"
|
||||
# -- 3. Nachzaehlen ------------------------------------------------------------
|
||||
|
||||
Schritt 'Sicherung pruefen (auspacken, Datenbank oeffnen, zaehlen)'
|
||||
$pruefung = docker run --rm `
|
||||
-v "${Ziel}:/in:ro" `
|
||||
--entrypoint sh $Abbild -c @"
|
||||
|
||||
# Die Wagenruecklaeufe muessen raus. Diese Datei liegt unter Windows und wird
|
||||
# je nach Werkzeug mit CRLF gespeichert; die Shell im Container liest dann
|
||||
# "set -e\r" und antwortet mit "Illegal option -". Der Fehler sieht aus wie ein
|
||||
# Tippfehler im Skript und ist keiner — genau einmal reingefallen.
|
||||
$skript = (@"
|
||||
set -e
|
||||
mkdir -p /tmp/p && cd /tmp/p
|
||||
tar xzf '/in/$name'
|
||||
@@ -105,7 +117,9 @@ const u = db.prepare('SELECT COUNT(*) AS n FROM users').get().n;
|
||||
console.log(JSON.stringify({ media: m, tokens: t, users: u }));
|
||||
"
|
||||
find /tmp/p/files -type f | wc -l
|
||||
"@
|
||||
"@) -replace "`r", ''
|
||||
|
||||
$pruefung = docker run --rm -v "${Ziel}:/in:ro" --entrypoint sh $Abbild -c $skript
|
||||
if ($LASTEXITCODE -ne 0) { Schlecht "Pruefung fehlgeschlagen: $pruefung"; exit 1 }
|
||||
|
||||
$zeilen = @($pruefung -split "`n" | Where-Object { $_.Trim() })
|
||||
@@ -125,7 +139,102 @@ if ($dateien -ne $zahlen.media) {
|
||||
}
|
||||
Gut 'Datenbank in der Sicherung ist lesbar und vollstaendig.'
|
||||
|
||||
# -- 4. Alte Sicherungen ausduennen --------------------------------------------
|
||||
# -- 4. In die Nextcloud -------------------------------------------------------
|
||||
#
|
||||
# Die Zugangsdaten liegen NEBEN den Sicherungen und nicht im Repo: sie gehoeren
|
||||
# zu dieser Maschine, nicht zum Code. Fehlt die Datei, wird das einmal gesagt
|
||||
# und lokal weitergesichert — eine Sicherung, die wegen eines fehlenden
|
||||
# Passworts GAR NICHT stattfindet, waere der schlechtere Tausch.
|
||||
#
|
||||
# Aufbau der Datei (eine Zuweisung je Zeile, # ist ein Kommentar):
|
||||
#
|
||||
# basis = https://cdn.d4rkst3r.de
|
||||
# benutzer = darkster
|
||||
# passwort = xxxxx-xxxxx-xxxxx-xxxxx-xxxxx <- APP-PASSWORT, nicht das echte
|
||||
# ordner = Sicherungen/d4rk_media
|
||||
#
|
||||
# Das App-Passwort steht in Nextcloud unter Einstellungen → Sicherheit →
|
||||
# "Neues App-Passwort erstellen". Es laesst sich einzeln zurueckziehen, ohne
|
||||
# dass das eigene Passwort geaendert werden muss — genau dafuer gibt es sie.
|
||||
|
||||
$zugangDatei = Join-Path $Ziel 'nextcloud.txt'
|
||||
|
||||
if (-not (Test-Path $zugangDatei)) {
|
||||
Write-Host " Keine Nextcloud hinterlegt ($zugangDatei) — die Sicherung bleibt lokal." -ForegroundColor Yellow
|
||||
} else {
|
||||
$z = @{}
|
||||
foreach ($zeile in Get-Content $zugangDatei) {
|
||||
if ($zeile -match '^\s*#') { continue }
|
||||
if ($zeile -match '^\s*([a-z]+)\s*=\s*(.+?)\s*$') { $z[$Matches[1]] = $Matches[2] }
|
||||
}
|
||||
|
||||
$fehlend = @('basis', 'benutzer', 'passwort') | Where-Object { -not $z[$_] }
|
||||
if ($fehlend) {
|
||||
Schlecht "In $zugangDatei fehlt: $($fehlend -join ', ')"
|
||||
exit 1
|
||||
}
|
||||
if (-not $z['ordner']) { $z['ordner'] = 'Sicherungen/d4rk_media' }
|
||||
|
||||
$basis = $z['basis'].TrimEnd('/')
|
||||
$wurzel = "$basis/remote.php/dav/files/$($z['benutzer'])"
|
||||
$auth = "$($z['benutzer']):$($z['passwort'])"
|
||||
|
||||
Schritt "In die Nextcloud: $($z['ordner'])"
|
||||
|
||||
# Die Ordner Ebene fuer Ebene anlegen. MKCOL legt NUR EINE an; auf einen
|
||||
# fehlenden Zwischenschritt antwortet es mit 409, und dann sucht man den
|
||||
# Fehler beim Passwort.
|
||||
$pfadBisher = ''
|
||||
foreach ($teil in $z['ordner'].Split('/')) {
|
||||
if (-not $teil) { continue }
|
||||
$pfadBisher = if ($pfadBisher) { "$pfadBisher/$teil" } else { $teil }
|
||||
# 201 = angelegt, 405 = gab es schon. Beides ist in Ordnung.
|
||||
$code = curl.exe -s -o NUL -w '%{http_code}' -u $auth -X MKCOL "$wurzel/$pfadBisher"
|
||||
if ($code -notin @('201', '405')) {
|
||||
Schlecht "MKCOL $pfadBisher -> HTTP $code"
|
||||
if ($code -eq '401') { Schlecht 'Benutzer oder App-Passwort stimmt nicht.' }
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
$ziel = "$wurzel/$($z['ordner'])/$name"
|
||||
$code = curl.exe -s -o NUL -w '%{http_code}' -u $auth -T $archiv $ziel
|
||||
if ($code -notin @('201', '204')) {
|
||||
Schlecht "Hochladen fehlgeschlagen -> HTTP $code"
|
||||
exit 1
|
||||
}
|
||||
Gut "hochgeladen ($code)"
|
||||
|
||||
# NACHZAEHLEN, auch hier: zurueckholen und die Pruefsumme vergleichen. Ein
|
||||
# PUT, der 201 sagt, hat noch nichts bewiesen — abgeschnittene Uebertragungen
|
||||
# sehen genauso aus. Bei ein paar Megabyte taeglich ist das billig.
|
||||
$zurueck = Join-Path $env:TEMP "pruefe-$name"
|
||||
curl.exe -s -u $auth -o $zurueck $ziel
|
||||
$hierHash = (Get-FileHash $archiv -Algorithm SHA256).Hash
|
||||
$dortHash = (Get-FileHash $zurueck -Algorithm SHA256).Hash
|
||||
Remove-Item $zurueck -Force -ErrorAction SilentlyContinue
|
||||
|
||||
if ($hierHash -ne $dortHash) {
|
||||
Schlecht 'Die Kopie in der Nextcloud ist NICHT identisch.'
|
||||
exit 1
|
||||
}
|
||||
Gut "zurueckgeholt und verglichen: identisch ($($hierHash.Substring(0,12))…)"
|
||||
|
||||
# Auch draussen ausduennen. Sonst waechst die Nextcloud still voll, und das
|
||||
# faellt erst auf, wenn sie voll ist.
|
||||
$liste = curl.exe -s -u $auth -X PROPFIND -H 'Depth: 1' "$wurzel/$($z['ordner'])"
|
||||
$fern = @([regex]::Matches($liste, 'd4rk_media-[\d-]+\.tar\.gz') |
|
||||
ForEach-Object { $_.Value } | Sort-Object -Unique -Descending)
|
||||
if ($fern.Count -gt $Behalten) {
|
||||
foreach ($alt in $fern | Select-Object -Skip $Behalten) {
|
||||
$c = curl.exe -s -o NUL -w '%{http_code}' -u $auth -X DELETE "$wurzel/$($z['ordner'])/$alt"
|
||||
if ($c -in @('204', '200')) { Gut "in der Nextcloud entfernt: $alt" }
|
||||
else { Write-Host " konnte $alt nicht entfernen (HTTP $c)" -ForegroundColor Yellow }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# -- 5. Alte Sicherungen ausduennen (lokal) ------------------------------------
|
||||
|
||||
$alle = Get-ChildItem $Ziel -Filter 'd4rk_media-*.tar.gz' | Sort-Object Name -Descending
|
||||
if ($alle.Count -gt $Behalten) {
|
||||
|
||||
Reference in New Issue
Block a user