#!/bin/bash # Seafile restore — companion to backup.sh (kb#192). # # Restores a snapshot produced by backup.sh (ccnet_db.sql, seafile_db.sql, # seahub_db.sql, and a data/ directory tree) into a running MariaDB # container plus a data directory. Defaults to the live containers/paths, # but every target is overridable via env vars so the same script can be # pointed at throwaway containers + a scratch data dir for a dry-run # restore test. # # Usage: # ./restore.sh /mnt/backups/seafile/ # # Env overrides (defaults = live service): # MYSQL_CONTAINER=seafile-mysql # SEAFILE_CONTAINER=seafile # stopped/started around the data-dir copy; set to "" to skip # DATA_DIR=/mnt/misc/seafile # host path the seafile-net containers bind-mount # MYSQL_USER=seafile # MYSQL_PASSWORD= # # WARNING: this drops and reloads the target's live ccnet/seafile/seahub # databases and overwrites its data directory. Never run against the live # "seafile-mysql"/"seafile" containers or /mnt/misc/seafile unless you # intend a real disaster recovery — for testing, point the *_CONTAINER # and DATA_DIR vars at throwaway equivalents instead. # # NOTE: restoring only the three databases (no data/ directory, e.g. # MYSQL_CONTAINER set but SEAFILE_CONTAINER="" and no data/ present in the # snapshot) is a valid partial restore for verifying DB integrity — # the script skips the data-dir step automatically if snapshot has no data/. set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" [ -f "$SCRIPT_DIR/.env" ] && source "$SCRIPT_DIR/.env" MYSQL_CONTAINER="${MYSQL_CONTAINER:-seafile-mysql}" # NOTE: use ${VAR-default} (no colon) for SEAFILE_CONTAINER so that an # explicit empty string (SEAFILE_CONTAINER="") disables the data-dir step, # as documented above — ${VAR:-default} would treat "" as unset and fall # back to the live container name, which is not what an operator asking # to skip that step intends. SEAFILE_CONTAINER="${SEAFILE_CONTAINER-seafile}" DATA_DIR="${DATA_DIR:-/mnt/misc/seafile}" MYSQL_USER="${MYSQL_USER:-seafile}" MYSQL_PASSWORD="${MYSQL_PASSWORD:-${SEAFILE_MYSQL_DB_PASSWORD:-FWsYYeZa15ro6x}}" if [ $# -lt 1 ]; then echo "Usage: $0 " >&2 echo " e.g. $0 /mnt/backups/seafile/20260728-0200" >&2 exit 1 fi SRC="$(realpath "$1")" for DB in ccnet_db seafile_db seahub_db; do if [ ! -s "$SRC/${DB}.sql" ]; then echo "Error: $SRC/${DB}.sql is missing or empty — refusing to restore from a broken snapshot" >&2 exit 1 fi done if ! docker inspect "$MYSQL_CONTAINER" > /dev/null 2>&1; then echo "Error: mysql container '$MYSQL_CONTAINER' does not exist" >&2 exit 1 fi echo "Restoring databases into '$MYSQL_CONTAINER' from $SRC" for DB in ccnet_db seafile_db seahub_db; do echo "Restoring $DB..." docker exec -i "$MYSQL_CONTAINER" mysql -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" \ -e "DROP DATABASE IF EXISTS \`$DB\`; CREATE DATABASE \`$DB\` CHARACTER SET utf8mb4;" docker exec -i "$MYSQL_CONTAINER" mysql -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" "$DB" < "$SRC/${DB}.sql" echo "Restored: $DB" done echo "Verifying restored databases..." for DB in ccnet_db seafile_db seahub_db; do TABLES=$(docker exec "$MYSQL_CONTAINER" mysql -u "$MYSQL_USER" -p"$MYSQL_PASSWORD" -N -e "SHOW TABLES FROM \`$DB\`;" | wc -l) echo "$DB: $TABLES tables" done if [ -d "$SRC/data" ] && [ -n "$SEAFILE_CONTAINER" ]; then echo "Restoring data directory into $DATA_DIR..." if docker inspect "$SEAFILE_CONTAINER" > /dev/null 2>&1; then docker stop "$SEAFILE_CONTAINER" > /dev/null fi rsync -a --delete \ --exclude='seafile-mysql/' \ --exclude='seafile-caddy/' \ "$SRC/data/" "$DATA_DIR/" if docker inspect "$SEAFILE_CONTAINER" > /dev/null 2>&1; then docker start "$SEAFILE_CONTAINER" > /dev/null fi else echo "Note: no data/ dir in snapshot or SEAFILE_CONTAINER unset — skipping data-dir restore (DB-only restore)" fi echo "Restore complete."