#!/bin/bash # ============================================================ # 高考质量分析系统 · 一条命令部署(群晖/Linux Docker) # 交互式设计: # - 令牌: 交互静默输入(或环境变量 GITEA_TOKEN) # - 目录: 交互输入 APP_DIR/DATA_DIR, 回车用默认 /volume2/docker/... # - 重复安装: 检测目录已存在(代码/数据), 询问是否覆盖; 数据默认保留 # 用法(服务器执行, 全程交互): # bash -c "$(curl -fsSL http://192.168.1.6:3000/bennyao/gaokao-deploy/raw/branch/master/init.sh)" # 也可用环境变量跳过交互: APP_DIR=... DATA_DIR=... GITEA_TOKEN=... bash -c "..." # ============================================================ set -e GITEA_HOST="${GITEA_HOST:-192.168.1.6}" GITEA_PORT="${GITEA_PORT:-3000}" GITEA_OWNER="${GITEA_OWNER:-bennyao}" GITEA_REPO="${GITEA_REPO:-gaokao-sys}" TOKEN_NAME="${TOKEN_NAME:-gaokao-deploy}" DEFAULT_APP_DIR="/volume2/docker/gaokao-sys" DEFAULT_DATA_DIR="/volume2/docker/gaokao-sys-data" PORT="${PORT:-8501}" BASE="http://$GITEA_HOST:$GITEA_PORT" API="$BASE/api/v1/repos/$GITEA_OWNER/$GITEA_REPO" echo "==============================================" echo " 高考质量分析系统 · 一键部署" echo " 代码来源: $GITEA_OWNER/$GITEA_REPO @ $GITEA_HOST" echo "==============================================" # ---- 令牌: 环境变量优先, 否则交互静默输入 ---- if [ -z "$GITEA_TOKEN" ]; then read -s -p "① 请输入 Gitea 访问令牌(静默, 不回显): " GITEA_TOKEN < /dev/tty echo "" fi : "${GITEA_TOKEN:?未输入令牌, 中止}" TOKEN_CLEANED=0 # ---- 目录: 环境变量优先, 否则交互输入(回车用默认) ---- if [ -z "$APP_DIR" ]; then read -p "② 代码目录 [回车用默认 $DEFAULT_APP_DIR]: " APP_DIR_IN < /dev/tty APP_DIR="${APP_DIR_IN:-$DEFAULT_APP_DIR}" fi if [ -z "$DATA_DIR" ]; then read -p "③ 数据目录 [回车用默认 $DEFAULT_DATA_DIR]: " DATA_DIR_IN < /dev/tty DATA_DIR="${DATA_DIR_IN:-$DEFAULT_DATA_DIR}" fi KEY_PATH="$DATA_DIR/.ssh_key" echo " 代码目录: $APP_DIR" echo " 数据目录: $DATA_DIR" # ---- 重复安装检测 ---- if [ -d "$APP_DIR/.git" ]; then echo "[提示] 检测到 $APP_DIR 已存在(上次安装的代码仓库)" read -p " 是否覆盖重新部署? [y/N]: " ANS < /dev/tty if [ "$ANS" != "y" ] && [ "$ANS" != "Y" ]; then echo "已取消, 保留现有安装。" exit 0 fi echo " 将停止旧容器并重新部署..." docker rm -f gaokao-sys >/dev/null 2>&1 || true elif [ -d "$APP_DIR" ] && [ ! -d "$APP_DIR/.git" ]; then read -p "[提示] $APP_DIR 已存在但不是代码仓库, 是否继续(会放入代码)? [y/N]: " ANS2 < /dev/tty [ "$ANS2" = "y" ] || [ "$ANS2" = "Y" ] || { echo "已取消"; exit 0; } fi if [ -d "$DATA_DIR/data" ] && [ -n "$(ls -A "$DATA_DIR/data" 2>/dev/null)" ]; then echo "[提示] 数据目录已有数据($(ls "$DATA_DIR/data" | wc -l) 个文件), 将保留不覆盖。" fi # ---- 退出时自动删除一次性令牌 ---- cleanup() { if [ -n "$GITEA_TOKEN" ] && [ "$TOKEN_CLEANED" != "1" ]; then if curl -fsS -X DELETE -H "Authorization: token $GITEA_TOKEN" \ "$BASE/api/v1/user/tokens/$GITEA_OWNER/$TOKEN_NAME" >/dev/null 2>&1; then echo "[OK] 一次性令牌已自动删除" else echo "[提示] 令牌自动删除失败, 请管理员到 Gitea → 设置 → 应用 手动删除" fi fi } trap cleanup EXIT echo "==> [1/7] 检查 Docker" command -v docker >/dev/null 2>&1 || { echo "[错误] 未安装 Docker"; exit 1; } echo "==> [2/7] 准备目录并克隆代码(令牌认证)" mkdir -p "$APP_DIR" 2>/dev/null || { echo "[错误] 无法创建 $APP_DIR(权限不足)" echo "请先执行: sudo mkdir -p $APP_DIR $DATA_DIR/data && sudo chown -R \$(whoami) $APP_DIR $DATA_DIR" exit 1 } mkdir -p "$DATA_DIR/data" 2>/dev/null || { echo "[错误] 无法创建 $DATA_DIR/data(权限不足)" echo "请先执行: sudo mkdir -p $DATA_DIR/data && sudo chown -R \$(whoami) $DATA_DIR" exit 1 } if [ ! -d "$APP_DIR/.git" ]; then git clone "http://oauth2:$GITEA_TOKEN@$GITEA_HOST:$GITEA_PORT/$GITEA_OWNER/$GITEA_REPO.git" "$APP_DIR" else cd "$APP_DIR" && git pull fi echo "==> [3/7] 生成部署密钥并自动加入 Gitea(API, 升级免密)" if [ ! -f "$KEY_PATH" ]; then ssh-keygen -t rsa -b 4096 -f "$KEY_PATH" -N "" -q PUB="$(cat "$KEY_PATH.pub")" if curl -fsS -X POST -H "Authorization: token $GITEA_TOKEN" \ -H "Content-Type: application/json" \ -d "{\"title\":\"$(hostname)-deploy\",\"key\":\"$PUB\",\"read_only\":true}" \ "$API/keys" >/dev/null 2>&1; then echo "[OK] 部署密钥已自动添加(只读)" else echo "[提示] 部署密钥添加失败: 到 Gitea → gaokao-sys → 设置 → 部署密钥 手动添加(只读):" cat "$KEY_PATH.pub" fi fi chmod 600 "$KEY_PATH" echo "==> [4/7] 构建镜像(首次较慢)" cd "$APP_DIR" docker build -t gaokao-sys:latest . echo "==> [5/7] 启动容器" docker rm -f gaokao-sys >/dev/null 2>&1 || true docker run -d --name gaokao-sys --restart=unless-stopped -p "$PORT:8501" \ -v "$APP_DIR":/app \ -v "$DATA_DIR/data":/app/data \ -v "$KEY_PATH":/app/.ssh_key:ro \ gaokao-sys:latest echo "==> [6/7] 验证" sleep 10 curl -sf "http://127.0.0.1:$PORT/_stcore/health" >/dev/null 2>&1 && echo "[OK] 服务健康" || echo "[警告] 服务未响应: docker logs gaokao-sys" echo "==> [7/7] 完成" IP=$(hostname -I 2>/dev/null | awk '{print $1}') echo " 访问: http://${IP:-<本机IP>}:$PORT" echo " 升级: 系统内「数据管理 → 🔄 更新」(手动/全自动, 走只读部署密钥)"