Files
proxy-station/.claude/skills/update-db/scripts/api.sh
YANG JIANKUAN a5d980a5e9 feat: 添加 WireGuard 设备管理并完善客户端分流
实现独立加密设备身份、网关 agent、持久生命周期任务和 Surge/FlClash 专属订阅凭证。

完善 Shadowrocket 规则转换与 Clash AI DNS,增加 Surge FFSB 适配、规则快照、运维工具及验收文档。
2026-11-02 22:44:23 +08:00

90 lines
5.0 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 api METHOD PATH --body-file FILE # 含凭据或复杂 JSON 优先用权限 600 的文件,避免放入 argv
# 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:-}"; BODY_FILE=""
if [ "$BODY" = "--body-file" ]; then
BODY_FILE="${4:-}"; BODY=""
[ -n "$BODY_FILE" ] && [ -f "$BODY_FILE" ] || die "--body-file 需要可读的 JSON 文件"
fi
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
case "$APIPATH" in *[!a-zA-Z0-9_./?=%\&~:+-]*) die "PATH 含不安全字符,请先做 URL 编码";; 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_FILE" ]; then
"${SSH[@]}" "$RCMD --data-binary @-" < "$BODY_FILE"
elif [ -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; umask 077; ts=$(date +%Y%m%d-%H%M%S); f='"$BACKUP_DIR"'/before-'"$CTX"'-$ts.db; sqlite3 '"$DB_FILE"' ".backup $f"; if [ -s "$f" ]; then chmod 600 "$f"; chown root:root "$f"; 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