同步上游至最新版本并重新应用自定义配置
Some checks failed
CI / test (push) Has been cancelled
CI / golangci-lint (push) Has been cancelled
Security Scan / backend-security (push) Has been cancelled
Security Scan / frontend-security (push) Has been cancelled

上游新增功能:
- 双模式用户消息队列(串行队列 + 软性限速)
- 自定义菜单页面(iframe嵌入 + CSP注入)
- 代理URL集中验证与全局fail-fast
- 新用户默认订阅设置
- 指纹缓存TTL懒续期机制
- 分组用量分布图表
- 代理密码可见性 + 复制代理URL
- 大量bug修复和性能优化

自定义配置保留:
- 品牌化:Sub2API → StarFireAPI
- 链接:GitHub → anthropic.edu.pl 官网
- docker-compose:starfireapi镜像、端口6580、外部Redis、项目名xinghuoapi
- 更新模块禁用:后端始终返回无更新、前端隐藏更新UI

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-03 10:15:24 +08:00
780 changed files with 145032 additions and 40124 deletions

View File

@@ -20,11 +20,61 @@ SERVER_PORT=8080
# Server mode: release or debug
SERVER_MODE=release
# Global max request body size in bytes (default: 100MB)
# 全局最大请求体大小(字节,默认 100MB
# -----------------------------------------------------------------------------
# Logging Configuration
# 日志配置
# -----------------------------------------------------------------------------
# 日志级别debug/info/warn/error
LOG_LEVEL=info
# 日志格式json/console
LOG_FORMAT=json
# 每条日志附带的 service 字段
LOG_SERVICE_NAME=sub2api
# 每条日志附带的 env 字段
LOG_ENV=production
# 是否输出调用方位置信息
LOG_CALLER=true
# 堆栈输出阈值none/error/fatal
LOG_STACKTRACE_LEVEL=error
# 输出开关(建议容器内保持双输出)
# 是否输出到 stdout/stderr
LOG_OUTPUT_TO_STDOUT=true
# 是否输出到文件
LOG_OUTPUT_TO_FILE=true
# 日志文件路径(留空自动推导):
# - 设置 DATA_DIR${DATA_DIR}/logs/sub2api.log
# - 未设置 DATA_DIR/app/data/logs/sub2api.log
LOG_OUTPUT_FILE_PATH=
# 滚动配置
# 单文件最大体积MB
LOG_ROTATION_MAX_SIZE_MB=100
# 保留历史文件数量0 表示不限制)
LOG_ROTATION_MAX_BACKUPS=10
# 历史日志保留天数0 表示不限制)
LOG_ROTATION_MAX_AGE_DAYS=7
# 是否压缩历史日志
LOG_ROTATION_COMPRESS=true
# 滚动文件时间戳是否使用本地时间
LOG_ROTATION_LOCAL_TIME=true
# 采样配置(高频重复日志降噪)
LOG_SAMPLING_ENABLED=false
# 每秒前 N 条日志不采样
LOG_SAMPLING_INITIAL=100
# 之后每 N 条保留 1 条
LOG_SAMPLING_THEREAFTER=100
# Global max request body size in bytes (default: 256MB)
# 全局最大请求体大小(字节,默认 256MB
# Applies to all requests, especially important for h2c first request memory protection
# 适用于所有请求,对 h2c 第一请求的内存保护尤为重要
SERVER_MAX_REQUEST_BODY_SIZE=104857600
SERVER_MAX_REQUEST_BODY_SIZE=268435456
# Gateway max request body size in bytes (default: 256MB)
# 网关请求体最大字节数(默认 256MB
GATEWAY_MAX_BODY_SIZE=268435456
# Enable HTTP/2 Cleartext (h2c) for client connections
# 启用 HTTP/2 Cleartext (h2c) 客户端连接
@@ -58,13 +108,67 @@ TZ=Asia/Shanghai
POSTGRES_USER=sub2api
POSTGRES_PASSWORD=change_this_secure_password
POSTGRES_DB=sub2api
# PostgreSQL 监听端口(同时用于 PG 服务端和应用连接,默认 5432
DATABASE_PORT=5432
# -----------------------------------------------------------------------------
# PostgreSQL 服务端参数(可选;主要用于 deploy/docker-compose-aicodex.yml
# -----------------------------------------------------------------------------
# POSTGRES_MAX_CONNECTIONSPostgreSQL 服务端允许的最大连接数。
# 必须 >=(所有 Sub2API 实例的 DATABASE_MAX_OPEN_CONNS 之和)+ 预留余量(例如 20%)。
POSTGRES_MAX_CONNECTIONS=1024
# POSTGRES_SHARED_BUFFERSPostgreSQL 用于缓存数据页的共享内存。
# 常见建议:物理内存的 10%~25%(容器内存受限时请按实际限制调整)。
# 8GB 内存容器参考1GB。
POSTGRES_SHARED_BUFFERS=1GB
# POSTGRES_EFFECTIVE_CACHE_SIZE查询规划器“假设可用的 OS 缓存大小”(不等于实际分配)。
# 常见建议:物理内存的 50%~75%。
# 8GB 内存容器参考6GB。
POSTGRES_EFFECTIVE_CACHE_SIZE=4GB
# POSTGRES_MAINTENANCE_WORK_MEM维护操作内存VACUUM/CREATE INDEX 等)。
# 值越大维护越快,但会占用更多内存。
# 8GB 内存容器参考128MB。
POSTGRES_MAINTENANCE_WORK_MEM=128MB
# -----------------------------------------------------------------------------
# PostgreSQL 连接池参数(可选,默认与程序内置一致)
# -----------------------------------------------------------------------------
# 说明:
# - 这些参数控制 Sub2API 进程到 PostgreSQL 的连接池大小(不是 PostgreSQL 自身的 max_connections
# - 多实例/多副本部署时,总连接上限约等于:实例数 * DATABASE_MAX_OPEN_CONNS。
# - 连接池过大可能导致:数据库连接耗尽、内存占用上升、上下文切换增多,反而变慢。
# - 建议结合 PostgreSQL 的 max_connections 与机器规格逐步调优:
# 通常把应用总连接上限控制在 max_connections 的 50%~80% 更稳妥。
#
# DATABASE_MAX_OPEN_CONNS最大打开连接数活跃+空闲),达到后新请求会等待可用连接。
# 典型范围50~500取决于 DB 规格、实例数、SQL 复杂度)。
DATABASE_MAX_OPEN_CONNS=256
# DATABASE_MAX_IDLE_CONNS最大空闲连接数热连接建议 <= MAX_OPEN。
# 太小会频繁建连增加延迟;太大会长期占用数据库资源。
DATABASE_MAX_IDLE_CONNS=128
# DATABASE_CONN_MAX_LIFETIME_MINUTES单个连接最大存活时间单位分钟
# 用于避免连接长期不重建导致的中间件/LB/NAT 异常或服务端重启后的“僵尸连接”。
# 设置为 0 表示不限制(一般不建议生产环境)。
DATABASE_CONN_MAX_LIFETIME_MINUTES=30
# DATABASE_CONN_MAX_IDLE_TIME_MINUTES空闲连接最大存活时间单位分钟
# 超过该时间的空闲连接会被回收,防止长时间闲置占用连接数。
# 设置为 0 表示不限制(一般不建议生产环境)。
DATABASE_CONN_MAX_IDLE_TIME_MINUTES=5
# -----------------------------------------------------------------------------
# Redis Configuration
# -----------------------------------------------------------------------------
# Redis 监听端口(同时用于应用连接和 Redis 服务端,默认 6379
REDIS_PORT=6379
# Leave empty for no password (default for local development)
REDIS_PASSWORD=
REDIS_DB=0
# Redis 服务端最大客户端连接数(可选;主要用于 deploy/docker-compose-aicodex.yml
REDIS_MAXCLIENTS=50000
# Redis 连接池大小(默认 1024
REDIS_POOL_SIZE=4096
# Redis 最小空闲连接数(默认 10
REDIS_MIN_IDLE_CONNS=256
REDIS_ENABLE_TLS=false
# -----------------------------------------------------------------------------
@@ -86,6 +190,11 @@ ADMIN_PASSWORD=
# Generate a secure secret: openssl rand -hex 32
JWT_SECRET=
JWT_EXPIRE_HOUR=24
# Access Token 有效期(分钟)
# 优先级说明:
# - >0: 按分钟生效(优先于 JWT_EXPIRE_HOUR
# - =0: 回退使用 JWT_EXPIRE_HOUR
JWT_ACCESS_TOKEN_EXPIRE_MINUTES=0
# -----------------------------------------------------------------------------
# TOTP (2FA) Configuration
@@ -107,6 +216,19 @@ TOTP_ENCRYPTION_KEY=
# Leave unset to use default ./config.yaml
#CONFIG_FILE=./config.yaml
# -----------------------------------------------------------------------------
# Built-in OAuth Client Secrets (Optional)
# -----------------------------------------------------------------------------
# SECURITY NOTE:
# - 本项目不会在代码仓库中内置第三方 OAuth client_secret。
# - 如需使用“内置客户端”(而不是自建 OAuth Client请在运行环境通过 env 注入。
#
# Gemini CLI built-in OAuth client_secret用于 Gemini code_assist/google_one 内置登录流)
# GEMINI_CLI_OAUTH_CLIENT_SECRET=
#
# Antigravity OAuth client_secret用于 Antigravity OAuth 登录流)
# ANTIGRAVITY_OAUTH_CLIENT_SECRET=
# -----------------------------------------------------------------------------
# Rate Limiting (Optional)
# 速率限制(可选)
@@ -119,6 +241,19 @@ RATE_LIMIT_OVERLOAD_COOLDOWN_MINUTES=10
# Gateway Scheduling (Optional)
# 调度缓存与受控回源配置(缓存就绪且命中时不读 DB
# -----------------------------------------------------------------------------
# Force Codex CLI mode: treat all /openai/v1/responses requests as Codex CLI.
# 强制按 Codex CLI 处理 /openai/v1/responses 请求(用于网关未透传/改写 User-Agent 的兜底)。
#
# 注意:开启后会影响所有客户端的行为(不仅限于 VS Code / Codex CLI请谨慎开启。
#
# 默认false
GATEWAY_FORCE_CODEX_CLI=false
# 上游连接池:每主机最大连接数(默认 1024流式/HTTP1.1 场景可调大,如 2400/4096
GATEWAY_MAX_CONNS_PER_HOST=2048
# 上游连接池:最大空闲连接总数(默认 2560账号/代理隔离 + 高并发场景可调大)
GATEWAY_MAX_IDLE_CONNS=8192
# 上游连接池:每主机最大空闲连接(默认 120
GATEWAY_MAX_IDLE_CONNS_PER_HOST=4096
# 粘性会话最大排队长度
GATEWAY_SCHEDULING_STICKY_SESSION_MAX_WAITING=3
# 粘性会话等待超时(时间段,例如 45s

