- windsurf: client/pool/local_ls/tool_emulation/tool_names/models 调整 - handler: admin account_data / failover_loop / gateway_handler - repository: scheduler_cache 及测试 - service: windsurf_chat_service / windsurf_gateway_service - deploy: compose 合并为单文件(含 windsurf-ls profile),Dockerfile.ls - cmd: 新增 dump_ls_models / dump_preamble / test_windsurf_tools 辅助工具
253 lines
9.5 KiB
YAML
253 lines
9.5 KiB
YAML
# =============================================================================
|
||
# Sub2API - Docker Compose Configuration (All-in-One)
|
||
# =============================================================================
|
||
# 包含服务:
|
||
# - sub2api 主应用
|
||
# - postgres 数据库
|
||
# - redis 缓存
|
||
# - windsurf-ls 可选: Windsurf Language Server (通过 profile 启用)
|
||
#
|
||
# 启动方式:
|
||
# 默认三件套: docker compose up -d
|
||
# 含 Windsurf LS: docker compose --profile windsurf up -d
|
||
#
|
||
# 注意:
|
||
# - 首次启动前先复制 .env.example 为 .env 并填写必填变量
|
||
# - JWT_SECRET / TOTP_ENCRYPTION_KEY 多实例必须固定
|
||
# - windsurf-ls 镜像仅支持 linux/amd64, arm64 宿主机会通过 QEMU 模拟
|
||
# =============================================================================
|
||
|
||
services:
|
||
# ===========================================================================
|
||
# Sub2API Application
|
||
# ===========================================================================
|
||
sub2api:
|
||
image: docker.io/zfc931912343/sub2api:latest
|
||
container_name: sub2api
|
||
restart: unless-stopped
|
||
ulimits:
|
||
nofile:
|
||
soft: 100000
|
||
hard: 100000
|
||
ports:
|
||
- "0.0.0.0:80:8080"
|
||
volumes:
|
||
- sub2api_data:/app/data
|
||
# Optional: 挂载自定义 config.yaml(先从 config.example.yaml 复制并修改)
|
||
# - ./config.yaml:/app/data/config.yaml
|
||
# Optional: 自定义 Codex instructions 模板
|
||
# - ./codex-instructions.md.tmpl:/app/data/codex-instructions.md.tmpl:ro
|
||
environment:
|
||
- AUTO_SETUP=true
|
||
|
||
# --- Server ---
|
||
- SERVER_HOST=0.0.0.0
|
||
- SERVER_PORT=8080
|
||
- SERVER_MODE=${SERVER_MODE:-release}
|
||
- RUN_MODE=${RUN_MODE:-standard}
|
||
|
||
# --- Database (PostgreSQL) ---
|
||
- DATABASE_HOST=postgres
|
||
- DATABASE_PORT=5432
|
||
- DATABASE_USER=${POSTGRES_USER:-sub2api}
|
||
- DATABASE_PASSWORD=${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}
|
||
- DATABASE_DBNAME=${POSTGRES_DB:-sub2api}
|
||
- DATABASE_SSLMODE=disable
|
||
- DATABASE_MAX_OPEN_CONNS=${DATABASE_MAX_OPEN_CONNS:-50}
|
||
- DATABASE_MAX_IDLE_CONNS=${DATABASE_MAX_IDLE_CONNS:-10}
|
||
- DATABASE_CONN_MAX_LIFETIME_MINUTES=${DATABASE_CONN_MAX_LIFETIME_MINUTES:-30}
|
||
- DATABASE_CONN_MAX_IDLE_TIME_MINUTES=${DATABASE_CONN_MAX_IDLE_TIME_MINUTES:-5}
|
||
|
||
# --- Redis ---
|
||
- REDIS_HOST=redis
|
||
- REDIS_PORT=6379
|
||
- REDIS_PASSWORD=${REDIS_PASSWORD:-}
|
||
- REDIS_DB=${REDIS_DB:-0}
|
||
- REDIS_POOL_SIZE=${REDIS_POOL_SIZE:-1024}
|
||
- REDIS_MIN_IDLE_CONNS=${REDIS_MIN_IDLE_CONNS:-10}
|
||
- REDIS_ENABLE_TLS=${REDIS_ENABLE_TLS:-false}
|
||
|
||
# --- Admin(仅首次启动生效)---
|
||
- ADMIN_EMAIL=${ADMIN_EMAIL:-admin@sub2api.local}
|
||
- ADMIN_PASSWORD=${ADMIN_PASSWORD:-}
|
||
|
||
# --- JWT(多实例必须固定,否则重启后 session 失效)---
|
||
# 生成: openssl rand -hex 32
|
||
- JWT_SECRET=${JWT_SECRET:?JWT_SECRET is required for multi-instance}
|
||
- JWT_EXPIRE_HOUR=${JWT_EXPIRE_HOUR:-24}
|
||
|
||
# --- TOTP 2FA(多实例必须固定,否则 2FA 失效)---
|
||
# 生成: openssl rand -hex 32
|
||
- TOTP_ENCRYPTION_KEY=${TOTP_ENCRYPTION_KEY:?TOTP_ENCRYPTION_KEY is required for multi-instance}
|
||
|
||
# --- Timezone ---
|
||
- TZ=${TZ:-Asia/Shanghai}
|
||
|
||
# --- Gemini OAuth ---
|
||
- GEMINI_OAUTH_CLIENT_ID=${GEMINI_OAUTH_CLIENT_ID:-}
|
||
- GEMINI_OAUTH_CLIENT_SECRET=${GEMINI_OAUTH_CLIENT_SECRET:-}
|
||
- GEMINI_OAUTH_SCOPES=${GEMINI_OAUTH_SCOPES:-}
|
||
- GEMINI_QUOTA_POLICY=${GEMINI_QUOTA_POLICY:-}
|
||
- GEMINI_CLI_OAUTH_CLIENT_SECRET=${GEMINI_CLI_OAUTH_CLIENT_SECRET:-}
|
||
- ANTIGRAVITY_OAUTH_CLIENT_SECRET=${ANTIGRAVITY_OAUTH_CLIENT_SECRET:-}
|
||
|
||
# --- Security ---
|
||
- SECURITY_URL_ALLOWLIST_ENABLED=${SECURITY_URL_ALLOWLIST_ENABLED:-false}
|
||
- SECURITY_URL_ALLOWLIST_ALLOW_INSECURE_HTTP=${SECURITY_URL_ALLOWLIST_ALLOW_INSECURE_HTTP:-false}
|
||
- SECURITY_URL_ALLOWLIST_ALLOW_PRIVATE_HOSTS=${SECURITY_URL_ALLOWLIST_ALLOW_PRIVATE_HOSTS:-false}
|
||
- SECURITY_URL_ALLOWLIST_UPSTREAM_HOSTS=${SECURITY_URL_ALLOWLIST_UPSTREAM_HOSTS:-}
|
||
|
||
# --- Update Proxy(国内机器可配置代理访问 GitHub)---
|
||
- UPDATE_PROXY_URL=${UPDATE_PROXY_URL:-}
|
||
|
||
# --- Windsurf (账号管理/登录,不依赖 LS) ---
|
||
- WINDSURF_ENABLED=${WINDSURF_ENABLED:-false}
|
||
- WINDSURF_FIREBASE_API_KEY=${WINDSURF_FIREBASE_API_KEY:-}
|
||
|
||
# --- Windsurf Language Server (可选,启用 windsurf profile 时生效) ---
|
||
- WINDSURF_DOCKER_HOST=${WINDSURF_DOCKER_HOST:-windsurf-ls}
|
||
- WINDSURF_DOCKER_PORT=${WINDSURF_DOCKER_PORT:-42099}
|
||
- WINDSURF_DOCKER_CSRF_TOKEN=${WINDSURF_DOCKER_CSRF_TOKEN:-}
|
||
|
||
depends_on:
|
||
postgres:
|
||
condition: service_healthy
|
||
redis:
|
||
condition: service_healthy
|
||
windsurf-ls:
|
||
condition: service_healthy
|
||
required: false
|
||
networks:
|
||
- sub2api-network
|
||
healthcheck:
|
||
test: [ "CMD", "wget", "-q", "-T", "5", "-O", "/dev/null", "http://localhost:8080/health" ]
|
||
interval: 30s
|
||
timeout: 10s
|
||
retries: 3
|
||
start_period: 30s
|
||
|
||
# ===========================================================================
|
||
# PostgreSQL Database
|
||
# ===========================================================================
|
||
postgres:
|
||
image: postgres:18-alpine
|
||
container_name: sub2api-postgres
|
||
restart: unless-stopped
|
||
ulimits:
|
||
nofile:
|
||
soft: 100000
|
||
hard: 100000
|
||
volumes:
|
||
- postgres_data:/var/lib/postgresql/data
|
||
environment:
|
||
# postgres:18-alpine 默认 PGDATA 在镜像内部匿名卷,必须显式指定才能持久化到命名卷
|
||
# 与旧版 compose 保持一致: PGDATA=/var/lib/postgresql/data,迁移无感
|
||
- PGDATA=/var/lib/postgresql/data
|
||
- POSTGRES_USER=${POSTGRES_USER:-sub2api}
|
||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}
|
||
- POSTGRES_DB=${POSTGRES_DB:-sub2api}
|
||
- TZ=${TZ:-Asia/Shanghai}
|
||
# 默认不对外暴露端口。如需从宿主机调试,取消注释:
|
||
# ports:
|
||
# - "127.0.0.1:5433:5432"
|
||
networks:
|
||
- sub2api-network
|
||
healthcheck:
|
||
test: [ "CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-sub2api} -d ${POSTGRES_DB:-sub2api}" ]
|
||
interval: 10s
|
||
timeout: 5s
|
||
retries: 5
|
||
start_period: 15s
|
||
|
||
# ===========================================================================
|
||
# Redis Cache
|
||
# ===========================================================================
|
||
redis:
|
||
image: redis:8-alpine
|
||
container_name: sub2api-redis
|
||
restart: unless-stopped
|
||
ulimits:
|
||
nofile:
|
||
soft: 100000
|
||
hard: 100000
|
||
volumes:
|
||
- redis_data:/data
|
||
command: >
|
||
sh -c 'if [ -n "$$REDIS_PASSWORD" ]; then
|
||
exec redis-server --requirepass "$$REDIS_PASSWORD" --appendonly yes --maxmemory 512mb --maxmemory-policy allkeys-lru;
|
||
else
|
||
exec redis-server --appendonly yes --maxmemory 512mb --maxmemory-policy allkeys-lru;
|
||
fi'
|
||
environment:
|
||
- REDIS_PASSWORD=${REDIS_PASSWORD:-}
|
||
# 默认不对外暴露端口。如需调试取消注释:
|
||
# ports:
|
||
# - "127.0.0.1:6380:6379"
|
||
networks:
|
||
- sub2api-network
|
||
healthcheck:
|
||
test: [ "CMD-SHELL", "if [ -n \"$$REDIS_PASSWORD\" ]; then redis-cli -a \"$$REDIS_PASSWORD\" ping; else redis-cli ping; fi" ]
|
||
interval: 10s
|
||
timeout: 5s
|
||
retries: 5
|
||
start_period: 10s
|
||
|
||
# ===========================================================================
|
||
# Windsurf Language Server (可选)
|
||
# ---------------------------------------------------------------------------
|
||
# 启用方式: docker compose --profile windsurf up -d
|
||
#
|
||
# 架构说明:
|
||
# - LS 本体只能绑 127.0.0.1:LS_INTERNAL_PORT(CSRF 仅允许 loopback peer)。
|
||
# - 容器内 socat 把 0.0.0.0:42099 → 127.0.0.1:42099,
|
||
# 使 compose 同网络内的服务可直接通过 `windsurf-ls:42099` 访问。
|
||
#
|
||
# 架构限制: 官方 LS 二进制仅提供 linux/amd64 & linux/arm64;非匹配平台通过 QEMU 模拟。
|
||
# 资源建议: CPU 1-2 cores, Memory 512MB - 1GB
|
||
# ===========================================================================
|
||
windsurf-ls:
|
||
image: docker.io/zfc931912343/sub2api-windsurf-ls:latest
|
||
container_name: sub2api-windsurf-ls
|
||
restart: unless-stopped
|
||
profiles: [ "windsurf" ]
|
||
volumes:
|
||
- windsurf_ls_data:/data
|
||
environment:
|
||
# Dockerfile.ls 的 ENTRYPOINT 消费这些键,不要改名
|
||
# LS_PORT : 容器对外暴露端口(socat 监听)
|
||
# LS_INTERNAL_PORT: LS 本体监听端口(socat 转发目的端口);两者必须不同
|
||
- LS_PORT=42099
|
||
- LS_INTERNAL_PORT=42098
|
||
- LS_CSRF_TOKEN=${WINDSURF_DOCKER_CSRF_TOKEN:?WINDSURF_DOCKER_CSRF_TOKEN is required when windsurf profile is enabled}
|
||
- LS_API_SERVER_URL=${LS_API_SERVER_URL:-https://server.self-serve.windsurf.com}
|
||
- HTTPS_PROXY=${LS_HTTPS_PROXY:-}
|
||
- HTTP_PROXY=${LS_HTTP_PROXY:-}
|
||
- TZ=${TZ:-Asia/Shanghai}
|
||
# 默认不对外暴露端口,仅通过内部网络通信
|
||
# 如需从宿主机调试,取消注释:
|
||
# ports:
|
||
# - "127.0.0.1:42099:42099"
|
||
networks:
|
||
- sub2api-network
|
||
healthcheck:
|
||
test: [ "CMD", "nc", "-z", "127.0.0.1", "42099" ]
|
||
interval: 10s
|
||
timeout: 3s
|
||
retries: 5
|
||
start_period: 20s
|
||
|
||
networks:
|
||
sub2api-network:
|
||
name: sub2api-network
|
||
driver: bridge
|
||
|
||
volumes:
|
||
sub2api_data:
|
||
name: sub2api_data
|
||
postgres_data:
|
||
name: sub2api_postgres_data
|
||
redis_data:
|
||
name: sub2api_redis_data
|
||
windsurf_ls_data:
|
||
name: sub2api_windsurf_ls_data
|