Merge pull request 'Feature: Restore-Skript für DB, Uploads und Migrationen' (#76) from fds2610-bot/Steuerkram:main into main
Reviewed-on: https://codeberg.org/fds2610/Steuerkram/pulls/76
This commit is contained in:
commit
942dd8fadd
2 changed files with 112 additions and 25 deletions
102
scripts/restore.sh
Executable file
102
scripts/restore.sh
Executable file
|
|
@ -0,0 +1,102 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Steuerkram – Restore-Script
|
||||||
|
# Spielt ein Backup-Paar (DB + Uploads) ein und führt anschließend alle
|
||||||
|
# ausstehenden Migrationen aus.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./scripts/restore.sh # neuestes vollständiges Backup
|
||||||
|
# ./scripts/restore.sh 20260517_134945 # bestimmten Timestamp
|
||||||
|
|
||||||
|
set -eo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
BACKUP_DIR="$PROJECT_DIR/backups"
|
||||||
|
COMPOSE="docker compose -f $PROJECT_DIR/docker-compose.yml --project-directory $PROJECT_DIR"
|
||||||
|
|
||||||
|
# --- Timestamp ermitteln ---
|
||||||
|
if [ -n "$1" ]; then
|
||||||
|
TS="$1"
|
||||||
|
else
|
||||||
|
# Neuesten Timestamp finden, für den BEIDE Dateien existieren
|
||||||
|
TS=""
|
||||||
|
for DB_FILE in $(ls -t "$BACKUP_DIR"/steuerkram_db_????????_??????.sql 2>/dev/null); do
|
||||||
|
CANDIDATE=$(basename "$DB_FILE" .sql | sed 's/steuerkram_db_//')
|
||||||
|
UPL_FILE="$BACKUP_DIR/steuerkram_uploads_${CANDIDATE}.tar.gz"
|
||||||
|
if [ -f "$UPL_FILE" ]; then
|
||||||
|
TS="$CANDIDATE"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
DB_FILE="$BACKUP_DIR/steuerkram_db_${TS}.sql"
|
||||||
|
UPL_FILE="$BACKUP_DIR/steuerkram_uploads_${TS}.tar.gz"
|
||||||
|
|
||||||
|
# --- Dateien prüfen ---
|
||||||
|
if [ -z "$TS" ]; then
|
||||||
|
echo "FEHLER: Kein vollständiges Backup-Paar (DB + Uploads) gefunden." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ ! -f "$DB_FILE" ]; then
|
||||||
|
echo "FEHLER: DB-Backup nicht gefunden: $DB_FILE" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ ! -f "$UPL_FILE" ]; then
|
||||||
|
echo "FEHLER: Uploads-Backup nicht gefunden: $UPL_FILE" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "========================================"
|
||||||
|
echo " Steuerkram Restore – $TS"
|
||||||
|
echo "========================================"
|
||||||
|
echo " DB: $DB_FILE"
|
||||||
|
echo " Uploads: $UPL_FILE"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# --- Sicherheitsabfrage ---
|
||||||
|
read -r -p "Vorhandene Datenbank und Uploads werden ÜBERSCHRIEBEN. Fortfahren? [j/N] " CONFIRM
|
||||||
|
if [[ ! "$CONFIRM" =~ ^[jJyY]$ ]]; then
|
||||||
|
echo "Abgebrochen."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Container-Status prüfen ---
|
||||||
|
if ! $COMPOSE ps --status running db | grep -q "db"; then
|
||||||
|
echo "FEHLER: DB-Container läuft nicht. Bitte erst 'docker compose up -d' ausführen." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! $COMPOSE ps --status running web | grep -q "web"; then
|
||||||
|
echo "FEHLER: Web-Container läuft nicht." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- 1. Datenbank einspielen ---
|
||||||
|
echo ""
|
||||||
|
echo "[1/3] Datenbank importieren..."
|
||||||
|
$COMPOSE exec -T db mariadb -u root -prootpass steuerkram < "$DB_FILE"
|
||||||
|
echo " OK"
|
||||||
|
|
||||||
|
# --- 2. Uploads einspielen ---
|
||||||
|
echo "[2/3] Uploads entpacken..."
|
||||||
|
$COMPOSE exec -T web sh -c "
|
||||||
|
cat > /tmp/uploads_restore.tar.gz
|
||||||
|
tar -xzf /tmp/uploads_restore.tar.gz -C /var/www/html/public/static/
|
||||||
|
rm /tmp/uploads_restore.tar.gz
|
||||||
|
" < "$UPL_FILE"
|
||||||
|
echo " OK"
|
||||||
|
|
||||||
|
# --- 3. Migrationen ---
|
||||||
|
echo "[3/3] Migrationen prüfen und einspielen..."
|
||||||
|
MIGRATE_OUT=$($COMPOSE exec -T web php scripts/migrate.php 2>&1)
|
||||||
|
echo "$MIGRATE_OUT" | sed 's/^/ /'
|
||||||
|
if echo "$MIGRATE_OUT" | grep -q "FEHLER"; then
|
||||||
|
echo ""
|
||||||
|
echo "WARNUNG: Mindestens eine Migration ist fehlgeschlagen!" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "========================================"
|
||||||
|
echo " Restore abgeschlossen."
|
||||||
|
echo "========================================"
|
||||||
|
|
@ -139,10 +139,7 @@
|
||||||
<th>Belegdatum</th>
|
<th>Belegdatum</th>
|
||||||
<th>Lieferant</th>
|
<th>Lieferant</th>
|
||||||
<th>Rechnungsnr.</th>
|
<th>Rechnungsnr.</th>
|
||||||
<th>Dateiname</th>
|
|
||||||
<th>Bewirtungsgrund / Teilnehmer</th>
|
|
||||||
<th class="td-right">Brutto</th>
|
<th class="td-right">Brutto</th>
|
||||||
<th>Status</th>
|
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
|
|
@ -154,39 +151,27 @@
|
||||||
title="Klicken für Vorschau">
|
title="Klicken für Vorschau">
|
||||||
<td onclick="event.stopPropagation()">
|
<td onclick="event.stopPropagation()">
|
||||||
<a href="/belege/<?= $h($b['id']) ?>" class="font-mono"><?= $h($b['beleg_nr']) ?></a>
|
<a href="/belege/<?= $h($b['id']) ?>" class="font-mono"><?= $h($b['beleg_nr']) ?></a>
|
||||||
|
<div style="margin-top:.2em">
|
||||||
|
<?php if ($b['locked']): ?>
|
||||||
|
<span class="badge badge-purple">Gesperrt</span>
|
||||||
|
<?php elseif ($b['booked']): ?>
|
||||||
|
<span class="badge badge-green">Gebucht</span>
|
||||||
|
<?php else: ?>
|
||||||
|
<span class="badge badge-yellow">Offen</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="td-muted"><?= View::formatDate($b['beleg_datum']) ?></td>
|
<td class="td-muted"><?= View::formatDate($b['beleg_datum']) ?></td>
|
||||||
<td class="td-muted"><?= $h($b['supplier_name'] ?? '–') ?></td>
|
<td class="td-muted"><?= $h($b['supplier_name'] ?? '–') ?></td>
|
||||||
<td class="td-muted"><?= $h($b['rechnungs_nr'] ?? '–') ?></td>
|
<td class="td-muted"><?= $h($b['rechnungs_nr'] ?? '–') ?></td>
|
||||||
<td class="text-sm text-muted"><?= $h(mb_strimwidth($b['filename'], 0, 35, '…')) ?></td>
|
|
||||||
<td style="max-width:220px">
|
|
||||||
<?php if (!empty($b['bewirtungsgrund'])): ?>
|
|
||||||
<div style="font-size:.8em;white-space:pre-wrap;word-break:break-word"><?= $h($b['bewirtungsgrund']) ?></div>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if (!empty($b['teilnehmerliste'])): ?>
|
|
||||||
<div style="font-size:.75em;color:var(--color-muted);white-space:pre-wrap;word-break:break-word;margin-top:.15em"><?= $h($b['teilnehmerliste']) ?></div>
|
|
||||||
<?php endif; ?>
|
|
||||||
<?php if (empty($b['bewirtungsgrund']) && empty($b['teilnehmerliste'])): ?>
|
|
||||||
<span class="text-muted">–</span>
|
|
||||||
<?php endif; ?>
|
|
||||||
</td>
|
|
||||||
<td class="td-right td-mono"><?= $b['betrag_brutto'] !== null ? View::formatMoney($b['betrag_brutto']) : '–' ?></td>
|
<td class="td-right td-mono"><?= $b['betrag_brutto'] !== null ? View::formatMoney($b['betrag_brutto']) : '–' ?></td>
|
||||||
<td>
|
|
||||||
<?php if ($b['locked']): ?>
|
|
||||||
<span class="badge badge-purple">Gesperrt</span>
|
|
||||||
<?php elseif ($b['booked']): ?>
|
|
||||||
<span class="badge badge-green">Gebucht</span>
|
|
||||||
<?php else: ?>
|
|
||||||
<span class="badge badge-yellow">Offen</span>
|
|
||||||
<?php endif; ?>
|
|
||||||
</td>
|
|
||||||
<td onclick="event.stopPropagation()">
|
<td onclick="event.stopPropagation()">
|
||||||
<a href="/belege/<?= $h($b['id']) ?>" class="btn btn-secondary btn-sm">Öffnen</a>
|
<a href="/belege/<?= $h($b['id']) ?>" class="btn btn-secondary btn-sm">Öffnen</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
<?php if (empty($rows)): ?>
|
<?php if (empty($rows)): ?>
|
||||||
<tr><td colspan="10" class="text-center text-muted" style="padding:2rem">Keine Belege gefunden.</td></tr>
|
<tr><td colspan="6" class="text-center text-muted" style="padding:2rem">Keine Belege gefunden.</td></tr>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue