Add in-app help page with user manual (Romanian)
Adds a /help page served from the dashboard navigation, containing the full
Romanian user manual for operators: dashboard, playlist management, adding
media, removing files from playlists, player management, and the advanced
edited-media view.
Implementation notes
- Manual is pre-rendered to HTML with pandoc and shipped as a static asset
(app/static/help/_manual_body.html) rather than parsed at runtime. This
keeps the container free of a Markdown dependency and makes the page render
identically regardless of what is installed.
- /help is login-protected; the sidebar table of contents is derived from the
level-2 headings at request time.
- Screenshots are served from app/static/help/screenshots/ (32 images).
- Regenerate after editing the manual:
documentatie/convert_help_page.sh
docker compose up -d --build
Also adds
- backup_players_playlists.sh / restore_database.sh for DB backup and restore.
- .gitignore rules so local database backups (real production data), generated
.docx files and duplicate screenshot copies are never committed.
This commit is contained in:
Executable
+156
@@ -0,0 +1,156 @@
|
||||
#!/bin/bash
|
||||
# ============================================================================
|
||||
# DigiServer - Database Restore
|
||||
# ----------------------------------------------------------------------------
|
||||
# Restores a database snapshot created by ./backup_players_playlists.sh.
|
||||
#
|
||||
# Usage:
|
||||
# ./restore_database.sh # restore the latest backup
|
||||
# ./restore_database.sh 20260915_143000 # restore a specific backup ID
|
||||
# ./restore_database.sh --list # list available backups
|
||||
# ./restore_database.sh <id> --skip-migrations
|
||||
#
|
||||
# Typical use: run AFTER upgrading the Docker image / docker host.
|
||||
# ============================================================================
|
||||
set -euo pipefail
|
||||
|
||||
PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
COMPOSE="docker compose -f $PROJECT_DIR/docker-compose.yml"
|
||||
BACKUP_ROOT="$PROJECT_DIR/backups"
|
||||
DB_HOST="$PROJECT_DIR/data/instance/dashboard.db"
|
||||
|
||||
SKIP_MIGRATIONS=0
|
||||
BACKUP_ID=""
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--list) ls -1 "$BACKUP_ROOT" 2>/dev/null | sort || echo "(no backups found)"; exit 0 ;;
|
||||
--skip-migrations) SKIP_MIGRATIONS=1 ;;
|
||||
-*) echo "Unknown option: $arg"; exit 1 ;;
|
||||
*) BACKUP_ID="$arg" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$BACKUP_ID" ]; then
|
||||
BACKUP_ID="$(ls -1 "$BACKUP_ROOT" 2>/dev/null | sort | tail -1 || true)"
|
||||
fi
|
||||
if [ -z "$BACKUP_ID" ]; then
|
||||
echo "ERROR: no backups found in $BACKUP_ROOT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DEST="$BACKUP_ROOT/$BACKUP_ID"
|
||||
if [ ! -d "$DEST" ]; then
|
||||
echo "ERROR: backup '$BACKUP_ID' not found in $BACKUP_ROOT"
|
||||
echo "Available:"
|
||||
ls -1 "$BACKUP_ROOT" 2>/dev/null | sed 's/^/ /'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "============================================================"
|
||||
echo " DigiServer database restore"
|
||||
echo " Backup: $BACKUP_ID"
|
||||
echo "============================================================"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 1. Verify integrity
|
||||
# ---------------------------------------------------------------------------
|
||||
if [ -f "$DEST/SHA256SUMS" ]; then
|
||||
echo
|
||||
echo "[1/6] Verifying checksums..."
|
||||
( cd "$DEST" && sha256sum -c SHA256SUMS )
|
||||
else
|
||||
echo
|
||||
echo "[1/6] No SHA256SUMS found, skipping verification"
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 2. Stop the app (so nothing writes during restore)
|
||||
# ---------------------------------------------------------------------------
|
||||
echo
|
||||
echo "[2/6] Stopping application container..."
|
||||
$COMPOSE stop digiserver-app
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 3. Back up the CURRENT database before overwriting it
|
||||
# ---------------------------------------------------------------------------
|
||||
if [ -f "$DB_HOST" ]; then
|
||||
SAFETY="$BACKUP_ROOT/pre_restore_$(date +%Y%m%d_%H%M%S)_dashboard.db"
|
||||
echo
|
||||
echo "[3/6] Saving current DB as a safety copy -> $(basename "$SAFETY")"
|
||||
cp "$DB_HOST" "$SAFETY"
|
||||
else
|
||||
echo
|
||||
echo "[3/6] No existing DB to preserve"
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 4. Restore the snapshot
|
||||
# ---------------------------------------------------------------------------
|
||||
echo
|
||||
echo "[4/6] Restoring database snapshot..."
|
||||
if [ -f "$DEST/dashboard.db.gz" ]; then
|
||||
gunzip -c "$DEST/dashboard.db.gz" > "$DB_HOST"
|
||||
elif [ -f "$DEST/dashboard.db" ]; then
|
||||
cp "$DEST/dashboard.db" "$DB_HOST"
|
||||
else
|
||||
echo "ERROR: no dashboard.db(.gz) in backup $BACKUP_ID"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Remove any stale journal left over from the old DB
|
||||
rm -f "$DB_HOST-journal" "$DB_HOST-wal" "$DB_HOST-shm"
|
||||
|
||||
# Ownership: container runs as uid 1000; host files are pi:pi (1000)
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
chown 1000:1000 "$DB_HOST"
|
||||
else
|
||||
sudo chown 1000:1000 "$DB_HOST"
|
||||
fi
|
||||
chmod 644 "$DB_HOST"
|
||||
echo " -> restored $(du -h "$DB_HOST" | cut -f1)"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 5. Start the (upgraded) app
|
||||
# ---------------------------------------------------------------------------
|
||||
echo
|
||||
echo "[5/6] Starting application..."
|
||||
$COMPOSE up -d --build
|
||||
sleep 8
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 6. Re-run migrations so any new schema is applied to the restored data
|
||||
# ---------------------------------------------------------------------------
|
||||
if [ "$SKIP_MIGRATIONS" -eq 0 ]; then
|
||||
echo
|
||||
echo "[6/6] Re-running migrations..."
|
||||
for m in add_https_config_table add_player_user_table add_email_to_https_config migrate_player_user_global; do
|
||||
if $COMPOSE exec -T digiserver-app test -f "/app/migrations/$m.py"; then
|
||||
echo " - $m"
|
||||
$COMPOSE exec -T digiserver-app python "/app/migrations/$m.py" || \
|
||||
echo " (warning: $m reported an issue - often means it was already applied)"
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo
|
||||
echo "[6/6] Migrations skipped (--skip-migrations)"
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Verify
|
||||
# ---------------------------------------------------------------------------
|
||||
echo
|
||||
echo "Verification:"
|
||||
$COMPOSE exec -T digiserver-app python -c "
|
||||
import sqlite3
|
||||
c = sqlite3.connect('/app/instance/dashboard.db')
|
||||
for t in ['player','playlist','playlist_content']:
|
||||
try:
|
||||
print(' %-18s %d' % (t, c.execute('SELECT COUNT(*) FROM \"%s\"' % t).fetchone()[0]))
|
||||
except Exception as e:
|
||||
print(' %-18s ERROR %s' % (t, e))
|
||||
"
|
||||
|
||||
echo
|
||||
echo "============================================================"
|
||||
echo " Restore complete: $BACKUP_ID"
|
||||
echo "============================================================"
|
||||
Reference in New Issue
Block a user