View File

@@ -1,39 +1,6 @@
# =============================================================================
# Sub2API Caddy Reverse Proxy Configuration (宿主机部署)
# =============================================================================
# 使用方法:
# 1. 安装 Caddy: https://caddyserver.com/docs/install
# 2. 修改下方 example.com 为你的域名
# 3. 确保域名 DNS 已指向服务器
# 4. 复制配置: sudo cp Caddyfile /etc/caddy/Caddyfile
# 5. 重载配置: sudo systemctl reload caddy
#
# Caddy 会自动申请和续期 Let's Encrypt SSL 证书
# =============================================================================
# 全局配置
{
# Let's Encrypt 邮箱通知
email admin@example.com
# 服务器配置
servers {
# 启用 HTTP/2 和 HTTP/3
protocols h1 h2 h3
# 超时配置
timeouts {
read_body 30s
read_header 10s
write 300s
idle 300s
}
}
}
# 修改为你的域名
example.com {
# =========================================================================
api.sub2api.com {
# =========================================================================
# 静态资源长期缓存(高优先级,放在最前面)
# 带 hash 的文件可以永久缓存,浏览器和 CDN 都会缓存
# =========================================================================
@@ -93,10 +60,7 @@ example.com {
write_buffer 16KB
compression off
}
# SSE/流式传输优化:禁用响应缓冲,立即刷新数据给客户端
flush_interval -1
# 故障转移
fail_duration 30s
max_fails 3
@@ -111,10 +75,6 @@ example.com {
gzip 6
minimum_length 256
match {
# SSE 请求通常会带 Accept: text/event-stream需排除压缩
not header Accept text/event-stream*
# 排除已知 SSE 路径(即便 Accept 缺失)
not path /v1/messages /v1/responses /responses /antigravity/v1/messages /v1beta/models/* /antigravity/v1beta/models/*
header Content-Type text/*
header Content-Type application/json*
header Content-Type application/javascript*
@@ -124,53 +84,6 @@ example.com {
}
}
# =========================================================================
# 速率限制 (需要 caddy-ratelimit 插件)
# 如未安装插件,请注释掉此段
# =========================================================================
# rate_limit {
# zone api {
# key {remote_host}
# events 100
# window 1m
# }
# }
# =========================================================================
# 安全响应头
# =========================================================================
header {
# 防止点击劫持
X-Frame-Options "SAMEORIGIN"
# XSS 保护
X-XSS-Protection "1; mode=block"
# 防止 MIME 类型嗅探
X-Content-Type-Options "nosniff"
# 引用策略
Referrer-Policy "strict-origin-when-cross-origin"
# HSTS - 强制 HTTPS (max-age=1年)
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
# 内容安全策略 (根据需要调整)
# Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https:;"
# 权限策略
Permissions-Policy "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()"
# 跨域资源策略
Cross-Origin-Opener-Policy "same-origin"
Cross-Origin-Embedder-Policy "require-corp"
Cross-Origin-Resource-Policy "same-origin"
# 移除敏感头
-Server
-X-Powered-By
}
# =========================================================================
# 请求大小限制 (防止大文件攻击)
# =========================================================================
@@ -198,7 +111,3 @@ example.com {
respond "{err.status_code} {err.status_text}"
}
}
# =============================================================================
# HTTP 重定向到 HTTPS (Caddy 默认自动处理,此处显式声明)
# =============================================================================

View File

@@ -0,0 +1,78 @@
# datamanagementd 部署说明(数据管理)
本文说明如何在宿主机部署 `datamanagementd`,并与主进程联动开启“数据管理”功能。
## 1. 关键约束
- 主进程固定探测路径:`/tmp/sub2api-datamanagement.sock`
- 仅当该 Unix Socket 可连通且 `Health` 成功时,后台“数据管理”才会启用
- `datamanagementd` 使用 SQLite 持久化元数据,不依赖主库
## 2. 宿主机构建与运行
```bash
cd /opt/sub2api-src/datamanagement
go build -o /opt/sub2api/datamanagementd ./cmd/datamanagementd
mkdir -p /var/lib/sub2api/datamanagement
chown -R sub2api:sub2api /var/lib/sub2api/datamanagement
```
手动启动示例:
```bash
/opt/sub2api/datamanagementd \
-socket-path /tmp/sub2api-datamanagement.sock \
-sqlite-path /var/lib/sub2api/datamanagement/datamanagementd.db \
-version 1.0.0
```
## 3. systemd 托管(推荐)
仓库已提供示例服务文件:`deploy/sub2api-datamanagementd.service`
```bash
sudo cp deploy/sub2api-datamanagementd.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now sub2api-datamanagementd
sudo systemctl status sub2api-datamanagementd
```
查看日志:
```bash
sudo journalctl -u sub2api-datamanagementd -f
```
也可以使用一键安装脚本(自动安装二进制 + 注册 systemd
```bash
# 方式一:使用现成二进制
sudo ./deploy/install-datamanagementd.sh --binary /path/to/datamanagementd
# 方式二:从源码构建后安装
sudo ./deploy/install-datamanagementd.sh --source /path/to/sub2api
```
## 4. Docker 部署联动
`sub2api` 运行在 Docker 容器中,需要将宿主机 Socket 挂载到容器同路径:
```yaml
services:
sub2api:
volumes:
- /tmp/sub2api-datamanagement.sock:/tmp/sub2api-datamanagement.sock
```
建议在 `docker-compose.override.yml` 中维护该挂载,避免覆盖主 compose 文件。
## 5. 依赖检查
`datamanagementd` 执行备份时依赖以下工具:
- `pg_dump`
- `redis-cli`
- `docker`(仅 `source_mode=docker_exec` 时)
缺失依赖会导致对应任务失败,并在任务详情中体现错误信息。

111
deploy/Dockerfile Normal file
View File

@@ -0,0 +1,111 @@
# =============================================================================
# Sub2API Multi-Stage Dockerfile
# =============================================================================
# Stage 1: Build frontend
# Stage 2: Build Go backend with embedded frontend
# Stage 3: Final minimal image
# =============================================================================
ARG NODE_IMAGE=node:24-alpine
ARG GOLANG_IMAGE=golang:1.25.5-alpine
ARG ALPINE_IMAGE=alpine:3.20
ARG GOPROXY=https://goproxy.cn,direct
ARG GOSUMDB=sum.golang.google.cn
# -----------------------------------------------------------------------------
# Stage 1: Frontend Builder
# -----------------------------------------------------------------------------
FROM ${NODE_IMAGE} AS frontend-builder
WORKDIR /app/frontend
# Install pnpm
RUN corepack enable && corepack prepare pnpm@latest --activate
# Install dependencies first (better caching)
COPY frontend/package.json frontend/pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
# Copy frontend source and build
COPY frontend/ ./
RUN pnpm run build
# -----------------------------------------------------------------------------
# Stage 2: Backend Builder
# -----------------------------------------------------------------------------
FROM ${GOLANG_IMAGE} AS backend-builder
# Build arguments for version info (set by CI)
ARG VERSION=docker
ARG COMMIT=docker
ARG DATE
ARG GOPROXY
ARG GOSUMDB
ENV GOPROXY=${GOPROXY}
ENV GOSUMDB=${GOSUMDB}
# Install build dependencies
RUN apk add --no-cache git ca-certificates tzdata
WORKDIR /app/backend
# Copy go mod files first (better caching)
COPY backend/go.mod backend/go.sum ./
RUN go mod download
# Copy backend source first
COPY backend/ ./
# Copy frontend dist from previous stage (must be after backend copy to avoid being overwritten)
COPY --from=frontend-builder /app/backend/internal/web/dist ./internal/web/dist
# Build the binary (BuildType=release for CI builds, embed frontend)
RUN CGO_ENABLED=0 GOOS=linux go build \
-tags embed \
-ldflags="-s -w -X main.Commit=${COMMIT} -X main.Date=${DATE:-$(date -u +%Y-%m-%dT%H:%M:%SZ)} -X main.BuildType=release" \
-o /app/sub2api \
./cmd/server
# -----------------------------------------------------------------------------
# Stage 3: Final Runtime Image
# -----------------------------------------------------------------------------
FROM ${ALPINE_IMAGE}
# Labels
LABEL maintainer="Wei-Shaw <github.com/Wei-Shaw>"
LABEL description="Sub2API - AI API Gateway Platform"
LABEL org.opencontainers.image.source="https://github.com/Wei-Shaw/sub2api"
# Install runtime dependencies
RUN apk add --no-cache \
ca-certificates \
tzdata \
curl \
&& rm -rf /var/cache/apk/*
# Create non-root user
RUN addgroup -g 1000 sub2api && \
adduser -u 1000 -G sub2api -s /bin/sh -D sub2api
# Set working directory
WORKDIR /app
# Copy binary from builder
COPY --from=backend-builder /app/sub2api /app/sub2api
# Create data directory
RUN mkdir -p /app/data && chown -R sub2api:sub2api /app
# Switch to non-root user
USER sub2api
# Expose port (can be overridden by SERVER_PORT env var)
EXPOSE 8080
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \
CMD curl -f http://localhost:${SERVER_PORT:-8080}/health || exit 1
# Run the application
ENTRYPOINT ["/app/sub2api"]

View File

@@ -19,7 +19,10 @@ This directory contains files for deploying Sub2API on Linux servers.
| `.env.example` | Docker environment variables template |
| `DOCKER.md` | Docker Hub documentation |
| `install.sh` | One-click binary installation script |
| `install-datamanagementd.sh` | datamanagementd 一键安装脚本 |
| `sub2api.service` | Systemd service unit file |
| `sub2api-datamanagementd.service` | datamanagementd systemd service unit file |
| `DATAMANAGEMENTD_CN.md` | datamanagementd 部署与联动说明(中文) |
| `config.example.yaml` | Example configuration file |
---
@@ -145,6 +148,14 @@ SELECT
(SELECT COUNT(*) FROM user_allowed_groups) AS new_pair_count;
```
### datamanagementd数据管理联动
如需启用管理后台“数据管理”功能,请额外部署宿主机 `datamanagementd`
- 主进程固定探测 `/tmp/sub2api-datamanagement.sock`
- Docker 场景下需把宿主机 Socket 挂载到容器内同路径
- 详细步骤见:`deploy/DATAMANAGEMENTD_CN.md`
### Commands
For **local directory version** (docker-compose.local.yml):
@@ -303,6 +314,10 @@ Requires your own OAuth client credentials.
```bash
GEMINI_OAUTH_CLIENT_ID=your-client-id.apps.googleusercontent.com
GEMINI_OAUTH_CLIENT_SECRET=GOCSPX-your-client-secret
# 可选:如需使用 Gemini CLI 内置 OAuth ClientCode Assist / Google One
# 安全说明:本仓库不会内置该 client_secret请在运行环境通过环境变量注入。
# GEMINI_CLI_OAUTH_CLIENT_SECRET=GOCSPX-your-built-in-secret
```
**Step 3: Create Account in Admin UI**
@@ -430,6 +445,11 @@ If you need to use AI Studio OAuth for Gemini accounts, add the OAuth client cre
Environment=GEMINI_OAUTH_CLIENT_SECRET=GOCSPX-your-client-secret
```
如需使用“内置 Gemini CLI OAuth Client”Code Assist / Google One还需要注入
```ini
Environment=GEMINI_CLI_OAUTH_CLIENT_SECRET=GOCSPX-your-built-in-secret
```
3. Reload and restart:
```bash
sudo systemctl daemon-reload
@@ -566,7 +586,7 @@ gateway:
name: "Profile 2"
cipher_suites: [4866, 4867, 4865, 49199, 49195, 49200, 49196]
curves: [29, 23, 24]
point_formats: [0]
point_formats: 0
# Another custom profile
profile_3:

View File

@@ -1,8 +1,13 @@
#!/bin/bash
#!/usr/bin/env bash
# 本地构建镜像的快速脚本,避免在命令行反复输入构建参数。
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
docker build -t sub2api:latest \
--build-arg GOPROXY=https://goproxy.cn,direct \
--build-arg GOSUMDB=sum.golang.google.cn \
-f Dockerfile \
.
-f "${REPO_ROOT}/Dockerfile" \
"${REPO_ROOT}"

View File

@@ -20,14 +20,18 @@ server:
# Mode: "debug" for development, "release" for production
# 运行模式:"debug" 用于开发,"release" 用于生产环境
mode: "release"
# Frontend base URL used to generate external links in emails (e.g. password reset)
# 用于生成邮件中的外部链接(例如:重置密码链接)的前端基础地址
# Example: "https://example.com"
frontend_url: ""
# Trusted proxies for X-Forwarded-For parsing (CIDR/IP). Empty disables trusted proxies.
# 信任的代理地址CIDR/IP 格式),用于解析 X-Forwarded-For 头。留空则禁用代理信任。
trusted_proxies: []
# Global max request body size in bytes (default: 100MB)
# 全局最大请求体大小(字节,默认 100MB
# Global max request body size in bytes (default: 256MB)
# 全局最大请求体大小(字节,默认 256MB
# Applies to all requests, especially important for h2c first request memory protection
# 适用于所有请求,对 h2c 第一请求的内存保护尤为重要
max_request_body_size: 104857600
max_request_body_size: 268435456
# HTTP/2 Cleartext (h2c) configuration
# HTTP/2 Cleartext (h2c) 配置
h2c:
@@ -108,9 +112,9 @@ security:
# 白名单禁用时是否允许 http:// URL默认: false要求 https
allow_insecure_http: true
response_headers:
# Enable configurable response header filtering (disable to use default allowlist)
# 启用可配置的响应头过滤(禁用则使用默认白名单
enabled: false
# Enable configurable response header filtering (default: true)
# 启用可配置的响应头过滤(默认启用,过滤上游敏感响应头
enabled: true
# Extra allowed response headers from upstream
# 额外允许的上游响应头
additional_allowed: []
@@ -130,6 +134,12 @@ security:
# Allow skipping TLS verification for proxy probe (debug only)
# 允许代理探测时跳过 TLS 证书验证(仅用于调试)
insecure_skip_verify: false
proxy_fallback:
# Allow auxiliary services (update check, pricing data) to fallback to direct
# connection when proxy initialization fails. Does NOT affect AI gateway connections.
# 辅助服务(更新检查、定价数据拉取)代理初始化失败时是否允许回退直连。
# 不影响 AI 账号网关连接。默认 falsefail-fast 防止 IP 泄露。
allow_direct_on_error: false
# =============================================================================
# Gateway Configuration
@@ -139,9 +149,45 @@ gateway:
# Timeout for waiting upstream response headers (seconds)
# 等待上游响应头超时时间(秒)
response_header_timeout: 600
# Max request body size in bytes (default: 100MB)
# 请求体最大字节数(默认 100MB
max_body_size: 104857600
# Max request body size in bytes (default: 256MB)
# 请求体最大字节数(默认 256MB
max_body_size: 268435456
# Max bytes to read for non-stream upstream responses (default: 8MB)
# 非流式上游响应体读取上限(默认 8MB
upstream_response_read_max_bytes: 8388608
# Max bytes to read for proxy probe responses (default: 1MB)
# 代理探测响应体读取上限(默认 1MB
proxy_probe_response_read_max_bytes: 1048576
# Enable Gemini upstream response header debug logs (default: false)
# 是否开启 Gemini 上游响应头调试日志(默认 false
gemini_debug_response_headers: false
# Sora max request body size in bytes (0=use max_body_size)
# Sora 请求体最大字节数0=使用 max_body_size
sora_max_body_size: 268435456
# Sora stream timeout (seconds, 0=disable)
# Sora 流式请求总超时0=禁用)
sora_stream_timeout_seconds: 900
# Sora non-stream timeout (seconds, 0=disable)
# Sora 非流式请求超时0=禁用)
sora_request_timeout_seconds: 180
# Sora stream enforcement mode: force/error
# Sora stream 强制策略force/error
sora_stream_mode: "force"
# Sora model filters
# Sora 模型过滤配置
sora_model_filters:
# Hide prompt-enhance models by default
# 默认隐藏 prompt-enhance 模型
hide_prompt_enhance: true
# Require API key for /sora/media proxy (default: false)
# /sora/media 是否强制要求 API Key默认 true
sora_media_require_api_key: true
# Sora media temporary signing key (empty disables signed URL)
# Sora 媒体临时签名密钥(为空则禁用签名)
sora_media_signing_key: ""
# Signed URL TTL seconds (<=0 disables)
# 临时签名 URL 有效期(秒,<=0 表示禁用)
sora_media_signed_url_ttl_seconds: 900
# Connection pool isolation strategy:
# 连接池隔离策略:
# - proxy: Isolate by proxy, same proxy shares connection pool (suitable for few proxies, many accounts)
@@ -151,17 +197,102 @@ gateway:
# - account_proxy: Isolate by account+proxy combination (default, finest granularity)
# - account_proxy: 按账户+代理组合隔离(默认,最细粒度)
connection_pool_isolation: "account_proxy"
# Force Codex CLI mode: treat all /openai/v1/responses requests as Codex CLI.
# 强制按 Codex CLI 处理 /openai/v1/responses 请求(用于网关未透传/改写 User-Agent 的兜底)。
#
# 注意:开启后会影响所有客户端的行为(不仅限于 VS Code / Codex CLI请谨慎开启。
force_codex_cli: false
# OpenAI 透传模式是否放行客户端超时头(如 x-stainless-timeout
# 默认 false过滤超时头降低上游提前断流风险。
openai_passthrough_allow_timeout_headers: false
# OpenAI Responses WebSocket 配置(默认开启,可按需回滚到 HTTP
openai_ws:
# 新版 WS mode 路由(默认关闭)。关闭时保持当前 legacy 实现行为。
mode_router_v2_enabled: false
# ingress 默认模式off|shared|dedicated仅 mode_router_v2_enabled=true 生效)
ingress_mode_default: shared
# 全局总开关,默认 true关闭时所有请求保持原有 HTTP/SSE 路由
enabled: true
# 按账号类型细分开关
oauth_enabled: true
apikey_enabled: true
# 全局强制 HTTP紧急回滚开关
force_http: false
# 允许在 WSv2 下按策略恢复 store=true默认 false
allow_store_recovery: false
# ingress 模式收到 previous_response_not_found 时,自动去掉 previous_response_id 重试一次(默认 true
ingress_previous_response_recovery_enabled: true
# store=false 且无可复用会话连接时的策略:
# strict=强制新建连接隔离优先adaptive=仅在高风险失败后强制新建off=尽量复用(性能优先)
store_disabled_conn_mode: strict
# store=false 且无可复用会话连接时,是否强制新建连接(默认 true优先会话隔离
# 兼容旧配置:仅在 store_disabled_conn_mode 未配置时生效
store_disabled_force_new_conn: true
# 是否启用 WSv2 generate=false 预热(默认 false
prewarm_generate_enabled: false
# 协议 feature 开关v2 优先于 v1
responses_websockets: false
responses_websockets_v2: true
# 连接池参数(按账号池化复用)
max_conns_per_account: 128
min_idle_per_account: 4
max_idle_per_account: 12
# 是否按账号并发动态计算连接池上限:
# effective_max_conns = min(max_conns_per_account, ceil(account.concurrency * factor))
dynamic_max_conns_by_account_concurrency_enabled: true
# 按账号类型分别设置系数OAuth / API Key
oauth_max_conns_factor: 1.0
apikey_max_conns_factor: 1.0
dial_timeout_seconds: 10
read_timeout_seconds: 900
write_timeout_seconds: 120
pool_target_utilization: 0.7
queue_limit_per_conn: 64
# 流式写出批量 flush 参数
event_flush_batch_size: 1
event_flush_interval_ms: 10
# 预热触发冷却(毫秒)
prewarm_cooldown_ms: 300
# WS 回退到 HTTP 后的冷却时间(秒),用于避免 WS/HTTP 来回抖动0 表示关闭冷却
fallback_cooldown_seconds: 30
# WS 重试退避参数(毫秒)
retry_backoff_initial_ms: 120
retry_backoff_max_ms: 2000
# 抖动比例0-1
retry_jitter_ratio: 0.2
# 单次请求 WS 重试总预算(毫秒);建议设置为有限值,避免重试拉高 TTFT 长尾
retry_total_budget_ms: 5000
# payload_schema 日志采样率0-1降低热路径日志放大
payload_log_sample_rate: 0.2
# 调度与粘连参数
lb_top_k: 7
sticky_session_ttl_seconds: 3600
# 会话哈希迁移兼容开关:新 key 未命中时回退读取旧 SHA-256 key
session_hash_read_old_fallback: true
# 会话哈希迁移兼容开关:写入时双写旧 SHA-256 key短 TTL
session_hash_dual_write_old: true
# context 元数据迁移兼容开关:保留旧 ctxkey.* 读取/注入桥接
metadata_bridge_enabled: true
sticky_response_id_ttl_seconds: 3600
# 兼容旧键:当 sticky_response_id_ttl_seconds 缺失时回退该值
sticky_previous_response_ttl_seconds: 3600
scheduler_score_weights:
priority: 1.0
load: 1.0
queue: 0.7
error_rate: 0.8
ttft: 0.5
# HTTP upstream connection pool settings (HTTP/2 + multi-proxy scenario defaults)
# HTTP 上游连接池配置HTTP/2 + 多代理场景默认值)
# Max idle connections across all hosts
# 所有主机的最大空闲连接数
max_idle_conns: 240
max_idle_conns: 2560
# Max idle connections per host
# 每个主机的最大空闲连接数
max_idle_conns_per_host: 120
# Max connections per host
# 每个主机的最大连接数
max_conns_per_host: 240
max_conns_per_host: 1024
# Idle connection timeout (seconds)
# 空闲连接超时时间(秒)
idle_conn_timeout_seconds: 90
@@ -246,9 +377,177 @@ gateway:
# name: "Custom Profile 1"
# profile_2:
# name: "Custom Profile 2"
# cipher_suites: [4866, 4867, 4865, 49199, 49195, 49200, 49196]
# curves: [29, 23, 24]
# point_formats: [0]
# =============================================================================
# Logging Configuration
# 日志配置
# =============================================================================
log:
# Log level: debug/info/warn/error
# 日志级别debug/info/warn/error
level: "info"
# Log format: json/console
# 日志格式json/console
format: "console"
# Service name field written into each log line
# 每条日志都会附带 service 字段
service_name: "sub2api"
# Environment field written into each log line
# 每条日志都会附带 env 字段
env: "production"
# Include caller information
# 是否输出调用方位置信息
caller: true
# Stacktrace threshold: none/error/fatal
# 堆栈输出阈值none/error/fatal
stacktrace_level: "error"
output:
# Keep stdout/stderr output for container log collection
# 保持标准输出用于容器日志采集
to_stdout: true
# Enable file output (default path auto-derived)
# 启用文件输出(默认路径自动推导)
to_file: true
# Empty means:
# - DATA_DIR set: {{DATA_DIR}}/logs/sub2api.log
# - otherwise: /app/data/logs/sub2api.log
# 留空时:
# - 设置 DATA_DIR{{DATA_DIR}}/logs/sub2api.log
# - 否则:/app/data/logs/sub2api.log
file_path: ""
rotation:
# Max file size before rotation (MB)
# 单文件滚动阈值MB
max_size_mb: 100
# Number of rotated files to keep (0 means unlimited)
# 保留历史文件数量0 表示不限制)
max_backups: 10
# Number of days to keep old log files (0 means unlimited)
# 历史日志保留天数0 表示不限制)
max_age_days: 7
# Compress rotated files
# 是否压缩历史日志
compress: true
# Use local time for timestamp in rotated filename
# 滚动文件名时间戳使用本地时区
local_time: true
sampling:
# Enable zap sampler (reduce high-frequency repetitive logs)
# 启用 zap 采样(减少高频重复日志)
enabled: false
# Number of first entries per second to always log
# 每秒无采样保留的前 N 条日志
initial: 100
# Thereafter keep 1 out of N entries per second
# 之后每 N 条保留 1 条
thereafter: 100
# =============================================================================
# Sora Direct Client Configuration
# Sora 直连配置
# =============================================================================
sora:
client:
# Sora backend base URL
# Sora 上游 Base URL
base_url: "https://sora.chatgpt.com/backend"
# Request timeout (seconds)
# 请求超时(秒)
timeout_seconds: 120
# Max retries for upstream requests
# 上游请求最大重试次数
max_retries: 3
# Account+proxy cooldown window after Cloudflare challenge (seconds, 0 to disable)
# Cloudflare challenge 后按账号+代理冷却窗口0 表示关闭)
cloudflare_challenge_cooldown_seconds: 900
# Poll interval (seconds)
# 轮询间隔(秒)
poll_interval_seconds: 2
# Max poll attempts
# 最大轮询次数
max_poll_attempts: 600
# Recent task query limit (image)
# 最近任务查询数量(图片轮询)
recent_task_limit: 50
# Recent task query max limit (fallback)
# 最近任务查询最大数量(回退)
recent_task_limit_max: 200
# Enable debug logs for Sora upstream requests
# 启用 Sora 直连调试日志
# 调试日志会输出上游请求尝试、重试、响应摘要Authorization/openai-sentinel-token 等敏感头会自动脱敏
debug: false
# Allow Sora client to fetch token via OpenAI token provider
# 是否允许 Sora 客户端通过 OpenAI token provider 取 token默认 false避免误走 OpenAI 刷新链路)
use_openai_token_provider: false
# Optional custom headers (key-value)
# 额外请求头(键值对)
headers: {}
# Default User-Agent for Sora requests
# Sora 默认 User-Agent
user_agent: "Sora/1.2026.007 (Android 15; 24122RKC7C; build 2600700)"
# Disable TLS fingerprint for Sora upstream
# 关闭 Sora 上游 TLS 指纹伪装
disable_tls_fingerprint: false
# curl_cffi sidecar for Sora only (required)
# 仅 Sora 链路使用的 curl_cffi sidecar必需
curl_cffi_sidecar:
# Sora 强制通过 sidecar 请求,必须启用
# Sora is forced to use sidecar only; keep enabled=true
enabled: true
# Sidecar base URL (default endpoint: /request)
# sidecar 基础地址(默认请求端点:/request
base_url: "http://sora-curl-cffi-sidecar:8080"
# curl_cffi impersonate profile, e.g. chrome131/chrome124/safari18_0
# curl_cffi 指纹伪装 profile例如 chrome131/chrome124/safari18_0
impersonate: "chrome131"
# Sidecar request timeout (seconds)
# sidecar 请求超时(秒)
timeout_seconds: 60
# Reuse session key per account+proxy to let sidecar persist cookies/session
# 按账号+代理复用 session key让 sidecar 持久化 cookies/session
session_reuse_enabled: true
# Session TTL in sidecar (seconds)
# sidecar 会话 TTL
session_ttl_seconds: 3600
storage:
# Storage type (local only for now)
# 存储类型(首发仅支持 local
type: "local"
# Local base path; empty uses /app/data/sora
# 本地存储基础路径;为空使用 /app/data/sora
local_path: ""
# Fallback to upstream URL when download fails
# 下载失败时回退到上游 URL
fallback_to_upstream: true
# Max concurrent downloads
# 并发下载上限
max_concurrent_downloads: 4
# Download timeout (seconds)
# 下载超时(秒)
download_timeout_seconds: 120
# Max download bytes
# 最大下载字节数
max_download_bytes: 209715200
# Enable debug logs for media storage
# 启用媒体存储调试日志
debug: false
cleanup:
# Enable cleanup task
# 启用清理任务
enabled: true
# Retention days
# 保留天数
retention_days: 7
# Cron schedule
# Cron 调度表达式
schedule: "0 3 * * *"
# Token refresh behavior
# token 刷新行为控制
token_refresh:
# Whether OpenAI refresh flow is allowed to sync linked Sora accounts
# 是否允许 OpenAI 刷新流程同步覆盖 linked_openai_account_id 关联的 Sora 账号 token
sync_linked_sora_accounts: false
# =============================================================================
# API Key Auth Cache Configuration
@@ -352,6 +651,30 @@ usage_cleanup:
# 单次任务最大执行时长(秒)
task_timeout_seconds: 1800
# =============================================================================
# HTTP 写接口幂等配置
# Idempotency Configuration
# =============================================================================
idempotency:
# Observe-only 模式:
# true: 观察期,不带 Idempotency-Key 仍放行(但会记录)
# false: 强制期,不带 Idempotency-Key 直接拒绝(仅对接入幂等保护的接口生效)
observe_only: true
# 关键写接口幂等记录 TTL
default_ttl_seconds: 86400
# 系统操作接口update/rollback/restart幂等记录 TTL
system_operation_ttl_seconds: 3600
# processing 锁超时(秒)
processing_timeout_seconds: 30
# 可重试失败退避窗口(秒)
failed_retry_backoff_seconds: 5
# 持久化响应体最大长度(字节)
max_stored_response_len: 65536
# 过期幂等记录清理周期(秒)
cleanup_interval_seconds: 60
# 每轮清理最大删除条数
cleanup_batch_size: 500
# =============================================================================
# Concurrency Wait Configuration
# 并发等待配置
@@ -381,9 +704,22 @@ database:
# Database name
# 数据库名称
dbname: "sub2api"
# SSL mode: disable, require, verify-ca, verify-full
# SSL 模式disable禁用, require要求, verify-ca验证CA, verify-full完全验证
sslmode: "disable"
# SSL mode: disable, prefer, require, verify-ca, verify-full
# SSL 模式disable禁用, prefer优先加密默认, require要求, verify-ca验证CA, verify-full完全验证
# 默认值为 "prefer",数据库支持 SSL 时自动使用加密连接,不支持时回退明文
sslmode: "prefer"
# Max open connections (高并发场景建议 256+,需配合 PostgreSQL max_connections 调整)
# 最大打开连接数
max_open_conns: 256
# Max idle connections (建议为 max_open_conns 的 50%,减少频繁建连开销)
# 最大空闲连接数
max_idle_conns: 128
# Connection max lifetime (minutes)
# 连接最大存活时间(分钟)
conn_max_lifetime_minutes: 30
# Connection max idle time (minutes)
# 空闲连接最大存活时间(分钟)
conn_max_idle_time_minutes: 5
# =============================================================================
# Redis Configuration
@@ -402,6 +738,12 @@ redis:
# Database number (0-15)
# 数据库编号0-15
db: 0
# Connection pool size (max concurrent connections)
# 连接池大小(最大并发连接数)
pool_size: 1024
# Minimum number of idle connections (高并发场景建议 128+,保持足够热连接)
# 最小空闲连接数
min_idle_conns: 128
# Enable TLS/SSL connection
# 是否启用 TLS/SSL 连接
enable_tls: false
@@ -428,9 +770,14 @@ jwt:
# 重要:生产环境中请更改为随机字符串!
# Generate with / 生成命令: openssl rand -hex 32
secret: "change-this-to-a-secure-random-string"
# Token expiration time in hours (max 24)
# 令牌过期时间(小时,最大 24
# Token expiration time in hours (max 168)
# 令牌过期时间(小时,最大 168
expire_hour: 24
# Access Token 过期时间(分钟)
# 优先级说明:
# - >0: 按分钟生效(优先于 expire_hour
# - =0: 回退使用 expire_hour
access_token_expire_minutes: 0
# =============================================================================
# TOTP (2FA) Configuration
@@ -515,12 +862,12 @@ rate_limit:
# 定价数据源(可选)
# =============================================================================
pricing:
# URL to fetch model pricing data (default: LiteLLM)
# 获取模型定价数据的 URL默认LiteLLM
remote_url: "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json"
# URL to fetch model pricing data (default: pinned model-price-repo commit)
# 获取模型定价数据的 URL默认固定 commit 的 model-price-repo
remote_url: "https://raw.githubusercontent.com/Wei-Shaw/model-price-repo/c7947e9871687e664180bc971d4837f1fc2784a9/model_prices_and_context_window.json"
# Hash verification URL (optional)
# 哈希校验 URL可选
hash_url: ""
hash_url: "https://raw.githubusercontent.com/Wei-Shaw/model-price-repo/c7947e9871687e664180bc971d4837f1fc2784a9/model_prices_and_context_window.sha256"
# Local data directory for caching
# 本地数据缓存目录
data_dir: "./data"
@@ -583,10 +930,14 @@ turnstile:
# 默认:使用 Gemini CLI 的公开 OAuth 凭证(与 Google 官方 CLI 工具相同)
gemini:
oauth:
# Gemini CLI public OAuth credentials (works for both Code Assist and AI Studio)
# Gemini CLI 公开 OAuth 凭证(适用于 Code Assist 和 AI Studio
client_id: "681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com"
client_secret: "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"
# OAuth 客户端配置说明:
# 1) 留空 client_id/client_secret使用 Gemini CLI 内置 OAuth Client其 client_secret 需通过环境变量注入
# - GEMINI_CLI_OAUTH_CLIENT_SECRET
# 2) 同时设置 client_id/client_secret使用你自建的 OAuth Client推荐权限更完整
#
# 注意client_id 与 client_secret 必须同时为空或同时非空。
client_id: ""
client_secret: ""
# Optional scopes (space-separated). Leave empty to auto-select based on oauth_type.
# 可选的权限范围(空格分隔)。留空则根据 oauth_type 自动选择。
scopes: ""

View File

@@ -33,7 +33,7 @@ services:
# Data persistence (config.yaml will be auto-generated here)
- sub2api_data:/app/data
# Mount custom config.yaml (optional, overrides auto-generated config)
- ./config.yaml:/app/data/config.yaml:ro
# - ./config.yaml:/app/data/config.yaml:ro
environment:
# =======================================================================
# Auto Setup (REQUIRED for Docker deployment)
@@ -57,6 +57,10 @@ services:
- 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 Configuration
@@ -65,6 +69,8 @@ services:
- 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}
# =======================================================================
# Admin Account (auto-created on first run)
@@ -98,6 +104,11 @@ services:
- GEMINI_OAUTH_SCOPES=${GEMINI_OAUTH_SCOPES:-}
- GEMINI_QUOTA_POLICY=${GEMINI_QUOTA_POLICY:-}
# Built-in OAuth client secrets (optional)
# SECURITY: This repo does not embed third-party client_secret.
- GEMINI_CLI_OAUTH_CLIENT_SECRET=${GEMINI_CLI_OAUTH_CLIENT_SECRET:-}
- ANTIGRAVITY_OAUTH_CLIENT_SECRET=${ANTIGRAVITY_OAUTH_CLIENT_SECRET:-}
# =======================================================================
# Security Configuration (URL Allowlist)
# =======================================================================
@@ -131,6 +142,10 @@ services:
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
# postgres:18-alpine 默认 PGDATA=/var/lib/postgresql/18/docker位于镜像声明的匿名卷 /var/lib/postgresql 内)。
# 若不显式设置 PGDATA则即使挂载了 postgres_data 到 /var/lib/postgresql/data数据也不会落盘到该命名卷
# docker compose down/up 后会触发 initdb 重新初始化,导致用户/密码等数据丢失。
- PGDATA=/var/lib/postgresql/data
- POSTGRES_USER=${POSTGRES_USER:-sub2api}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}
- POSTGRES_DB=${POSTGRES_DB:-sub2api}
@@ -150,7 +165,7 @@ services:
# Redis Cache
# ===========================================================================
redis:
image: redis:7-alpine
image: redis:8-alpine
container_name: sub2api-redis
restart: unless-stopped
ulimits:

View File

@@ -62,6 +62,10 @@ services:
- 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 Configuration
@@ -70,6 +74,8 @@ services:
- 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}
# =======================================================================
@@ -117,6 +123,11 @@ services:
- GEMINI_OAUTH_SCOPES=${GEMINI_OAUTH_SCOPES:-}
- GEMINI_QUOTA_POLICY=${GEMINI_QUOTA_POLICY:-}
# Built-in OAuth client secrets (optional)
# SECURITY: This repo does not embed third-party client_secret.
- GEMINI_CLI_OAUTH_CLIENT_SECRET=${GEMINI_CLI_OAUTH_CLIENT_SECRET:-}
- ANTIGRAVITY_OAUTH_CLIENT_SECRET=${ANTIGRAVITY_OAUTH_CLIENT_SECRET:-}
# =======================================================================
# Security Configuration (URL Allowlist)
# =======================================================================

View File

@@ -127,6 +127,19 @@ services:
# - ./logs:/app/logs
# - ./backups:/app/backups
# =============================================================================
# Scenario 6: 启用宿主机 datamanagementd数据管理
# =============================================================================
# 说明:
# - datamanagementd 运行在宿主机systemd 或手动)
# - 主进程固定探测 /tmp/sub2api-datamanagement.sock
# - 需要把宿主机 socket 挂载到容器内同路径
#
# services:
# sub2api:
# volumes:
# - /tmp/sub2api-datamanagement.sock:/tmp/sub2api-datamanagement.sock
# =============================================================================
# Additional Notes
# =============================================================================

View File

@@ -48,6 +48,10 @@ services:
- DATABASE_PASSWORD=${DATABASE_PASSWORD:?DATABASE_PASSWORD is required}
- DATABASE_DBNAME=${DATABASE_DBNAME:-sub2api}
- DATABASE_SSLMODE=${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 Configuration - Required
@@ -56,6 +60,8 @@ services:
- REDIS_PORT=${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}
# =======================================================================
@@ -82,6 +88,11 @@ services:
- GEMINI_OAUTH_CLIENT_SECRET=${GEMINI_OAUTH_CLIENT_SECRET:-}
- GEMINI_OAUTH_SCOPES=${GEMINI_OAUTH_SCOPES:-}
- GEMINI_QUOTA_POLICY=${GEMINI_QUOTA_POLICY:-}
# Built-in OAuth client secrets (optional)
# SECURITY: This repo does not embed third-party client_secret.
- GEMINI_CLI_OAUTH_CLIENT_SECRET=${GEMINI_CLI_OAUTH_CLIENT_SECRET:-}
- ANTIGRAVITY_OAUTH_CLIENT_SECRET=${ANTIGRAVITY_OAUTH_CLIENT_SECRET:-}
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s

View File

@@ -54,6 +54,10 @@ services:
- 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 Configuration
@@ -62,6 +66,8 @@ services:
- REDIS_PORT=${REDIS_PORT:-6379}
- REDIS_PASSWORD=${REDIS_PASSWORD:-redis_JCHeKT}
- 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}
# =======================================================================
@@ -109,6 +115,11 @@ services:
- GEMINI_OAUTH_SCOPES=${GEMINI_OAUTH_SCOPES:-}
- GEMINI_QUOTA_POLICY=${GEMINI_QUOTA_POLICY:-}
# Built-in OAuth client secrets (optional)
# SECURITY: This repo does not embed third-party client_secret.
- GEMINI_CLI_OAUTH_CLIENT_SECRET=${GEMINI_CLI_OAUTH_CLIENT_SECRET:-}
- ANTIGRAVITY_OAUTH_CLIENT_SECRET=${ANTIGRAVITY_OAUTH_CLIENT_SECRET:-}
# =======================================================================
# Security Configuration (URL Allowlist)
# =======================================================================
@@ -153,6 +164,10 @@ services:
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
# postgres:18-alpine 默认 PGDATA=/var/lib/postgresql/18/docker位于镜像声明的匿名卷 /var/lib/postgresql 内)。
# 若不显式设置 PGDATA则即使挂载了 postgres_data 到 /var/lib/postgresql/data数据也不会落盘到该命名卷
# docker compose down/up 后会触发 initdb 重新初始化,导致用户/密码等数据丢失。
- PGDATA=/var/lib/postgresql/data
- POSTGRES_USER=${POSTGRES_USER:-sub2api}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}
- POSTGRES_DB=${POSTGRES_DB:-sub2api}

123
deploy/install-datamanagementd.sh Executable file
View File

@@ -0,0 +1,123 @@
#!/usr/bin/env bash
set -euo pipefail
# 用法:
# sudo ./install-datamanagementd.sh --binary /path/to/datamanagementd
# 或:
# sudo ./install-datamanagementd.sh --source /path/to/sub2api/repo
BIN_PATH=""
SOURCE_PATH=""
INSTALL_DIR="/opt/sub2api"
DATA_DIR="/var/lib/sub2api/datamanagement"
SERVICE_FILE_NAME="sub2api-datamanagementd.service"
function print_help() {
cat <<'EOF'
用法:
install-datamanagementd.sh [--binary <datamanagementd二进制路径>] [--source <仓库路径>]
参数:
--binary 指定已构建的 datamanagementd 二进制路径
--source 指定 sub2api 仓库路径(脚本会执行 go build
-h, --help 显示帮助
示例:
sudo ./install-datamanagementd.sh --binary ./datamanagement/datamanagementd
sudo ./install-datamanagementd.sh --source /opt/sub2api-src
EOF
}
while [[ $# -gt 0 ]]; do
case "$1" in
--binary)
BIN_PATH="${2:-}"
shift 2
;;
--source)
SOURCE_PATH="${2:-}"
shift 2
;;
-h|--help)
print_help
exit 0
;;
*)
echo "未知参数: $1"
print_help
exit 1
;;
esac
done
if [[ -n "$BIN_PATH" && -n "$SOURCE_PATH" ]]; then
echo "错误: --binary 与 --source 只能二选一"
exit 1
fi
if [[ -z "$BIN_PATH" && -z "$SOURCE_PATH" ]]; then
echo "错误: 必须提供 --binary 或 --source"
exit 1
fi
if [[ "$(id -u)" -ne 0 ]]; then
echo "错误: 请使用 root 权限执行(例如 sudo"
exit 1
fi
if [[ -n "$SOURCE_PATH" ]]; then
if [[ ! -d "$SOURCE_PATH/datamanagement" ]]; then
echo "错误: 无效仓库路径,未找到 $SOURCE_PATH/datamanagement"
exit 1
fi
echo "[1/6] 从源码构建 datamanagementd..."
(cd "$SOURCE_PATH/datamanagement" && go build -o datamanagementd ./cmd/datamanagementd)
BIN_PATH="$SOURCE_PATH/datamanagement/datamanagementd"
fi
if [[ ! -f "$BIN_PATH" ]]; then
echo "错误: 二进制文件不存在: $BIN_PATH"
exit 1
fi
if ! id sub2api >/dev/null 2>&1; then
echo "[2/6] 创建系统用户 sub2api..."
useradd --system --no-create-home --shell /usr/sbin/nologin sub2api
else
echo "[2/6] 系统用户 sub2api 已存在,跳过创建"
fi
echo "[3/6] 安装 datamanagementd 二进制..."
mkdir -p "$INSTALL_DIR"
install -m 0755 "$BIN_PATH" "$INSTALL_DIR/datamanagementd"
echo "[4/6] 准备数据目录..."
mkdir -p "$DATA_DIR"
chown -R sub2api:sub2api /var/lib/sub2api
chmod 0750 "$DATA_DIR"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SERVICE_TEMPLATE="$SCRIPT_DIR/$SERVICE_FILE_NAME"
if [[ ! -f "$SERVICE_TEMPLATE" ]]; then
echo "错误: 未找到服务模板 $SERVICE_TEMPLATE"
exit 1
fi
echo "[5/6] 安装 systemd 服务..."
cp "$SERVICE_TEMPLATE" "/etc/systemd/system/$SERVICE_FILE_NAME"
systemctl daemon-reload
systemctl enable --now sub2api-datamanagementd
echo "[6/6] 完成,当前状态:"
systemctl --no-pager --full status sub2api-datamanagementd || true
cat <<'EOF'
下一步建议:
1. 查看日志sudo journalctl -u sub2api-datamanagementd -f
2. 在 sub2api容器部署时挂载 socket:
/tmp/sub2api-datamanagement.sock:/tmp/sub2api-datamanagement.sock
3. 进入管理后台“数据管理”页面确认 agent=enabled
EOF

View File

@@ -0,0 +1,22 @@
[Unit]
Description=Sub2API Data Management Daemon
After=network.target
Wants=network.target
[Service]
Type=simple
User=sub2api
Group=sub2api
WorkingDirectory=/opt/sub2api
ExecStart=/opt/sub2api/datamanagementd \
-socket-path /tmp/sub2api-datamanagement.sock \
-sqlite-path /var/lib/sub2api/datamanagement/datamanagementd.db \
-version 1.0.0
Restart=always
RestartSec=5s
LimitNOFILE=100000
NoNewPrivileges=true
PrivateTmp=false
[Install]
WantedBy=multi-user.target