You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

188 lines
5.5 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#!/usr/bin/env bash
# 宝塔宿主机 MySQL + Docker 应用 — 192.168.2.14:30327
# 用法(/home/project/yubao):
# bash deploy/install-bt-host-mysql.sh
# 若尚无 .env,会自动从 deploy/.env.bt-host-mysql.example 复制
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
COMPOSE_FILE="${ROOT_DIR}/deploy/docker-compose.bt-host-mysql.yml"
ENV_FILE="${ROOT_DIR}/.env"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
log() { echo -e "${GREEN}[deploy-bt]${NC} $*"; }
warn() { echo -e "${YELLOW}[deploy-bt]${NC} $*"; }
err() { echo -e "${RED}[deploy-bt]${NC} $*" >&2; }
read_env() {
local key="$1"
local line
line="$(grep -E "^[[:space:]]*${key}=" "$ENV_FILE" 2>/dev/null | tail -1 || true)"
if [[ -z "$line" ]]; then
echo ""
return
fi
line="${line#*=}"
line="${line#"${line%%[![:space:]]*}"}"
line="${line%"${line##*[![:space:]]}"}"
if [[ "$line" == \"*\" && "$line" == *\" ]]; then
line="${line:1:${#line}-2}"
elif [[ "$line" == \'*\' && "$line" == *\' ]]; then
line="${line:1:${#line}-2}"
fi
echo "$line"
}
validate_env() {
local example="${ROOT_DIR}/deploy/.env.bt-host-mysql.example"
if [[ ! -f "$ENV_FILE" ]]; then
if [[ -f "$example" ]]; then
log "未找到 .env,从 deploy/.env.bt-host-mysql.example 自动复制"
cp "$example" "$ENV_FILE"
else
err "未找到 .env 且缺少 ${example}"
exit 1
fi
fi
DATABASE_URL="$(read_env DATABASE_URL)"
SESSION_SECRET="$(read_env SESSION_SECRET)"
APP_ADMIN_PASS="$(read_env APP_ADMIN_PASS)"
APP_OPS_PASS="$(read_env APP_OPS_PASS)"
APP_PORT="$(read_env APP_PORT)"
PUBLIC_HOST="$(read_env PUBLIC_HOST)"
MYSQL_USER="$(read_env MYSQL_USER)"
MYSQL_PASSWORD="$(read_env MYSQL_PASSWORD)"
local missing=0
for key in DATABASE_URL SESSION_SECRET APP_ADMIN_PASS APP_OPS_PASS MYSQL_USER MYSQL_PASSWORD; do
local val="${!key:-}"
if [[ -z "$val" ]] || [[ "$val" == CHANGE_ME* ]]; then
err "请在 .env 中填写: $key"
missing=1
fi
done
if [[ "$APP_ADMIN_PASS" == "$APP_OPS_PASS" ]]; then
err "APP_ADMIN_PASS 与 APP_OPS_PASS 不能相同"
missing=1
fi
for weak in admin123 ops123 password 123456; do
if [[ "$APP_ADMIN_PASS" == "$weak" || "$APP_OPS_PASS" == "$weak" ]]; then
err "生产环境禁止使用弱口令: $weak"
missing=1
fi
done
if [[ "$SESSION_SECRET" == *"change-me"* ]] || [[ ${#SESSION_SECRET} -lt 32 ]]; then
err "SESSION_SECRET 须至少 32 字符且非开发默认值"
missing=1
fi
if [[ $missing -ne 0 ]]; then
exit 1
fi
}
preflight_mysql() {
local db_user db_pass db_name
db_user="$(read_env MYSQL_USER)"
db_pass="$(read_env MYSQL_PASSWORD)"
db_name="$(read_env MYSQL_DATABASE)"
if [[ -z "$db_pass" ]]; then
return 0
fi
log "测试宝塔 MySQL 连接..."
if docker run --rm --add-host=host.docker.internal:host-gateway mysql:8 \
mysql -h host.docker.internal -u "${db_user}" -p"${db_pass}" -e "SELECT 1" "${db_name}" >/dev/null 2>&1; then
log "MySQL 连接正常"
return 0
fi
err "MySQL 认证失败:请核对 .env 中 MYSQL_PASSWORD / DATABASE_URL 是否与宝塔一致"
err "常见原因:"
err " 1) 宝塔用户未授权 Docker 网段(172.%)"
err " 2) 重新上传代码覆盖了 .env"
err "修复示例:"
err " MYSQL_PASSWORD=宝塔里 ${db_user} 用户的真实密码"
err " DATABASE_URL=mysql://${db_user}:真实密码@host.docker.internal:3306/${db_name}"
exit 1
}
deploy_docker() {
if ! command -v docker >/dev/null 2>&1; then
err "未安装 Docker"
exit 1
fi
if ! docker compose version >/dev/null 2>&1; then
err "需要 Docker Compose V2"
exit 1
fi
mkdir -p "${ROOT_DIR}/data"
cd "$ROOT_DIR"
local compose=(docker compose --env-file "$ENV_FILE" -f "$COMPOSE_FILE")
log "构建并启动(无容器 MySQL,连接宝塔 MySQL)..."
export DOCKER_BUILDKIT=1
export BUILDKIT_MAX_PARALLELISM=1
"${compose[@]}" build web
"${compose[@]}" up -d
log "同步数据库结构(prisma db push)..."
"${compose[@]}" run --rm --no-deps web pnpm exec prisma db push --skip-generate
log "同步默认账号(admin / ops)..."
"${compose[@]}" run --rm --no-deps web pnpm db:seed
log "容器状态:"
"${compose[@]}" ps
}
smoke_check() {
local port="${APP_PORT:-30327}"
local url="http://127.0.0.1:${port}/api/health"
log "健康检查: ${url}"
local body
body="$(curl -sf "$url" 2>/dev/null || true)"
if [[ "$body" == \{* ]]; then
log "Web 服务正常"
else
warn "Web 暂未响应,请查看:docker compose -f deploy/docker-compose.bt-host-mysql.yml logs -f web"
fi
}
print_summary() {
local port="${APP_PORT:-30327}"
local host="${PUBLIC_HOST:-<服务器内网IP>}"
echo ""
log "========== 部署完成(宝塔 MySQL 模式)=========="
echo " 内网访问: http://${host}:${port}"
echo " 登录页: http://${host}:${port}/login"
echo " 邮件列表: http://${host}:${port}/mails"
echo " 设置页: http://${host}:${port}/settings"
echo ""
echo " 常用命令:"
echo " docker compose --env-file .env -f deploy/docker-compose.bt-host-mysql.yml logs -f worker"
echo " docker compose --env-file .env -f deploy/docker-compose.bt-host-mysql.yml restart"
echo ""
warn "首次登录后请立即修改 admin/ops 密码(.env 中 APP_ADMIN_PASS / APP_OPS_PASS)"
warn "CC / IMAP 推荐在 Admin「设置」页配置,不必改代码"
}
main() {
validate_env
preflight_mysql
deploy_docker
smoke_check
print_summary
}
main "$@"