Files
vps/install/23-Sub2API.sh
T
eddy 6b7a786ddf fix: 保留 Sub2API 重装时的凭证配置
- 生成持久化 .env 文件,用于保存数据库凭证、密钥、时区和初始管理员账户
- 优先复用已有 .env,并尽量迁移旧 compose 中的明文 Postgres 凭证
- 更新 docker-compose.yml,改为环境变量驱动初始化,并加入健康检查和 AUTO_SETUP
- 调整重装/更新提示,明确保留现有数据与凭证配置
2026-07-05 04:48:31 +08:00

270 lines
10 KiB
Bash
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.
#!/bin/bash
# chmod +x 23-Sub2API.sh && ./23-Sub2API.sh
# curl -sS -O https://gitea.tohub.top/Share/vps/raw/branch/main/install/23-Sub2API.sh && chmod +x 23-Sub2API.sh && ./23-Sub2API.sh
set -euo pipefail
port80=8230
# 安装目录与更新脚本路径
install_dir=/root/data/docker_data/Sub2API
update_script="$install_dir/auto-update.sh"
env_file="$install_dir/.env"
# 交互:已有安装时默认只更新,避免覆盖数据库账号、密码和连接配置
existing_install=0
if [ -f "$install_dir/docker-compose.yml" ]; then
existing_install=1
fi
echo "请选择操作:"
if [ "$existing_install" -eq 1 ]; then
echo " 1) 已安装,仅更新 Sub2API app 镜像(默认,安全,不覆盖配置/数据库)"
echo " 2) 强制重装 Sub2API(重写 docker-compose.yml,保留 .env 与数据目录)"
else
echo " 1) 安装 Sub2API(默认)"
echo " 2) 不安装,立即执行 Docker 更新"
fi
read -r -p "请输入选项 [1/2](默认 1):" action_choice
action_choice="${action_choice:-1}"
if { [ "$existing_install" -eq 1 ] && [ "$action_choice" = "1" ]; } || { [ "$existing_install" -eq 0 ] && [ "$action_choice" = "2" ]; }; then
echo "已选择:立即执行 Docker 更新。"
if [ -x "$update_script" ]; then
"$update_script"
elif [ -f "$install_dir/docker-compose.yml" ]; then
cd "$install_dir" || { echo "无法进入安装目录 $install_dir" >&2; exit 1; }
if docker compose version >/dev/null 2>&1; then
compose_cmd=(docker compose)
elif command -v docker-compose >/dev/null 2>&1; then
compose_cmd=(docker-compose)
else
echo "未检测到 docker compose 或 docker-compose,无法执行更新。" >&2
exit 1
fi
# 只更新 app 服务(Sub2API 镜像),不动 db / redis
"${compose_cmd[@]}" pull app
"${compose_cmd[@]}" up -d app
docker image prune -f
else
echo "未检测到已安装的 Sub2API(缺少 $install_dir/docker-compose.yml),请先安装。" >&2
exit 1
fi
echo "Docker 更新完成。"
exit 0
fi
if [ "$existing_install" -eq 1 ] && [ "$action_choice" = "2" ]; then
echo "检测到已有 Sub2API 安装:$install_dir/docker-compose.yml"
echo "强制重装会重写 docker-compose.yml.env 与 data/ 数据目录会保留,原管理员账号密码继续有效)。"
read -r -p "如确需重装,请输入 REINSTALL 确认:" reinstall_confirm
if [ "$reinstall_confirm" != "REINSTALL" ]; then
echo "未确认强制重装,已退出。原有配置未修改。"
exit 0
fi
fi
# 1、更新包(更新失败不阻断后续安装)
export DEBIAN_FRONTEND=noninteractive
apt update -y || true
apt upgrade -y || true #更新一下包
# 2、创建安装目录
mkdir -p "$install_dir"
cd "$install_dir" || { echo "无法进入安装目录 $install_dir" >&2; exit 1; }
# 3、生成 .env(仅首次生成,重装/更新不覆盖,避免密钥和数据库密码变化)
gen_secret() {
openssl rand -hex 32 2>/dev/null || head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n'
}
if [ ! -f "$env_file" ]; then
# 兼容旧版安装:尝试从旧 docker-compose.yml 中提取已在用的数据库密码,
# 避免生成新密码后与已有 postgres 数据目录不一致导致连不上库
old_pg_password=""
old_pg_user=""
if [ -f docker-compose.yml ]; then
# grep -v '\$' 排除新版 compose 中的 ${VAR} 占位符,只取旧版明文值
old_pg_password=$(sed -n 's/.*POSTGRES_PASSWORD=\([^ #]*\).*/\1/p' docker-compose.yml | grep -v '\$' | head -n1 || true)
old_pg_user=$(sed -n 's/.*POSTGRES_USER=\([^ #]*\).*/\1/p' docker-compose.yml | grep -v '\$' | head -n1 || true)
fi
pg_password="${old_pg_password:-$(gen_secret)}"
pg_user="${old_pg_user:-postgres}"
# 交互:设置管理员账号密码(仅数据库首次初始化时生效;
# 如果数据库已有数据,登录仍用数据库里已存在的账号密码)
default_admin_email="admin@sub2api.local"
default_admin_password=$(gen_secret | cut -c1-16)
echo "配置管理员账户(仅数据库首次初始化时创建):"
read -r -p "管理员邮箱(默认 $default_admin_email):" admin_email
admin_email="${admin_email:-$default_admin_email}"
read -r -p "管理员密码(回车使用随机密码 $default_admin_password):" admin_password
admin_password="${admin_password:-$default_admin_password}"
cat <<EOF > "$env_file"
# Sub2API 环境配置(由安装脚本生成,请勿删除,否则需重新配置)
# 数据库
POSTGRES_USER=$pg_user
POSTGRES_PASSWORD=$pg_password
POSTGRES_DB=sub2api
# 固定密钥:保证容器重建/更新后登录态和 2FA 不失效
JWT_SECRET=$(gen_secret)
TOTP_ENCRYPTION_KEY=$(gen_secret)
# 管理员账户(仅首次启动时创建,之后修改无效)
ADMIN_EMAIL=$admin_email
ADMIN_PASSWORD=$admin_password
TZ=Asia/Shanghai
EOF
chmod 600 "$env_file"
echo "已生成 $env_file(包含数据库密码、密钥和管理员初始密码,请妥善保管)。"
else
echo "检测到已有 $env_file,沿用原有管理员账号密码与数据库配置,不再询问。"
fi
# 4、填写docker-compose配置
# AUTO_SETUP=true + 环境变量传入数据库/Redis 配置,容器重建后无需再走网页安装向导
cat <<EOF > docker-compose.yml
services:
db:
image: postgres:15-alpine
container_name: sub2api-db
restart: unless-stopped
volumes:
- ./data/postgres:/var/lib/postgresql/data
environment:
- POSTGRES_USER=\${POSTGRES_USER:-postgres}
- POSTGRES_PASSWORD=\${POSTGRES_PASSWORD:?POSTGRES_PASSWORD 未配置,请检查 .env}
- POSTGRES_DB=\${POSTGRES_DB:-sub2api}
- TZ=\${TZ:-Asia/Shanghai}
healthcheck:
test: ["CMD-SHELL", "pg_isready -U \${POSTGRES_USER:-postgres} -d \${POSTGRES_DB:-sub2api}"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
container_name: sub2api-redis
restart: unless-stopped
volumes:
- ./data/redis:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
app:
image: weishaw/sub2api:latest
container_name: sub2api-app
restart: unless-stopped
volumes:
- ./data/app:/app/data # 持久化 app 配置/数据
ports:
- "$port80:8080" # 左边的端口可以更换,右边不要动!
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
environment:
# 自动初始化:跳过网页安装向导,配置全部来自环境变量(.env)
- AUTO_SETUP=true
- SERVER_HOST=0.0.0.0
- SERVER_PORT=8080 # 必须与 ports 右边的容器端口一致
- SERVER_MODE=release
# 主机用容器服务名 db / redis,不要写宿主机 IP
- DATABASE_HOST=db
- DATABASE_PORT=5432
- DATABASE_USER=\${POSTGRES_USER:-postgres}
- DATABASE_PASSWORD=\${POSTGRES_PASSWORD}
- DATABASE_DBNAME=\${POSTGRES_DB:-sub2api}
- DATABASE_SSLMODE=disable
- REDIS_HOST=redis
- REDIS_PORT=6379
# 固定密钥(来自 .env):更新/重启后登录态和 2FA 不失效
- JWT_SECRET=\${JWT_SECRET}
- TOTP_ENCRYPTION_KEY=\${TOTP_ENCRYPTION_KEY}
# 管理员账户仅首次启动时创建
- ADMIN_EMAIL=\${ADMIN_EMAIL:-admin@sub2api.local}
- ADMIN_PASSWORD=\${ADMIN_PASSWORD:-}
- TZ=\${TZ:-Asia/Shanghai}
EOF
# 5、安装
if ! command -v docker >/dev/null 2>&1; then
echo "未检测到 docker,请先安装 Docker 后再运行本脚本。" >&2
exit 1
fi
if docker compose version >/dev/null 2>&1; then
docker compose up -d
elif command -v docker-compose >/dev/null 2>&1; then
docker-compose up -d
else
echo "未检测到 docker compose 或 docker-compose,请先安装 Docker Compose。" >&2
exit 1
fi
# 6、打开防火墙的端口
if command -v ufw >/dev/null 2>&1; then
ufw allow "$port80"
ufw status
else
echo "未检测到 ufw,跳过防火墙端口放行。"
fi
# 7、配置每日自动更新
cat <<'EOF' > "$update_script"
#!/bin/bash
set -euo pipefail
install_dir=/root/data/docker_data/Sub2API
cd "$install_dir" # 进入 docker-compose 所在的文件夹
if docker compose version >/dev/null 2>&1; then
compose_cmd=(docker compose)
elif command -v docker-compose >/dev/null 2>&1; then
compose_cmd=(docker-compose)
else
echo "未检测到 docker compose 或 docker-compose,无法自动更新。" >&2
exit 1
fi
# 只更新 app 服务(Sub2API 镜像),不动 db / redis
"${compose_cmd[@]}" pull app
"${compose_cmd[@]}" up -d app
# 清理旧镜像,释放磁盘空间
docker image prune -f
EOF
chmod +x "$update_script"
# 写入每日 04:00 自动更新任务
cron_line="0 4 * * * $update_script >> /root/data/docker_data/Sub2API/auto-update.log 2>&1"
if command -v crontab >/dev/null 2>&1; then
# 使用脚本路径去重,避免重复写入 Sub2API 自动更新任务
( crontab -l 2>/dev/null | grep -v -F -- "$update_script" || true ; echo "$cron_line" ) | crontab -
echo "已配置定时自动更新任务:每天 04:00 自动更新 Sub2API app 镜像(不更新 db / redis)。"
else
echo "未检测到 crontab,跳过定时更新任务配置。可手动执行 $update_script 更新。"
fi
# 打印访问链接
echo "------------------------"
echo "访问链接:"
echo "https://sub2api.ghuang.top"
echo "管理员账户(首次安装时自动创建,凭证保存在 $env_file):"
grep -E '^(ADMIN_EMAIL|ADMIN_PASSWORD)=' "$env_file" || true
echo "------------------------"
echo "已启用 AUTO_SETUP 自动初始化:更新/重建容器后无需重新走网页安装向导。"
echo "已开启定时自动更新:每天 04:00 仅拉取最新 app 镜像并重启(db / redis 不动)。"
echo "手动更新可执行:$update_script"
echo "------------------------"