diff --git a/.env.example b/.env.example index 0ee40c3..36f1247 100644 --- a/.env.example +++ b/.env.example @@ -4,8 +4,13 @@ DATA_ENCRYPTION_KEY=your_32_plus_char_data_encryption_key_change_me POSTGRES_USER=affiliate POSTGRES_PASSWORD=affiliate_dev_password POSTGRES_DB=affiliate_dash +POSTGRES_PORT=5432 +COMPOSE_PROJECT_NAME=affiliate_dash +POSTGRES_DATA_VOLUME=affiliate_dash_postgres_data TZ=Asia/Shanghai DATABASE_URL="postgres://affiliate:affiliate_dev_password@127.0.0.1:5432/affiliate_dash?sslmode=disable&TimeZone=Asia/Shanghai" +# Docker 后端容器内连接串通常不需要改;需要外部数据库时再覆盖。 +# DOCKER_DATABASE_URL="postgres://affiliate:affiliate_dev_password@postgres:5432/affiliate_dash?sslmode=disable&TimeZone=Asia/Shanghai" GIN_MODE=release OPEN_API_KEY=sk_source_dev_key_change_me OPEN_API_SECRET=sk_source_dev_secret_change_me diff --git a/Caddyfile b/Caddyfile index 55f12a4..e6bb222 100644 --- a/Caddyfile +++ b/Caddyfile @@ -3,7 +3,7 @@ # admin off } -skin.khhao.com { +(app_routes) { encode gzip zstd # /api-debug 是前端 SPA 路由,不要代理到后端 @@ -25,3 +25,11 @@ skin.khhao.com { file_server } } + +:80 { + import app_routes +} + +skin.khhao.com { + import app_routes +} diff --git a/README.md b/README.md index 5898baa..e21fadb 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ - Go 1.26+ - Node.js 22+ -- Docker(用于自动启动 PostgreSQL 开发容器) +- Docker / Docker Compose(用于自动启动 PostgreSQL 开发服务) ### 快速启动 @@ -33,6 +33,8 @@ make dev ./start.sh ``` +`make dev` 会通过 Docker Compose 启动 `affiliate_dash/postgres`,后端和前端仍在宿主机运行,方便热更新。首次从旧版 `affiliate_dash_postgres_dev` 开发容器迁移时,脚本会停止旧容器并优先复用旧数据卷。 + 启动后: - 前端:`http://localhost:5173` - 后端:`http://localhost:8080` @@ -61,10 +63,11 @@ cp .env.example .env # JWT_SECRET — 生产环境务必修改 # OPEN_API_KEY — 皮肤源头对接 API Key # OPEN_API_SECRET — 签名密钥 -# DATABASE_URL — PostgreSQL 连接,容器内主机名为 postgres +# POSTGRES_USER / POSTGRES_PASSWORD / POSTGRES_DB — PostgreSQL 初始化配置 ``` > 国内服务器可在 `.env` 中取消 `GOPROXY`、`ALPINE_MIRROR`、`NPM_REGISTRY` 等镜像加速源的注释。 +> Docker 后端容器默认使用 `postgres` 作为数据库主机;只有连接外部数据库时才需要设置 `DOCKER_DATABASE_URL`。 ### 启动服务 @@ -78,6 +81,7 @@ docker compose up -d --build 服务端口: - `:80` — 前端页面 + API(Caddy 统一入口) - `:8080` — 后端 API(直接访问,调试用) +- `:5432` — PostgreSQL(本地开发/调试用,可通过 `POSTGRES_PORT` 调整) ### 常用命令 @@ -89,7 +93,7 @@ make docker-down # 停止并移除 ### 自定义域名 -修改 `Caddyfile` 中的域名 `skin.khhao.com` 为你的域名,重新构建即可。 +`Caddyfile` 默认同时支持本地 `:80` 和示例域名 `skin.khhao.com`。生产部署时把 `skin.khhao.com` 改为你的域名,重新构建即可。 ## 环境变量 @@ -98,6 +102,10 @@ make docker-down # 停止并移除 | `PORT` | 后端端口 | `8080` | | `JWT_SECRET` | JWT 签名密钥 | 开发默认值 | | `DATABASE_URL` | PostgreSQL 连接串 | 本地开发默认值 | +| `DOCKER_DATABASE_URL` | Docker 后端容器内 PostgreSQL 连接串,通常无需设置 | Compose 内置默认值 | +| `POSTGRES_PORT` | PostgreSQL 映射到宿主机的端口 | `5432` | +| `COMPOSE_PROJECT_NAME` | Docker Compose 项目名 | `affiliate_dash` | +| `POSTGRES_DATA_VOLUME` | PostgreSQL 数据卷名 | `affiliate_dash_postgres_data` | | `GIN_MODE` | Gin 运行模式 | `debug` | | `OPEN_API_KEY` | 开放接口 ApiKey | 需修改 | | `OPEN_API_SECRET` | 开放接口签名密钥 | 需修改 | diff --git a/docker-compose.yml b/docker-compose.yml index 10b50a7..997dafb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,3 +1,5 @@ +name: ${COMPOSE_PROJECT_NAME:-affiliate_dash} + services: postgres: image: ${POSTGRES_IMAGE:-docker.m.daocloud.io/library/postgres:16-alpine} @@ -8,6 +10,8 @@ services: - TZ=${TZ:-Asia/Shanghai} - PGTZ=${TZ:-Asia/Shanghai} command: ["postgres", "-c", "timezone=${TZ:-Asia/Shanghai}"] + ports: + - "${POSTGRES_PORT:-5432}:5432" volumes: - postgres-data:/var/lib/postgresql/data restart: unless-stopped @@ -34,7 +38,7 @@ services: - PORT=8080 - TZ=${TZ:-Asia/Shanghai} - JWT_SECRET=${JWT_SECRET} - - "DATABASE_URL=postgres://${POSTGRES_USER:-affiliate}:${POSTGRES_PASSWORD:-affiliate_dev_password}@postgres:5432/${POSTGRES_DB:-affiliate_dash}?sslmode=disable&TimeZone=${TZ:-Asia/Shanghai}" + - "DATABASE_URL=${DOCKER_DATABASE_URL:-postgres://${POSTGRES_USER:-affiliate}:${POSTGRES_PASSWORD:-affiliate_dev_password}@postgres:5432/${POSTGRES_DB:-affiliate_dash}?sslmode=disable&TimeZone=${TZ:-Asia/Shanghai}}" - GIN_MODE=${GIN_MODE:-release} - OPEN_API_KEY=${OPEN_API_KEY} - OPEN_API_SECRET=${OPEN_API_SECRET} @@ -79,6 +83,7 @@ services: volumes: postgres-data: + name: ${POSTGRES_DATA_VOLUME:-affiliate_dash_postgres_data} backend-data: caddy-data: caddy-config: diff --git a/start.sh b/start.sh index e835f44..600713c 100755 --- a/start.sh +++ b/start.sh @@ -25,11 +25,15 @@ POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-affiliate_dev_password}" POSTGRES_DB="${POSTGRES_DB:-affiliate_dash}" POSTGRES_PORT="${POSTGRES_PORT:-5432}" POSTGRES_IMAGE="${POSTGRES_IMAGE:-docker.m.daocloud.io/library/postgres:16-alpine}" -DEV_DB_CONTAINER="${DEV_DB_CONTAINER:-affiliate_dash_postgres_dev}" +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}}" 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() { @@ -49,64 +53,75 @@ free_port() { fi } -postgres_container_exists() { - docker ps -a --format '{{.Names}}' | grep -qx "$DEV_DB_CONTAINER" +docker_compose_available() { + command -v docker >/dev/null 2>&1 && docker compose version >/dev/null 2>&1 } -postgres_container_running() { - docker ps --format '{{.Names}}' | grep -qx "$DEV_DB_CONTAINER" +postgres_port_reachable() { + (echo >"/dev/tcp/127.0.0.1/${POSTGRES_PORT}") >/dev/null 2>&1 } -postgres_container_timezone_ready() { - local env cmd - env="$(docker inspect -f '{{range .Config.Env}}{{println .}}{{end}}' "$DEV_DB_CONTAINER" 2>/dev/null || true)" - cmd="$(docker inspect -f '{{json .Config.Cmd}}' "$DEV_DB_CONTAINER" 2>/dev/null || true)" - grep -qx "TZ=${TZ}" <<<"$env" && grep -qx "PGTZ=${TZ}" <<<"$env" && [[ "$cmd" == *"timezone=${TZ}"* ]] +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 command -v docker >/dev/null 2>&1 && postgres_container_exists; then - if ! postgres_container_timezone_ready; then - log "检测到旧 PostgreSQL 开发容器缺少时区配置,重建容器并保留数据卷: ${DEV_DB_CONTAINER}" - docker rm -f "$DEV_DB_CONTAINER" >/dev/null - fi - elif (echo >"/dev/tcp/127.0.0.1/${POSTGRES_PORT}") >/dev/null 2>&1; then - log "检测到本机 PostgreSQL 端口已可访问: ${POSTGRES_PORT}" + if ! docker_compose_available; then + log "未找到 Docker Compose,请确认本机 PostgreSQL 已可通过 DATABASE_URL 访问" return 0 fi - if ! command -v docker >/dev/null 2>&1; then - log "未找到 Docker,请确认本机 PostgreSQL 已可通过 DATABASE_URL 访问" - return 0 - fi - if postgres_container_running; then - log "PostgreSQL 开发容器已运行: ${DEV_DB_CONTAINER}" - elif postgres_container_exists; then - log "启动 PostgreSQL 开发容器: ${DEV_DB_CONTAINER}" - docker start "$DEV_DB_CONTAINER" >/dev/null + + prepare_postgres_volume + stop_legacy_postgres_container + + if compose_postgres_running; then + log "PostgreSQL Compose 服务已运行: ${COMPOSE_PROJECT_NAME}/postgres" else - log "创建 PostgreSQL 开发容器: ${DEV_DB_CONTAINER}" - docker run -d \ - --name "$DEV_DB_CONTAINER" \ - -p "${POSTGRES_PORT}:5432" \ - -e POSTGRES_USER="$POSTGRES_USER" \ - -e POSTGRES_PASSWORD="$POSTGRES_PASSWORD" \ - -e POSTGRES_DB="$POSTGRES_DB" \ - -e TZ="$TZ" \ - -e PGTZ="$TZ" \ - -v "${DEV_DB_CONTAINER}-data:/var/lib/postgresql/data" \ - "$POSTGRES_IMAGE" \ - -c timezone="$TZ" >/dev/null + 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 exec "$DEV_DB_CONTAINER" pg_isready -U "$POSTGRES_USER" -d "$POSTGRES_DB" >/dev/null 2>&1; then + 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 logs "$DEV_DB_CONTAINER" 2>/dev/null || true + docker compose logs postgres 2>/dev/null || true exit 1 }