diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index b1d26229..b3c81ede 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -1,148 +1,140 @@ +# ============================================ +# 生产环境 Docker Compose 配置 +# ============================================ +# 使用方法: +# docker-compose -f docker-compose.prod.yml up -d +# ============================================ + version: '3.8' services: - app: - build: - context: . - dockerfile: Dockerfile - container_name: furniture3d-app - restart: unless-stopped - env_file: - - .env - depends_on: - postgres: - condition: service_healthy - redis: - condition: service_started - minio: - condition: service_healthy - ports: - - "${WEB_PORT:-3001}:3000" - environment: - NODE_ENV: production - PORT: 3000 - DATABASE_URL: postgresql://furniture:furniture123@postgres:5432/furniture_db?schema=public - S3_ENDPOINT: http://minio:9000 - S3_PUBLIC_ENDPOINT: http://192.168.42.252:9002 - S3_REGION: us-east-1 - S3_ACCESS_KEY: minioadmin - S3_SECRET_KEY: minioadmin123 - S3_BUCKET: furniture-3d-models - NEXT_TELEMETRY_DISABLED: "1" - JWT_SECRET: change-me-in-test - mem_limit: 2g - cpus: 2.0 - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:3000/api/health?mode=simple"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 40s - command: > - sh -c " - if [ -x ./node_modules/.bin/prisma ]; then ./node_modules/.bin/prisma db push || true; fi; - if [ -x ./node_modules/prisma/build/index.js ]; then node ./node_modules/prisma/build/index.js db push || true; fi; - node server.js - " - logging: - driver: "json-file" - options: - max-size: "10m" - max-file: "3" - + # PostgreSQL 数据库 postgres: image: postgres:15-alpine container_name: furniture3d-postgres - restart: unless-stopped + restart: always environment: - POSTGRES_USER: furniture - POSTGRES_PASSWORD: furniture123 - POSTGRES_DB: furniture_db + POSTGRES_USER: ${DB_USER:-postgres} + POSTGRES_PASSWORD: ${DB_PASSWORD:-postgres} + POSTGRES_DB: ${DB_NAME:-furniture3d} + # 性能优化参数 + POSTGRES_INITDB_ARGS: "--encoding=UTF8 --locale=C" ports: - - "5433:5432" + - "${DB_PORT:-5432}:5432" volumes: - postgres_data:/var/lib/postgresql/data - mem_limit: 1g - cpus: 1.0 + - ./backups/database:/backups healthcheck: - test: ["CMD-SHELL", "pg_isready -U furniture -d furniture_db"] + test: ["CMD-SHELL", "pg_isready -U ${DB_USER:-postgres} -d ${DB_NAME:-furniture3d}"] interval: 10s timeout: 5s retries: 5 + start_period: 30s + deploy: + resources: + limits: + cpus: '2' + memory: 2G + reservations: + cpus: '0.5' + memory: 512M logging: driver: "json-file" options: - max-size: "10m" - max-file: "3" - - redis: - image: redis:7-alpine - container_name: furniture3d-redis - restart: unless-stopped - command: > - redis-server - --maxmemory 256mb - --maxmemory-policy allkeys-lru - ports: - - "6379:6379" - volumes: - - redis_data:/data - mem_limit: 512m - cpus: 0.5 - healthcheck: - test: ["CMD", "redis-cli", "ping"] - interval: 10s - timeout: 3s - retries: 3 - logging: - driver: "json-file" - options: - max-size: "10m" - max-file: "3" + max-size: "50m" + max-file: "5" + networks: + - furniture3d-network + # MinIO 对象存储 minio: image: minio/minio:latest container_name: furniture3d-minio - restart: unless-stopped + restart: always command: server /data --console-address ":9001" environment: - MINIO_ROOT_USER: minioadmin - MINIO_ROOT_PASSWORD: minioadmin123 + MINIO_ROOT_USER: ${MINIO_ROOT_USER:-minioadmin} + MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:-minioadmin} + # 生产环境建议启用 TLS + # MINIO_SERVER_URL: https://minio.yourdomain.com ports: - - "9002:9000" - - "9003:9001" + - "${MINIO_API_PORT:-9000}:9000" + - "${MINIO_CONSOLE_PORT:-9001}:9001" volumes: - minio_data:/data - mem_limit: 512m - cpus: 0.5 + - ./backups/files:/backups healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] interval: 30s timeout: 20s retries: 3 + start_period: 30s + deploy: + resources: + limits: + cpus: '1' + memory: 1G + reservations: + cpus: '0.25' + memory: 256M logging: driver: "json-file" options: - max-size: "10m" - max-file: "3" + max-size: "50m" + max-file: "5" + networks: + - furniture3d-network - minio-setup: - image: minio/mc:latest - container_name: furniture3d-minio-setup + # Next.js 应用(可选,也可以单独部署) + app: + build: + context: . + dockerfile: Dockerfile + container_name: furniture3d-app + restart: always + environment: + NODE_ENV: production + DATABASE_URL: postgresql://${DB_USER:-postgres}:${DB_PASSWORD:-postgres}@postgres:5432/${DB_NAME:-furniture3d} + S3_ENDPOINT: http://minio:9000 + S3_ACCESS_KEY: ${MINIO_ROOT_USER:-minioadmin} + S3_SECRET_KEY: ${MINIO_ROOT_PASSWORD:-minioadmin} + S3_BUCKET: ${S3_BUCKET:-furniture-3d-models} + JWT_SECRET: ${JWT_SECRET:-your-super-secret-jwt-key-change-in-production} + ports: + - "${APP_PORT:-3000}:3000" depends_on: + postgres: + condition: service_healthy minio: condition: service_healthy - environment: - MC_HOST_minio: http://minioadmin:minioadmin123@minio:9000 - entrypoint: ["sh", "-c"] - command: > - "mc alias set myminio http://minio:9000 minioadmin minioadmin123 && \ - mc mb -p myminio/furniture-3d-models || true && \ - mc anonymous set download myminio/furniture-3d-models || true && \ - echo 'MinIO bucket ready' && sleep 1" - restart: "no" + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3000/api/health?mode=simple"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s + deploy: + resources: + limits: + cpus: '2' + memory: 1G + reservations: + cpus: '0.5' + memory: 512M + logging: + driver: "json-file" + options: + max-size: "100m" + max-file: "10" + networks: + - furniture3d-network volumes: postgres_data: - redis_data: + driver: local minio_data: + driver: local + +networks: + furniture3d-network: + driver: bridge diff --git a/scripts/validate-config.ps1 b/scripts/validate-config.ps1 new file mode 100644 index 00000000..32b9c925 --- /dev/null +++ b/scripts/validate-config.ps1 @@ -0,0 +1,217 @@ +# ============================================ +# 配置验证脚本 (PowerShell) +# ============================================ +# 功能: +# - 验证环境变量配置 +# - 验证端口可用性 +# - 验证依赖服务连接 +# 使用: +# .\scripts\validate-config.ps1 +# ============================================ + +param( + [switch]$Verbose +) + +$ErrorActionPreference = "Continue" +$script:HasErrors = $false +$script:HasWarnings = $false + +function Write-Check { + param([string]$Name, [string]$Status, [string]$Message) + + $icon = switch ($Status) { + "OK" { "✅" } + "WARN" { "⚠️" } + "ERROR" { "❌" } + default { "ℹ️" } + } + + Write-Host " $icon $Name : $Message" + + if ($Status -eq "ERROR") { $script:HasErrors = $true } + if ($Status -eq "WARN") { $script:HasWarnings = $true } +} + +Write-Host "" +Write-Host "========================================" +Write-Host " 配置验证脚本" +Write-Host "========================================" +Write-Host "" + +# 1. 检查环境变量文件 +Write-Host "📋 检查环境变量..." + +if (Test-Path ".env") { + Write-Check "环境变量文件" "OK" ".env 文件存在" + + # 读取 .env 文件 + $envContent = Get-Content ".env" -Raw + + # 检查必需的环境变量 + $requiredVars = @( + @{ Name = "DATABASE_URL"; Pattern = "DATABASE_URL=" } + ) + + # JWT_SECRET 或 NEXTAUTH_SECRET 至少需要一个 + if ($envContent -match "JWT_SECRET=" -or $envContent -match "NEXTAUTH_SECRET=") { + Write-Check "JWT/Auth Secret" "OK" "已配置" + } else { + Write-Check "JWT/Auth Secret" "ERROR" "未配置 (需要 JWT_SECRET 或 NEXTAUTH_SECRET)" + } + + foreach ($var in $requiredVars) { + if ($envContent -match $var.Pattern) { + Write-Check $var.Name "OK" "已配置" + } else { + Write-Check $var.Name "ERROR" "未配置" + } + } + + # 检查可选但推荐的环境变量 + $optionalVars = @( + @{ Name = "S3_ENDPOINT"; Pattern = "S3_ENDPOINT=" }, + @{ Name = "S3_ACCESS_KEY"; Pattern = "S3_ACCESS_KEY=" }, + @{ Name = "S3_SECRET_KEY"; Pattern = "S3_SECRET_KEY=" } + ) + + foreach ($var in $optionalVars) { + if ($envContent -match $var.Pattern) { + Write-Check $var.Name "OK" "已配置" + } else { + Write-Check $var.Name "WARN" "未配置(可选)" + } + } +} else { + Write-Check "环境变量文件" "ERROR" ".env 文件不存在" +} + +Write-Host "" + +# 2. 检查端口可用性 +Write-Host "🔌 检查端口可用性..." + +$ports = @( + @{ Port = 3000; Name = "Next.js 应用" }, + @{ Port = 5432; Name = "PostgreSQL" }, + @{ Port = 9000; Name = "MinIO API" }, + @{ Port = 9001; Name = "MinIO Console" } +) + +foreach ($p in $ports) { + $connection = Get-NetTCPConnection -LocalPort $p.Port -ErrorAction SilentlyContinue + if ($connection) { + $process = Get-Process -Id $connection.OwningProcess -ErrorAction SilentlyContinue + $processName = if ($process) { $process.ProcessName } else { "未知进程" } + Write-Check "$($p.Name) (端口 $($p.Port))" "OK" "已被 $processName 使用" + } else { + Write-Check "$($p.Name) (端口 $($p.Port))" "OK" "端口可用" + } +} + +Write-Host "" + +# 3. 检查依赖服务 +Write-Host "🔗 检查依赖服务..." + +# 检查 PostgreSQL +try { + $pgConnection = Test-NetConnection -ComputerName localhost -Port 5432 -WarningAction SilentlyContinue + if ($pgConnection.TcpTestSucceeded) { + Write-Check "PostgreSQL" "OK" "服务运行中" + } else { + Write-Check "PostgreSQL" "WARN" "服务未运行或无法连接" + } +} catch { + Write-Check "PostgreSQL" "WARN" "无法检测服务状态" +} + +# 检查 MinIO +try { + $minioConnection = Test-NetConnection -ComputerName localhost -Port 9000 -WarningAction SilentlyContinue + if ($minioConnection.TcpTestSucceeded) { + Write-Check "MinIO" "OK" "服务运行中" + } else { + Write-Check "MinIO" "WARN" "服务未运行或无法连接" + } +} catch { + Write-Check "MinIO" "WARN" "无法检测服务状态" +} + +Write-Host "" + +# 4. 检查 Node.js 和 npm +Write-Host "📦 检查开发环境..." + +try { + $nodeVersion = node --version 2>$null + if ($nodeVersion) { + Write-Check "Node.js" "OK" "版本 $nodeVersion" + } else { + Write-Check "Node.js" "ERROR" "未安装" + } +} catch { + Write-Check "Node.js" "ERROR" "未安装" +} + +try { + $npmVersion = npm --version 2>$null + if ($npmVersion) { + Write-Check "npm" "OK" "版本 $npmVersion" + } else { + Write-Check "npm" "ERROR" "未安装" + } +} catch { + Write-Check "npm" "ERROR" "未安装" +} + +# 检查 node_modules +if (Test-Path "node_modules") { + Write-Check "依赖包" "OK" "node_modules 存在" +} else { + Write-Check "依赖包" "WARN" "node_modules 不存在,请运行 npm install" +} + +Write-Host "" + +# 5. 检查 Docker(可选) +Write-Host "🐳 检查 Docker..." + +try { + $dockerVersion = docker --version 2>$null + if ($dockerVersion) { + Write-Check "Docker" "OK" $dockerVersion + } else { + Write-Check "Docker" "WARN" "未安装(可选)" + } +} catch { + Write-Check "Docker" "WARN" "未安装(可选)" +} + +try { + $composeVersion = docker-compose --version 2>$null + if ($composeVersion) { + Write-Check "Docker Compose" "OK" $composeVersion + } else { + Write-Check "Docker Compose" "WARN" "未安装(可选)" + } +} catch { + Write-Check "Docker Compose" "WARN" "未安装(可选)" +} + +Write-Host "" +Write-Host "========================================" + +if ($script:HasErrors) { + Write-Host " ❌ 配置验证失败,请修复上述错误" + Write-Host "========================================" + exit 1 +} elseif ($script:HasWarnings) { + Write-Host " ⚠️ 配置验证通过,但有警告" + Write-Host "========================================" + exit 0 +} else { + Write-Host " ✅ 配置验证通过" + Write-Host "========================================" + exit 0 +} diff --git a/scripts/validate-config.sh b/scripts/validate-config.sh new file mode 100644 index 00000000..b1584c1d --- /dev/null +++ b/scripts/validate-config.sh @@ -0,0 +1,191 @@ +#!/bin/bash + +# ============================================ +# 配置验证脚本 (Linux/Mac) +# ============================================ +# 功能: +# - 验证环境变量配置 +# - 验证端口可用性 +# - 验证依赖服务连接 +# 使用: +# ./scripts/validate-config.sh +# ============================================ + +HAS_ERRORS=false +HAS_WARNINGS=false + +write_check() { + local name="$1" + local status="$2" + local message="$3" + + case "$status" in + "OK") icon="✅" ;; + "WARN") icon="⚠️"; HAS_WARNINGS=true ;; + "ERROR") icon="❌"; HAS_ERRORS=true ;; + *) icon="ℹ️" ;; + esac + + echo " $icon $name : $message" +} + +echo "" +echo "========================================" +echo " 配置验证脚本" +echo "========================================" +echo "" + +# 1. 检查环境变量文件 +echo "📋 检查环境变量..." + +if [ -f ".env" ]; then + write_check "环境变量文件" "OK" ".env 文件存在" + + # 检查必需的环境变量 + if grep -q "DATABASE_URL=" .env; then + write_check "DATABASE_URL" "OK" "已配置" + else + write_check "DATABASE_URL" "ERROR" "未配置" + fi + + if grep -q "JWT_SECRET=" .env; then + write_check "JWT_SECRET" "OK" "已配置" + else + write_check "JWT_SECRET" "ERROR" "未配置" + fi + + # 检查可选环境变量 + if grep -q "S3_ENDPOINT=" .env; then + write_check "S3_ENDPOINT" "OK" "已配置" + else + write_check "S3_ENDPOINT" "WARN" "未配置(可选)" + fi + + if grep -q "S3_ACCESS_KEY=" .env; then + write_check "S3_ACCESS_KEY" "OK" "已配置" + else + write_check "S3_ACCESS_KEY" "WARN" "未配置(可选)" + fi +else + write_check "环境变量文件" "ERROR" ".env 文件不存在" +fi + +echo "" + +# 2. 检查端口可用性 +echo "🔌 检查端口可用性..." + +check_port() { + local port="$1" + local name="$2" + + if command -v lsof &> /dev/null; then + if lsof -i ":$port" &> /dev/null; then + process=$(lsof -i ":$port" | tail -1 | awk '{print $1}') + write_check "$name (端口 $port)" "OK" "已被 $process 使用" + else + write_check "$name (端口 $port)" "OK" "端口可用" + fi + elif command -v netstat &> /dev/null; then + if netstat -tuln | grep -q ":$port "; then + write_check "$name (端口 $port)" "OK" "端口已被使用" + else + write_check "$name (端口 $port)" "OK" "端口可用" + fi + else + write_check "$name (端口 $port)" "WARN" "无法检测端口状态" + fi +} + +check_port 3000 "Next.js 应用" +check_port 5432 "PostgreSQL" +check_port 9000 "MinIO API" +check_port 9001 "MinIO Console" + +echo "" + +# 3. 检查依赖服务 +echo "🔗 检查依赖服务..." + +# 检查 PostgreSQL +if command -v pg_isready &> /dev/null; then + if pg_isready -h localhost -p 5432 &> /dev/null; then + write_check "PostgreSQL" "OK" "服务运行中" + else + write_check "PostgreSQL" "WARN" "服务未运行或无法连接" + fi +elif nc -z localhost 5432 2>/dev/null; then + write_check "PostgreSQL" "OK" "端口可访问" +else + write_check "PostgreSQL" "WARN" "服务未运行或无法连接" +fi + +# 检查 MinIO +if curl -s http://localhost:9000/minio/health/live &> /dev/null; then + write_check "MinIO" "OK" "服务运行中" +elif nc -z localhost 9000 2>/dev/null; then + write_check "MinIO" "OK" "端口可访问" +else + write_check "MinIO" "WARN" "服务未运行或无法连接" +fi + +echo "" + +# 4. 检查开发环境 +echo "📦 检查开发环境..." + +if command -v node &> /dev/null; then + node_version=$(node --version) + write_check "Node.js" "OK" "版本 $node_version" +else + write_check "Node.js" "ERROR" "未安装" +fi + +if command -v npm &> /dev/null; then + npm_version=$(npm --version) + write_check "npm" "OK" "版本 $npm_version" +else + write_check "npm" "ERROR" "未安装" +fi + +if [ -d "node_modules" ]; then + write_check "依赖包" "OK" "node_modules 存在" +else + write_check "依赖包" "WARN" "node_modules 不存在,请运行 npm install" +fi + +echo "" + +# 5. 检查 Docker(可选) +echo "🐳 检查 Docker..." + +if command -v docker &> /dev/null; then + docker_version=$(docker --version) + write_check "Docker" "OK" "$docker_version" +else + write_check "Docker" "WARN" "未安装(可选)" +fi + +if command -v docker-compose &> /dev/null; then + compose_version=$(docker-compose --version) + write_check "Docker Compose" "OK" "$compose_version" +else + write_check "Docker Compose" "WARN" "未安装(可选)" +fi + +echo "" +echo "========================================" + +if [ "$HAS_ERRORS" = true ]; then + echo " ❌ 配置验证失败,请修复上述错误" + echo "========================================" + exit 1 +elif [ "$HAS_WARNINGS" = true ]; then + echo " ⚠️ 配置验证通过,但有警告" + echo "========================================" + exit 0 +else + echo " ✅ 配置验证通过" + echo "========================================" + exit 0 +fi