Files
proxy-station/.claude/skills/update-db/scripts/api.sh
2026-10-03 19:33:03 +08:00

82 lines
4.4 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# update-db 的唯一执行通道:对生产库的一切操作经它走 ssh(在远端本机回环上调 API / 做快照)。
#
# 用法:
# api.sh api METHOD PATH [JSON] # 调管理接口;PATH 以 /api/ 或 /sub/ 开头;JSON 作第三参
# api.sh backup <context> # 变更前快照 → data/backups/before-<context>-<时间戳>.db
# api.sh backups # 列出最近 20 个备份
# api.sh counts # 各表行数,变更前后对比用
# api.sh health # systemd 状态 + /api/health
# api.sh restore <file> --confirm # 整体回滚:停服换库再拉起(仅在用户明确要求时执行)
#
# 可用环境变量覆盖:DEPLOY_HOST(默认 la)、DEPLOY_SERVICE(默认 proxy-station)、
# PS_DATA_DIR(默认 /opt/proxy-station/data)、PS_ENV_FILE(默认 /etc/proxy-station/env)
set -euo pipefail
# 会话注入的 HTTP 代理只供模型 API 使用,ssh 一律直连(同 deploy.sh)
unset HTTPS_PROXY HTTP_PROXY https_proxy http_proxy ALL_PROXY all_proxy 2>/dev/null || true
HOST="${DEPLOY_HOST:-la}"
SERVICE="${DEPLOY_SERVICE:-proxy-station}"
DATA_DIR="${PS_DATA_DIR:-/opt/proxy-station/data}"
DB_FILE="$DATA_DIR/proxy-station.db"
BACKUP_DIR="$DATA_DIR/backups"
ENV_FILE="${PS_ENV_FILE:-/etc/proxy-station/env}"
SSH=(ssh -o BatchMode=yes -o ConnectTimeout=15 "$HOST")
die() { echo "错误:$*" >&2; exit 1; }
[ $# -ge 1 ] || die "缺少子命令(api/backup/backups/counts/health/restore)"
cmd="$1"; shift
case "$cmd" in
api)
[ $# -ge 2 ] || die "用法:api.sh api METHOD PATH [JSON]"
METHOD="$1"; APIPATH="$2"; BODY="${3:-}"
case "$METHOD" in GET|POST|PUT|PATCH|DELETE) ;; *) die "METHOD 只能是 GET/POST/PUT/PATCH/DELETE";; esac
case "$APIPATH" in /api/*|/sub/*) ;; *) die "PATH 必须以 /api/ 或 /sub/ 开头";; esac
# 单引号段内的 $ADMIN_TOKEN 与 ${PORT:-3000} 留给远端 shell 展开:token 不经本地 shell、不落本地磁盘
RCMD='. '"$ENV_FILE"' 2>/dev/null; curl -sS -m 60 -X '"$METHOD"' -H "Authorization: Bearer $ADMIN_TOKEN" -H "Content-Type: application/json" -w "\n__HTTP_STATUS:%{http_code}__" http://127.0.0.1:${PORT:-3000}'"$APIPATH"
if [ -n "$BODY" ]; then
printf '%s' "$BODY" | "${SSH[@]}" "$RCMD --data-binary @-"
else
"${SSH[@]}" "$RCMD"
fi
;;
backup)
CTX="${1:-}"; [ -n "$CTX" ] || die "用法:api.sh backup <context>(小写字母/数字/连字符,如 add-jp-hy2)"
case "$CTX" in *[!a-z0-9-]*) die "context 只允许小写字母、数字与连字符";; esac
# .backup 是 SQLite 在线备份,WAL 下安全,产出独立完整快照(无需 wal/shm)
"${SSH[@]}" 'set -e; ts=$(date +%Y%m%d-%H%M%S); f='"$BACKUP_DIR"'/before-'"$CTX"'-$ts.db; sqlite3 '"$DB_FILE"' ".backup $f"; if [ -s "$f" ]; then ls -la "$f"; else echo "备份为空" >&2; exit 1; fi'
;;
backups)
"${SSH[@]}" "ls -la '$BACKUP_DIR' | tail -20"
;;
counts)
# 表清单来自 BOOTSTRAP_SQL;新增表时应同步更新这里
SQL="SELECT 'nodes='||COUNT(*) FROM nodes"
for t in policy_groups group_members rules rulesets ruleset_cache sub_tokens settings; do
SQL="$SQL UNION ALL SELECT '$t='||COUNT(*) FROM $t"
done
"${SSH[@]}" "sqlite3 '$DB_FILE' \"$SQL\""
;;
health)
"${SSH[@]}" "systemctl is-active $SERVICE; curl -sS -m 5 http://127.0.0.1:3000/api/health; echo"
;;
restore)
FILE="${1:-}"; [ -n "$FILE" ] || die "用法:api.sh restore <备份文件> --confirm"
[ "${2:-}" = "--confirm" ] || die "restore 会停服换库(订阅中断约 10–20 秒),必须由用户明确要求后追加 --confirm 执行"
case "$FILE" in "$BACKUP_DIR"/*.db) ;; *) die "备份文件必须是 $BACKUP_DIR 下的 .db 快照";; esac
"${SSH[@]}" 'set -e; f='"$FILE"'; [ -f "$f" ] || { echo "备份不存在:$f" >&2; exit 1; }
systemctl stop '"$SERVICE"'
cp "$f" '"$DB_FILE"'.new && mv '"$DB_FILE"'.new '"$DB_FILE"'
rm -f '"$DB_FILE"'-wal '"$DB_FILE"'-shm
chown '"$SERVICE"':'"$SERVICE"' '"$DB_FILE"'
systemctl start '"$SERVICE"'
for i in $(seq 1 20); do curl -sf -m 2 http://127.0.0.1:3000/api/health >/dev/null && break; sleep 1; done
systemctl is-active '"$SERVICE"' && curl -s http://127.0.0.1:3000/api/health && echo && echo "已回滚自 $(basename "$f")"'
;;
*) die "未知子命令:$cmd(可用:api/backup/backups/counts/health/restore)" ;;
esac