Files
2026-08-04 15:34:34 +08:00

213 lines
6.3 KiB
Bash
Executable File
Raw Permalink 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.
#!/usr/bin/env bash
# 一键启动前后端开发服务
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PIDS=()
log() { echo ">>> $*"; }
# 加载仓库根目录 .envsource 后可供 PORT / FRONTEND_PORT 使用)
if [[ -f "$ROOT/.env" ]]; then
set -a
# shellcheck disable=SC1091
source "$ROOT/.env"
set +a
log "已加载 $ROOT/.env"
elif [[ -f "$ROOT/.env.example" ]]; then
log "未找到 .env,可执行: cp .env.example .env"
fi
BACKEND_PORT="${PORT:-18080}"
FRONTEND_PORT="${FRONTEND_PORT:-15173}"
POSTGRES_USER="${POSTGRES_USER:-affiliate}"
POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-affiliate_dev_password}"
POSTGRES_DB="${POSTGRES_DB:-affiliate_dash}"
POSTGRES_PORT="${POSTGRES_PORT:-15432}"
POSTGRES_IMAGE="${POSTGRES_IMAGE:-docker.m.daocloud.io/library/postgres:16-alpine}"
COMPOSE_PROJECT_NAME="${COMPOSE_PROJECT_NAME:-affiliate_dash}"
POSTGRES_DATA_VOLUME="${POSTGRES_DATA_VOLUME:-affiliate_dash_postgres_data}"
LEGACY_DEV_DB_CONTAINER="${DEV_DB_CONTAINER:-affiliate_dash_postgres_dev}"
TZ="${TZ:-Asia/Shanghai}"
DATABASE_URL="${DATABASE_URL:-postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@127.0.0.1:${POSTGRES_PORT}/${POSTGRES_DB}?sslmode=disable&TimeZone=${TZ}}"
# 兼容旧 .env:本地 DATABASE_URL 端口与 POSTGRES_PORT 不一致时提醒
if [[ "${DATABASE_URL}" == *"127.0.0.1:"* ]] && [[ "${DATABASE_URL}" != *"127.0.0.1:${POSTGRES_PORT}/"* ]]; then
log "警告: .env 中 DATABASE_URL 的本地端口与 POSTGRES_PORT(${POSTGRES_PORT}) 不一致,请更新 .env"
fi
export DATABASE_URL
export TZ
export COMPOSE_PROJECT_NAME
export POSTGRES_USER POSTGRES_PASSWORD POSTGRES_DB POSTGRES_PORT POSTGRES_IMAGE POSTGRES_DATA_VOLUME
# 结束指定端口上的进程(避免残留占用)
free_port() {
local port=$1
local pids
pids=$(lsof -tiTCP:"$port" -sTCP:LISTEN 2>/dev/null || true)
if [[ -n "${pids}" ]]; then
log "端口 ${port} 已被占用,正在释放: ${pids}"
# shellcheck disable=SC2086
kill ${pids} 2>/dev/null || true
sleep 0.5
pids=$(lsof -tiTCP:"$port" -sTCP:LISTEN 2>/dev/null || true)
if [[ -n "${pids}" ]]; then
# shellcheck disable=SC2086
kill -9 ${pids} 2>/dev/null || true
fi
fi
}
docker_compose_available() {
command -v docker >/dev/null 2>&1 && docker compose version >/dev/null 2>&1
}
postgres_port_reachable() {
(echo >"/dev/tcp/127.0.0.1/${POSTGRES_PORT}") >/dev/null 2>&1
}
legacy_postgres_container_exists() {
docker ps -a --format '{{.Names}}' | grep -qx "$LEGACY_DEV_DB_CONTAINER"
}
legacy_postgres_container_running() {
docker ps --format '{{.Names}}' | grep -qx "$LEGACY_DEV_DB_CONTAINER"
}
compose_postgres_running() {
docker compose ps --services --status running 2>/dev/null | grep -qx "postgres"
}
prepare_postgres_volume() {
local legacy_volume="${LEGACY_DEV_DB_CONTAINER}-data"
if [[ "${POSTGRES_DATA_VOLUME}" == "affiliate_dash_postgres_data" ]] && docker volume inspect "$legacy_volume" >/dev/null 2>&1; then
POSTGRES_DATA_VOLUME="$legacy_volume"
export POSTGRES_DATA_VOLUME
log "检测到旧开发库数据卷,Compose 将继续复用: ${POSTGRES_DATA_VOLUME}"
fi
}
stop_legacy_postgres_container() {
if legacy_postgres_container_exists; then
if legacy_postgres_container_running; then
log "检测到旧 PostgreSQL 开发容器,停止后交由 Docker Compose 管理: ${LEGACY_DEV_DB_CONTAINER}"
docker stop "$LEGACY_DEV_DB_CONTAINER" >/dev/null
else
log "检测到旧 PostgreSQL 开发容器已停止,保留容器和数据卷: ${LEGACY_DEV_DB_CONTAINER}"
fi
fi
}
ensure_postgres() {
if ! docker_compose_available; then
log "未找到 Docker Compose,请确认本机 PostgreSQL 已可通过 DATABASE_URL 访问"
return 0
fi
prepare_postgres_volume
stop_legacy_postgres_container
if compose_postgres_running; then
log "PostgreSQL Compose 服务已运行: ${COMPOSE_PROJECT_NAME}/postgres"
else
if postgres_port_reachable; then
log "检测到本机 PostgreSQL 端口已可访问: ${POSTGRES_PORT},跳过 Compose 数据库启动"
return 0
fi
log "启动 PostgreSQL Compose 服务: ${COMPOSE_PROJECT_NAME}/postgres"
docker compose up -d postgres >/dev/null
fi
for i in $(seq 1 40); do
if docker compose exec -T postgres pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB" >/dev/null 2>&1; then
log "PostgreSQL 已就绪"
return 0
fi
sleep 0.5
done
echo "错误: PostgreSQL 启动超时"
docker compose logs postgres 2>/dev/null || true
exit 1
}
cleanup() {
trap - INT TERM EXIT
echo ""
log "正在停止前后端..."
for pid in "${PIDS[@]:-}"; do
if kill -0 "$pid" 2>/dev/null; then
# 结束子进程树
pkill -P "$pid" 2>/dev/null || true
kill "$pid" 2>/dev/null || true
fi
done
free_port "$BACKEND_PORT"
free_port "$FRONTEND_PORT"
wait 2>/dev/null || true
log "已全部停止"
exit 0
}
trap cleanup INT TERM
# 检查依赖
if ! command -v go >/dev/null 2>&1; then
echo "错误: 未找到 go,请先安装 Go"
exit 1
fi
if ! command -v npm >/dev/null 2>&1; then
echo "错误: 未找到 npm,请先安装 Node.js"
exit 1
fi
# 首次安装前端依赖
if [[ ! -d "$ROOT/frontend/node_modules" ]]; then
log "安装前端依赖..."
(cd "$ROOT/frontend" && npm install)
fi
free_port "$BACKEND_PORT"
free_port "$FRONTEND_PORT"
ensure_postgres
log "启动后端 http://localhost:${BACKEND_PORT}"
(
cd "$ROOT/backend"
export PORT="$BACKEND_PORT"
go run ./cmd/server
) &
PIDS+=($!)
# 等后端健康检查就绪(最多约 15 秒)
for i in $(seq 1 30); do
if curl -sf "http://127.0.0.1:${BACKEND_PORT}/health" >/dev/null 2>&1; then
log "后端已就绪"
break
fi
if ! kill -0 "${PIDS[0]}" 2>/dev/null; then
echo "错误: 后端启动失败"
cleanup
fi
sleep 0.5
done
log "启动前端 http://localhost:${FRONTEND_PORT}"
(
cd "$ROOT/frontend"
npm run dev -- --host --port "$FRONTEND_PORT"
) &
PIDS+=($!)
echo ""
log "========================================"
log " 游戏皮肤分销系统 已启动"
log " 前端: http://localhost:${FRONTEND_PORT}"
log " 后端: http://localhost:${BACKEND_PORT}"
log " 账号: admin / admin123"
log " 按 Ctrl+C 停止全部服务"
log "========================================"
echo ""
# 任一子进程退出则整体清理
wait -n 2>/dev/null || wait
cleanup