Files
AgapHost/seafile/restore.sh
alvis a27bae828a kb: batch from 2026-07-30 parallel run (#181 #183 #189 #192 #164 #128 #219)
Work produced by the /kb driver on 2026-07-30. Each change is recorded on its
Kanboard task; all remain Done-unverified or parked pending alvis's decisions.

#183 agap-mcp/src/gitea.js
  askpassScript() and giteaWikiWrite()'s wiki checkout both used
  /tmp/agap-mcp-wiki, so writing the askpass helper made the dir non-empty and
  git clone always failed. gitea_wiki_write had likely never succeeded in
  production. Askpass moved to its own dir.

#181 agap-mcp/src/server.js
  Initialise registeredToolCount at module load so /health reports the real
  count immediately instead of 0 until the first MCP request.

#189 kanboard/backup.sh, seafile/backup.sh, vaultwarden/backup.sh,
     users-backup.sh, openai/backup-{hindsight-adolf,llm-dbs}.sh
  Remove the dead *.ts Zabbix trapper pushes (never landed). users-backup.sh
  also pointed at localhost:81 instead of 192.168.1.4:81 and pushed a date
  string into a numeric item. Freshness monitoring now rides the .age items.

#192 RESTORE-RUNBOOK.md, {kanboard,seafile,vaultwarden}/restore.sh
  Restore path for the three services, verified in throwaway containers.
  Note: this work found Seafile backups have carried an empty ccnet_db.sql
  since 2026-07-07 -- filed as kb#222, not fixed here.

#164 openai/litellm-config.yaml
  Metered `judge` (anthropic/claude-haiku-4-5) entry removed per alvis's
  2026-07-30 decision. ANTHROPIC_API_KEY was never wired, so it could not spend.

#128 openai/agent_registry.py
  litellm_key_spec() now also grants the routing-mode aliases, gated by the
  same _reachable_tiers() check as raw grants, so a small-tier agent cannot
  acquire automatic routing that resolves to tier-large.

#219 openai/migrate-adolf-state.sh
  Migration script only; inert until run. Copies (never moves) the
  openai_adolf-state volume to /mnt/ssd/dbs/adolf, verifying a full sha256
  manifest before declaring success. Tested against a throwaway volume.

Deliberately NOT included, both awaiting alvis:
  agap-mcp/docker-compose.yml -- kb#174's contested BW_EMAIL revert (parked).
  openai/docker-compose.yml   -- kb#219's bind-mount switch; the target dirs
                                 under /mnt/ssd/dbs/adolf do not exist yet, so
                                 committing it would let a later `compose up`
                                 recreate Adolf against empty paths.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Y5QPagv4iun1ghpwM96Ff
2026-07-30 15:06:09 +00:00

101 lines
4.0 KiB
Bash
Executable File

#!/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/<snapshot-dir>
#
# 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=<from agap_git/seafile/.env SEAFILE_MYSQL_DB_PASSWORD, falls back to backup.sh's literal>
#
# 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 <path-to-backup-snapshot-dir>" >&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."