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
101 lines
4.0 KiB
Bash
Executable File
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."
|