* fix(gemini): 修复 google_one OAuth 配置和 scopes 问题 - 修复 google_one 类型在 ExchangeCode 和 RefreshToken 中使用内置客户端 - 添加 DefaultGoogleOneScopes,包含 generative-language 和 drive.readonly 权限 - 在 EffectiveOAuthConfig 中为 google_one 类型使用专门的 scopes - 将 docker-compose.override.yml 重命名为 .example 并添加到 .gitignore - 完善 docker-compose.override.yml.example 示例文档 解决问题: 1. google_one OAuth 授权后 API 调用返回 403 权限不足 2. 缺少访问 Gemini API 所需的 generative-language scope 3. 缺少获取 Drive 存储配额所需的 drive.readonly scope * fix(antigravity): 完全跳过 Claude 模型的所有 thinking 块 问题分析: - 当前代码尝试保留有 signature 的 thinking 块 - 但 Vertex AI 的 signature 是完整性令牌,无法在本地验证 - 导致 400 错误:Invalid signature in thinking block 根本原因: 1. thinking 功能已对非 Gemini 模型禁用 (isThinkingEnabled=false) 2. Vertex AI 要求原样重放 (thinking, signature) 对或完全不发送 3. 本地无法复制 Vertex 的加密验证逻辑 修复方案: - 对 Claude 模型完全跳过所有 thinking 块(无论是否有 signature) - 保持 Gemini 模型使用 dummy signature 的行为不变 - 更新测试用例以反映新的预期行为 影响: - 消除 thinking 相关的 400 错误 - 与现有的 thinking 禁用策略保持一致 - 不影响 Gemini 模型的 thinking 功能 测试: - ✅ TestBuildParts_ThinkingBlockWithoutSignature 全部通过 - ✅ TestBuildTools_CustomTypeTools 全部通过 参考:Codex review 建议 * fix(gateway): 修复 count_tokens 端点 400 错误 问题分析: - count_tokens 请求包含 thinking 块时返回 400 错误 - 原因:thinking 块未被过滤,直接转发到上游 API - 上游 API 拒绝无效的 thinking signature 根本原因: 1. /v1/messages 请求通过 TransformClaudeToGemini 过滤 thinking 块 2. count_tokens 请求绕过转换,直接转发原始请求体 3. 导致包含无效 signature 的 thinking 块被发送到上游 修复方案: - 创建 FilterThinkingBlocks 工具函数 - 在 buildCountTokensRequest 中应用过滤(1 行修改) - 与 /v1/messages 行为保持一致 实现细节: - FilterThinkingBlocks: 解析 JSON,过滤 thinking 块,重新序列化 - 失败安全:解析/序列化失败时返回原始请求体 - 性能优化:仅在发现 thinking 块时重新序列化 测试: - ✅ 6 个单元测试全部通过 - ✅ 覆盖正常过滤、无 thinking 块、无效 JSON 等场景 - ✅ 现有测试不受影响 影响: - 消除 count_tokens 的 400 错误 - 不影响 Antigravity 账号(仍返回模拟响应) - 适用于所有账号类型(OAuth、API Key) 文件修改: - backend/internal/service/gateway_request.go: +62 行(新函数) - backend/internal/service/gateway_service.go: +2 行(应用过滤) - backend/internal/service/gateway_request_test.go: +62 行(测试) * fix(gateway): 增强 thinking 块过滤逻辑 基于 Codex 分析和建议的改进: 问题分析: - 新错误:signature: Field required(signature 字段缺失) - 旧错误:Invalid signature(signature 存在但无效) - 两者都说明 thinking 块在请求中是危险的 Codex 建议: - 保持 Option A:完全跳过所有 thinking 块 - 原因:thinking 块应该是只输出的,除非有服务端来源证明 - 在无状态代理中,无法安全区分上游来源 vs 客户端注入 改进内容: 1. 增强 FilterThinkingBlocks 函数 - 过滤显式的 thinking 块:{"type":"thinking", ...} - 过滤无 type 的 thinking 对象:{"thinking": {...}} - 保留 tool_use 等其他类型块中的 thinking 字段 - 修复:只在实际过滤时更新 content 数组 2. 扩展过滤范围 - 将 FilterThinkingBlocks 应用到 /v1/messages 主路径 - 之前只应用于 count_tokens,现在两个端点都过滤 - 防止所有端点的 thinking 相关 400 错误 3. 改进测试 - 新增:过滤无 type discriminator 的 thinking 块 - 新增:不过滤 tool_use 中的 thinking 字段 - 使用 containsThinkingBlock 辅助函数验证 测试: - ✅ 8 个测试用例全部通过 - ✅ 覆盖各种 thinking 块格式 - ✅ 确保不误伤其他类型的块 影响: - 消除 signature required 和 invalid signature 错误 - 统一 /v1/messages 和 count_tokens 的行为 - 更健壮的 thinking 块检测逻辑 参考:Codex review 和代码改进 * refactor: 根据 Codex 审查建议进行代码优化 基于 Codex 代码审查的 P1 和 P2 改进: P1 改进(重要问题): 1. 优化日志输出 - 移除 thinking 块跳过时的 log.Printf - 避免高频请求下的日志噪音 - 添加注释说明可通过指标监控 2. 清理遗留代码 - 删除未使用的 isValidThoughtSignature 函数(27行) - 该函数在改为完全跳过 thinking 块后不再需要 P2 改进(性能优化): 3. 添加快速路径检查 - 在 FilterThinkingBlocks 中添加 bytes.Contains 预检查 - 如果请求体不包含 "thinking" 字符串,直接返回 - 避免不必要的 JSON 解析,提升性能 技术细节: - request_transformer.go: -27行(删除函数),+1行(优化注释) - gateway_request.go: +5行(快速路径 + bytes 导入) 测试: - ✅ TestBuildParts_ThinkingBlockWithoutSignature 全部通过 - ✅ TestFilterThinkingBlocks 全部通过(8个测试用例) 影响: - 减少日志噪音 - 提升性能(快速路径) - 代码更简洁(删除未使用代码) 参考:Codex 代码审查建议 * fix: 修复 golangci-lint 检查问题 - 格式化 gateway_request_test.go - 使用 switch 语句替代 if-else 链(staticcheck QF1003) * fix(antigravity): 修复 thinking signature 处理并实现 Auto 模式降级 问题分析: 1. 原先代码错误地禁用了 Claude via Vertex 的 thinkingConfig 2. 历史 thinking 块的 signature 被完全跳过,导致验证失败 3. 跨模型混用时 dummy signature 会导致 400 错误 修复内容: **request_transformer.go**: - 删除第 38-43 行的错误逻辑(禁用 thinkingConfig) - 引入 thoughtSignatureMode(Preserve/Dummy)策略 - Claude 模式:透传真实 signature,过滤空/dummy - Gemini 模式:使用 dummy signature - 支持 signature-only thinking 块 - tool_use 的 signature 也透传 **antigravity_gateway_service.go**: - 新增 isSignatureRelatedError() 检测 signature 相关错误 - 新增 stripThinkingFromClaudeRequest() 移除 thinking 块 - 实现 Auto 模式:检测 400 + signature 关键词时自动降级重试 - 重试时完全移除 thinking 配置和消息中的 thinking 块 - 最多重试一次,避免循环 **测试**: - 更新并新增测试覆盖 Claude preserve/Gemini dummy 模式 - 新增 tool_use signature 处理测试 - 所有测试通过(6/6) 影响: - ✅ Claude via Vertex 可以正常使用 thinking 功能 - ✅ 历史 signature 正确透传,避免验证失败 - ✅ 跨模型混用时自动过滤无效 signature - ✅ 错误驱动降级,自动修复 signature 问题 - ✅ 不影响纯 Claude API 和其他渠道 参考:Codex 深度分析和实现建议 * fix(lint): 修复 gofmt 格式问题 * fix(antigravity): 修复 stripThinkingFromClaudeRequest 遗漏 untyped thinking blocks 问题: - Codex 审查指出 stripThinkingFromClaudeRequest 只移除了 type="thinking" 的块 - 没有处理没有 type 字段的 thinking 对象(如 {"thinking": "...", "signature": "..."}) - 导致重试时仍包含无效 thinking 块,上游 400 错误持续 修复: - 添加检查:跳过没有 type 但有 thinking 字段的块 - 现在会移除两种格式: 1. {"type": "thinking", "thinking": "...", "signature": "..."} 2. {"thinking": "...", "signature": "..."}(untyped) 测试:所有测试通过 参考:Codex P1 审查意见
Sub2API Deployment Files
This directory contains files for deploying Sub2API on Linux servers.
Deployment Methods
| Method | Best For | Setup Wizard |
|---|---|---|
| Docker Compose | Quick setup, all-in-one | Not needed (auto-setup) |
| Binary Install | Production servers, systemd | Web-based wizard |
Files
| File | Description |
|---|---|
docker-compose.yml |
Docker Compose configuration |
.env.example |
Docker environment variables template |
DOCKER.md |
Docker Hub documentation |
install.sh |
One-click binary installation script |
sub2api.service |
Systemd service unit file |
config.example.yaml |
Example configuration file |
Docker Deployment (Recommended)
Quick Start
# Clone repository
git clone https://github.com/Wei-Shaw/sub2api.git
cd sub2api/deploy
# Configure environment
cp .env.example .env
nano .env # Set POSTGRES_PASSWORD (required)
# Start all services
docker-compose up -d
# View logs (check for auto-generated admin password)
docker-compose logs -f sub2api
# Access Web UI
# http://localhost:8080
How Auto-Setup Works
When using Docker Compose with AUTO_SETUP=true:
-
On first run, the system automatically:
- Connects to PostgreSQL and Redis
- Applies database migrations (SQL files in
backend/migrations/*.sql) and records them inschema_migrations - Generates JWT secret (if not provided)
- Creates admin account (password auto-generated if not provided)
- Writes config.yaml
-
No manual Setup Wizard needed - just configure
.envand start -
If
ADMIN_PASSWORDis not set, check logs for the generated password:docker-compose logs sub2api | grep "admin password"
Database Migration Notes (PostgreSQL)
- Migrations are applied in lexicographic order (e.g.
001_...sql,002_...sql). schema_migrationstracks applied migrations (filename + checksum).- Migrations are forward-only; rollback requires a DB backup restore or a manual compensating SQL script.
Verify users.allowed_groups → user_allowed_groups backfill
During the incremental GORM→Ent migration, users.allowed_groups (legacy BIGINT[]) is being replaced by a normalized join table user_allowed_groups(user_id, group_id).
Run this query to compare the legacy data vs the join table:
WITH old_pairs AS (
SELECT DISTINCT u.id AS user_id, x.group_id
FROM users u
CROSS JOIN LATERAL unnest(u.allowed_groups) AS x(group_id)
WHERE u.allowed_groups IS NOT NULL
)
SELECT
(SELECT COUNT(*) FROM old_pairs) AS old_pair_count,
(SELECT COUNT(*) FROM user_allowed_groups) AS new_pair_count;
Commands
# Start services
docker-compose up -d
# Stop services
docker-compose down
# View logs
docker-compose logs -f sub2api
# Restart Sub2API only
docker-compose restart sub2api
# Update to latest version
docker-compose pull
docker-compose up -d
# Remove all data (caution!)
docker-compose down -v
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
POSTGRES_PASSWORD |
Yes | - | PostgreSQL password |
SERVER_PORT |
No | 8080 |
Server port |
ADMIN_EMAIL |
No | admin@sub2api.local |
Admin email |
ADMIN_PASSWORD |
No | (auto-generated) | Admin password |
JWT_SECRET |
No | (auto-generated) | JWT secret |
TZ |
No | Asia/Shanghai |
Timezone |
GEMINI_OAUTH_CLIENT_ID |
No | (builtin) | Google OAuth client ID (Gemini OAuth). Leave empty to use the built-in Gemini CLI client. |
GEMINI_OAUTH_CLIENT_SECRET |
No | (builtin) | Google OAuth client secret (Gemini OAuth). Leave empty to use the built-in Gemini CLI client. |
GEMINI_OAUTH_SCOPES |
No | (default) | OAuth scopes (Gemini OAuth) |
GEMINI_QUOTA_POLICY |
No | (empty) | JSON overrides for Gemini local quota simulation (Code Assist only). |
See .env.example for all available options.
Gemini OAuth Configuration
Sub2API supports three methods to connect to Gemini:
Method 1: Code Assist OAuth (Recommended for GCP Users)
No configuration needed - always uses the built-in Gemini CLI OAuth client (public).
- Leave
GEMINI_OAUTH_CLIENT_IDandGEMINI_OAUTH_CLIENT_SECRETempty - In the Admin UI, create a Gemini OAuth account and select "Code Assist" type
- Complete the OAuth flow in your browser
Note: Even if you configure
GEMINI_OAUTH_CLIENT_ID/GEMINI_OAUTH_CLIENT_SECRETfor AI Studio OAuth, Code Assist OAuth will still use the built-in Gemini CLI client.
Requirements:
- Google account with access to Google Cloud Platform
- A GCP project (auto-detected or manually specified)
How to get Project ID (if auto-detection fails):
- Go to Google Cloud Console
- Click the project dropdown at the top of the page
- Copy the Project ID (not the project name) from the list
- Common formats:
my-project-123456orcloud-ai-companion-xxxxx
Method 2: AI Studio OAuth (For Regular Google Accounts)
Requires your own OAuth client credentials.
Step 1: Create OAuth Client in Google Cloud Console
- Go to Google Cloud Console - Credentials
- Create a new project or select an existing one
- Enable the Generative Language API:
- Go to "APIs & Services" → "Library"
- Search for "Generative Language API"
- Click "Enable"
- Configure OAuth Consent Screen (if not done):
- Go to "APIs & Services" → "OAuth consent screen"
- Choose "External" user type
- Fill in app name, user support email, developer contact
- Add scopes:
https://www.googleapis.com/auth/generative-language.retriever(and optionallyhttps://www.googleapis.com/auth/cloud-platform) - Add test users (your Google account email)
- Create OAuth 2.0 credentials:
- Go to "APIs & Services" → "Credentials"
- Click "Create Credentials" → "OAuth client ID"
- Application type: Web application (or Desktop app)
- Name: e.g., "Sub2API Gemini"
- Authorized redirect URIs: Add
http://localhost:1455/auth/callback
- Copy the Client ID and Client Secret
- ⚠️ Publish to Production (IMPORTANT):
- Go to "APIs & Services" → "OAuth consent screen"
- Click "PUBLISH APP" to move from Testing to Production
- Testing mode limitations:
- Only manually added test users can authenticate (max 100 users)
- Refresh tokens expire after 7 days
- Users must be re-added periodically
- Production mode: Any Google user can authenticate, tokens don't expire
- Note: For sensitive scopes, Google may require verification (demo video, privacy policy)
Step 2: Configure Environment Variables
GEMINI_OAUTH_CLIENT_ID=your-client-id.apps.googleusercontent.com
GEMINI_OAUTH_CLIENT_SECRET=GOCSPX-your-client-secret
Step 3: Create Account in Admin UI
- Create a Gemini OAuth account and select "AI Studio" type
- Complete the OAuth flow
- After consent, your browser will be redirected to
http://localhost:1455/auth/callback?code=...&state=... - Copy the full callback URL (recommended) or just the
codeand paste it back into the Admin UI
- After consent, your browser will be redirected to
Method 3: API Key (Simplest)
- Go to Google AI Studio
- Click "Create API key"
- In Admin UI, create a Gemini API Key account
- Paste your API key (starts with
AIza...)
Comparison Table
| Feature | Code Assist OAuth | AI Studio OAuth | API Key |
|---|---|---|---|
| Setup Complexity | Easy (no config) | Medium (OAuth client) | Easy |
| GCP Project Required | Yes | No | No |
| Custom OAuth Client | No (built-in) | Yes (required) | N/A |
| Rate Limits | GCP quota | Standard | Standard |
| Best For | GCP developers | Regular users needing OAuth | Quick testing |
Binary Installation
For production servers using systemd.
One-Line Installation
curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/install.sh | sudo bash
Manual Installation
- Download the latest release from GitHub Releases
- Extract and copy the binary to
/opt/sub2api/ - Copy
sub2api.serviceto/etc/systemd/system/ - Run:
sudo systemctl daemon-reload sudo systemctl enable sub2api sudo systemctl start sub2api - Open the Setup Wizard in your browser to complete configuration
Commands
# Install
sudo ./install.sh
# Upgrade
sudo ./install.sh upgrade
# Uninstall
sudo ./install.sh uninstall
Service Management
# Start the service
sudo systemctl start sub2api
# Stop the service
sudo systemctl stop sub2api
# Restart the service
sudo systemctl restart sub2api
# Check status
sudo systemctl status sub2api
# View logs
sudo journalctl -u sub2api -f
# Enable auto-start on boot
sudo systemctl enable sub2api
Configuration
Server Address and Port
During installation, you will be prompted to configure the server listen address and port. These settings are stored in the systemd service file as environment variables.
To change after installation:
-
Edit the systemd service:
sudo systemctl edit sub2api -
Add or modify:
[Service] Environment=SERVER_HOST=0.0.0.0 Environment=SERVER_PORT=3000 -
Reload and restart:
sudo systemctl daemon-reload sudo systemctl restart sub2api
Gemini OAuth Configuration
If you need to use AI Studio OAuth for Gemini accounts, add the OAuth client credentials to the systemd service file:
-
Edit the service file:
sudo nano /etc/systemd/system/sub2api.service -
Add your OAuth credentials in the
[Service]section (after the existingEnvironment=lines):Environment=GEMINI_OAUTH_CLIENT_ID=your-client-id.apps.googleusercontent.com Environment=GEMINI_OAUTH_CLIENT_SECRET=GOCSPX-your-client-secret -
Reload and restart:
sudo systemctl daemon-reload sudo systemctl restart sub2api
Note: Code Assist OAuth does not require any configuration - it uses the built-in Gemini CLI client. See the Gemini OAuth Configuration section above for detailed setup instructions.
Application Configuration
The main config file is at /etc/sub2api/config.yaml (created by Setup Wizard).
Prerequisites
- Linux server (Ubuntu 20.04+, Debian 11+, CentOS 8+, etc.)
- PostgreSQL 14+
- Redis 6+
- systemd
Directory Structure
/opt/sub2api/
├── sub2api # Main binary
├── sub2api.backup # Backup (after upgrade)
└── data/ # Runtime data
/etc/sub2api/
└── config.yaml # Configuration file
Troubleshooting
Docker
# Check container status
docker-compose ps
# View detailed logs
docker-compose logs --tail=100 sub2api
# Check database connection
docker-compose exec postgres pg_isready
# Check Redis connection
docker-compose exec redis redis-cli ping
# Restart all services
docker-compose restart
Binary Install
# Check service status
sudo systemctl status sub2api
# View recent logs
sudo journalctl -u sub2api -n 50
# Check config file
sudo cat /etc/sub2api/config.yaml
# Check PostgreSQL
sudo systemctl status postgresql
# Check Redis
sudo systemctl status redis
Common Issues
- Port already in use: Change
SERVER_PORTin.envor systemd config - Database connection failed: Check PostgreSQL is running and credentials are correct
- Redis connection failed: Check Redis is running and password is correct
- Permission denied: Ensure proper file ownership for binary install