#!/bin/bash set -e cd "$(dirname "$0")" # 颜色定义 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' BOLD='\033[1m' NC='\033[0m' # 解析参数 WITH_FRONTEND=true DEV_MODE=false QUIET_MODE=false for arg in "$@"; do case $arg in --no-frontend) WITH_FRONTEND=false ;; --dev) DEV_MODE=true ;; --quiet) QUIET_MODE=true ;; esac done # 选择 compose 文件 if [ "$DEV_MODE" = true ]; then COMPOSE_FILE="docker-compose.dev.yml" echo -e "${YELLOW}[MODE]${NC} 开发模式 - 本地构建镜像" else COMPOSE_FILE="docker-compose.yml" echo -e "${GREEN}[MODE]${NC} 生产模式 - 使用 Docker Hub 镜像" fi # 检查 Docker 环境 if ! command -v docker >/dev/null 2>&1; then echo -e "${RED}[ERROR]${NC} 未检测到 docker 命令,请先安装 Docker" exit 1 fi if ! docker info >/dev/null 2>&1; then echo -e "${RED}[ERROR]${NC} Docker 守护进程未运行,请先启动 Docker" exit 1 fi 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 -e "${RED}[ERROR]${NC} 未检测到 docker compose,请先安装" exit 1 fi # 检查配置文件 if [ ! -f .env ]; then echo -e "${RED}[ERROR]${NC} 未找到 .env 配置文件" echo " 请先复制 .env.example 为 .env 并配置" exit 1 fi # 确保数据目录存在 DATA_DIR="/opt/xingrin" if [ ! -d "$DATA_DIR/results" ] || [ ! -d "$DATA_DIR/logs" ]; then echo -e "${CYAN}[INIT]${NC} 创建数据目录: $DATA_DIR" sudo mkdir -p "$DATA_DIR/results" "$DATA_DIR/logs" sudo chmod -R 755 "$DATA_DIR" fi # 读取数据库配置 DB_HOST=$(grep -E "^DB_HOST=" .env | cut -d'=' -f2 | tr -d ' "'"'" || echo "postgres") if [[ "$DB_HOST" == "postgres" || "$DB_HOST" == "localhost" || "$DB_HOST" == "127.0.0.1" ]]; then echo -e "${CYAN}[DB]${NC} 使用本地 PostgreSQL 容器" PROFILE_ARG="--profile local-db" else echo -e "${CYAN}[DB]${NC} 使用远程 PostgreSQL: $DB_HOST" PROFILE_ARG="" fi # 启动服务(启用 BuildKit 缓存 + 并行构建加速) export DOCKER_BUILDKIT=1 export COMPOSE_DOCKER_CLI_BUILD=1 export BUILDKIT_INLINE_CACHE=1 # 使用指定的 compose 文件 COMPOSE_ARGS="-f ${COMPOSE_FILE} ${PROFILE_ARG}" SERVICES="$(${COMPOSE_CMD} ${COMPOSE_ARGS} config --services)" service_exists() { echo "$SERVICES" | grep -qx "$1" } BACKEND_SERVICES=() for s in redis server agent; do if service_exists "$s"; then BACKEND_SERVICES+=("$s") fi done # 如果使用本地数据库,先启动 postgres 并等待健康 start_postgres_first() { if [ -n "$PROFILE_ARG" ]; then echo -e "${CYAN}[DB]${NC} 启动 PostgreSQL 容器..." ${COMPOSE_CMD} ${COMPOSE_ARGS} up -d postgres echo -e "${CYAN}[DB]${NC} 等待 PostgreSQL 就绪..." local max_wait=30 local count=0 while [ $count -lt $max_wait ]; do if ${COMPOSE_CMD} ${COMPOSE_ARGS} exec -T postgres pg_isready -U postgres >/dev/null 2>&1; then echo -e "${GREEN}[DB]${NC} PostgreSQL 已就绪" return 0 fi sleep 1 count=$((count + 1)) done echo -e "${YELLOW}[WARN]${NC} PostgreSQL 等待超时,继续启动其他服务..." fi } echo "" if [ "$DEV_MODE" = true ]; then # 开发模式:本地构建 if [ "$WITH_FRONTEND" = true ]; then echo -e "${CYAN}[BUILD]${NC} 并行构建镜像..." ${COMPOSE_CMD} ${COMPOSE_ARGS} build --parallel start_postgres_first echo -e "${CYAN}[START]${NC} 启动全部服务..." ${COMPOSE_CMD} ${COMPOSE_ARGS} up -d else echo -e "${CYAN}[BUILD]${NC} 并行构建后端镜像..." ${COMPOSE_CMD} ${COMPOSE_ARGS} build --parallel "${BACKEND_SERVICES[@]}" start_postgres_first echo -e "${CYAN}[START]${NC} 启动后端服务..." ${COMPOSE_CMD} ${COMPOSE_ARGS} up -d "${BACKEND_SERVICES[@]}" fi else # 生产模式:拉取 Docker Hub 镜像 # pull 后 up -d 会自动检测镜像变化并重建容器 if [ "$WITH_FRONTEND" = true ]; then echo -e "${CYAN}[PULL]${NC} 拉取最新镜像..." ${COMPOSE_CMD} ${COMPOSE_ARGS} pull start_postgres_first echo -e "${CYAN}[START]${NC} 启动全部服务..." ${COMPOSE_CMD} ${COMPOSE_ARGS} up -d else echo -e "${CYAN}[PULL]${NC} 拉取后端镜像..." ${COMPOSE_CMD} ${COMPOSE_ARGS} pull "${BACKEND_SERVICES[@]}" start_postgres_first echo -e "${CYAN}[START]${NC} 启动后端服务..." ${COMPOSE_CMD} ${COMPOSE_ARGS} up -d "${BACKEND_SERVICES[@]}" fi fi echo -e "${GREEN}[OK]${NC} 服务已启动" # 数据初始化 if [ "$DEV_MODE" = true ]; then ./scripts/init-data.sh --dev else ./scripts/init-data.sh fi # 静默模式下不显示结果(由调用方显示) if [ "$QUIET_MODE" = true ]; then exit 0 fi # 获取访问地址 PUBLIC_HOST=$(grep "^PUBLIC_HOST=" .env 2>/dev/null | cut -d= -f2) if [ -n "$PUBLIC_HOST" ] && [ "$PUBLIC_HOST" != "server" ]; then ACCESS_HOST="$PUBLIC_HOST" else ACCESS_HOST="localhost" fi # 显示结果 echo "" echo -e "${BOLD}${GREEN}════════════════════════════════════════${NC}" echo -e "${BOLD}${GREEN} 服务启动成功!${NC}" echo -e "${BOLD}${GREEN}════════════════════════════════════════${NC}" echo "" echo -e "${BOLD}访问地址${NC}" if [ "$WITH_FRONTEND" = true ]; then echo -e " XingRin: ${CYAN}https://${ACCESS_HOST}:8083/${NC}" echo -e " ${YELLOW}(HTTP 会自动跳转到 HTTPS)${NC}" else echo -e " API: ${CYAN}通过前端或 nginx 访问(后端未暴露 8888)${NC}" echo "" echo -e "${YELLOW}[TIP]${NC} 前端未启动,请手动运行:" echo " cd frontend && pnpm dev" fi echo ""