Files
2026-04-24 17:46:03 +08:00

93 lines
3.1 KiB
Bash
Executable File

#!/usr/bin/env bash
# Loom 生产部署脚本(在目标服务器上执行)
# 用法:bash scripts/deploy.sh
#
# 前置条件:
# - 已装 docker + docker compose v2
# - 域名 ai.aidfxn.com 已解析到本机公网 IP
# - 服务器 80 / 443 已对公网开放
# - 已 `cp .env.production.example .env` 并填完所有 ***=
set -euo pipefail
DOMAIN="ai.aidfxn.com"
EMAIL="${DEPLOY_EMAIL:-runzi.lao@gmail.com}"
echo "[1/6] 检查 .env 是否就绪..."
if [ ! -f .env ]; then
echo " 缺少 .env,请先 cp .env.production.example .env 并填值" >&2
exit 1
fi
for k in APP_SECRET_KEY POSTGRES_PASSWORD FERNET_KEY; do
if ! grep -qE "^${k}=.+" .env; then
echo " .env 中 ${k} 为空,必须填写" >&2
exit 1
fi
done
# DATABASE_URL 占位替换(让脚本更省事)
if grep -q "__POSTGRES_PASSWORD__" .env; then
PWD_VAL=$(grep -E "^POSTGRES_PASSWORD=" .env | cut -d= -f2-)
# 用 awk 做安全替换(避免 sed 对特殊字符的问题)
awk -v pw="$PWD_VAL" '{gsub(/__POSTGRES_PASSWORD__/, pw); print}' .env > .env.tmp && mv .env.tmp .env
echo " 已替换 DATABASE_URL 中的 __POSTGRES_PASSWORD__"
fi
echo "[2/6] 准备 certbot 目录..."
mkdir -p certbot/conf certbot/www
echo "[3/6] 先拉 / 构建镜像..."
docker compose -f docker-compose.yml -f docker-compose.prod.yml --profile full pull postgres redis certbot || true
docker compose -f docker-compose.yml -f docker-compose.prod.yml --profile full build backend frontend
# 检查证书是否已存在
if [ ! -d "certbot/conf/live/${DOMAIN}" ]; then
echo "[4/6] 首次签发 Let's Encrypt 证书..."
# 启一个临时 nginx(只暴露 80,用 HTTP-01 验证)
docker run --rm -d --name loom-certbot-bootstrap \
-p 80:80 \
-v "$(pwd)/certbot/www:/usr/share/nginx/html:ro" \
nginx:1.27-alpine
sleep 2
docker run --rm \
-v "$(pwd)/certbot/conf:/etc/letsencrypt" \
-v "$(pwd)/certbot/www:/var/www/certbot" \
certbot/certbot certonly --webroot \
-w /var/www/certbot \
--email "${EMAIL}" --agree-tos --no-eff-email \
-d "${DOMAIN}"
docker stop loom-certbot-bootstrap || true
else
echo "[4/6] 证书已存在,跳过申请"
fi
echo "[5/6] 启动全栈..."
docker compose -f docker-compose.yml -f docker-compose.prod.yml --profile full up -d
echo "[6/6] 等待 backend 就绪..."
for i in $(seq 1 30); do
if curl -sf "https://${DOMAIN}/api/v1/healthz" -k >/dev/null 2>&1; then
echo " ✅ backend ready"
break
fi
sleep 2
if [ "$i" = "30" ]; then
echo " ⚠️ 60s 内 /api/v1/healthz 未响应,请查看 docker logs"
docker compose ps
exit 1
fi
done
echo
echo "============================================================"
echo "部署完成!访问:https://${DOMAIN}/"
echo " 超管:admin / admin123(首次登录请立刻修改密码)"
echo
echo "常用运维命令:"
echo " docker compose -f docker-compose.yml -f docker-compose.prod.yml ps"
echo " docker compose -f docker-compose.yml -f docker-compose.prod.yml logs -f backend"
echo " docker compose -f docker-compose.yml -f docker-compose.prod.yml exec backend alembic upgrade head"
echo "============================================================"