Files
xinghuoapi/deploy/README.md
shaw 5cda979209 feat(deploy): 优化 Docker 部署体验,新增一键部署脚本
## 新增功能

- 新增 docker-compose.local.yml:使用本地目录存储数据,便于迁移和备份
- 新增 docker-deploy.sh:一键部署脚本,自动生成安全密钥(JWT_SECRET、TOTP_ENCRYPTION_KEY、POSTGRES_PASSWORD)
- 新增 deploy/.gitignore:忽略运行时数据目录

## 优化改进

- docker-compose.local.yml 包含 PGDATA 环境变量修复,解决 PostgreSQL 18 Alpine 数据丢失问题
- 脚本自动设置 .env 文件权限为 600,增强安全性
- 脚本显示生成的凭证,方便用户记录

## 文档更新

- 更新 README.md(英文版):新增"快速开始"章节,添加部署版本对比表
- 更新 README_CN.md(中文版):同步英文版更新
- 更新 deploy/README.md:详细说明两种部署方式和迁移方法

## 使用方式

一键部署:
```bash
curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/docker-deploy.sh | bash
docker-compose -f docker-compose.local.yml up -d
```

轻松迁移:
```bash
tar czf sub2api-complete.tar.gz deploy/
# 传输到新服务器后直接解压启动即可
```
2026-02-02 16:17:07 +08:00

594 lines
18 KiB
Markdown

# 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 (named volumes) |
| `docker-compose.local.yml` | Docker Compose configuration (local directories, easy migration) |
| `docker-deploy.sh` | **One-click Docker deployment script (recommended)** |
| `.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)
### Method 1: One-Click Deployment (Recommended)
Use the automated preparation script for the easiest setup:
```bash
# Download and run the preparation script
curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/docker-deploy.sh | bash
# Or download first, then run
curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/docker-deploy.sh -o docker-deploy.sh
chmod +x docker-deploy.sh
./docker-deploy.sh
```
**What the script does:**
- Downloads `docker-compose.local.yml` and `.env.example`
- Automatically generates secure secrets (JWT_SECRET, TOTP_ENCRYPTION_KEY, POSTGRES_PASSWORD)
- Creates `.env` file with generated secrets
- Creates necessary data directories (data/, postgres_data/, redis_data/)
- **Displays generated credentials** (POSTGRES_PASSWORD, JWT_SECRET, etc.)
**After running the script:**
```bash
# Start services
docker-compose -f docker-compose.local.yml up -d
# View logs
docker-compose -f docker-compose.local.yml logs -f sub2api
# If admin password was auto-generated, find it in logs:
docker-compose -f docker-compose.local.yml logs sub2api | grep "admin password"
# Access Web UI
# http://localhost:8080
```
### Method 2: Manual Deployment
If you prefer manual control:
```bash
# 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 and other required variables
# Generate secure secrets (recommended)
JWT_SECRET=$(openssl rand -hex 32)
TOTP_ENCRYPTION_KEY=$(openssl rand -hex 32)
echo "JWT_SECRET=${JWT_SECRET}" >> .env
echo "TOTP_ENCRYPTION_KEY=${TOTP_ENCRYPTION_KEY}" >> .env
# Create data directories
mkdir -p data postgres_data redis_data
# Start all services using local directory version
docker-compose -f docker-compose.local.yml up -d
# View logs (check for auto-generated admin password)
docker-compose -f docker-compose.local.yml logs -f sub2api
# Access Web UI
# http://localhost:8080
```
### Deployment Version Comparison
| Version | Data Storage | Migration | Best For |
|---------|-------------|-----------|----------|
| **docker-compose.local.yml** | Local directories (./data, ./postgres_data, ./redis_data) | ✅ Easy (tar entire directory) | Production, need frequent backups/migration |
| **docker-compose.yml** | Named volumes (/var/lib/docker/volumes/) | ⚠️ Requires docker commands | Simple setup, don't need migration |
**Recommendation:** Use `docker-compose.local.yml` (deployed by `docker-deploy.sh`) for easier data management and migration.
### How Auto-Setup Works
When using Docker Compose with `AUTO_SETUP=true`:
1. On first run, the system automatically:
- Connects to PostgreSQL and Redis
- Applies database migrations (SQL files in `backend/migrations/*.sql`) and records them in `schema_migrations`
- Generates JWT secret (if not provided)
- Creates admin account (password auto-generated if not provided)
- Writes config.yaml
2. No manual Setup Wizard needed - just configure `.env` and start
3. If `ADMIN_PASSWORD` is not set, check logs for the generated password:
```bash
docker-compose logs sub2api | grep "admin password"
```
### Database Migration Notes (PostgreSQL)
- Migrations are applied in lexicographic order (e.g. `001_...sql`, `002_...sql`).
- `schema_migrations` tracks 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:
```sql
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
For **local directory version** (docker-compose.local.yml):
```bash
# Start services
docker-compose -f docker-compose.local.yml up -d
# Stop services
docker-compose -f docker-compose.local.yml down
# View logs
docker-compose -f docker-compose.local.yml logs -f sub2api
# Restart Sub2API only
docker-compose -f docker-compose.local.yml restart sub2api
# Update to latest version
docker-compose -f docker-compose.local.yml pull
docker-compose -f docker-compose.local.yml up -d
# Remove all data (caution!)
docker-compose -f docker-compose.local.yml down
rm -rf data/ postgres_data/ redis_data/
```
For **named volumes version** (docker-compose.yml):
```bash
# 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 |
| `JWT_SECRET` | **Recommended** | *(auto-generated)* | JWT secret (fixed for persistent sessions) |
| `TOTP_ENCRYPTION_KEY` | **Recommended** | *(auto-generated)* | TOTP encryption key (fixed for persistent 2FA) |
| `SERVER_PORT` | No | `8080` | Server port |
| `ADMIN_EMAIL` | No | `admin@sub2api.local` | Admin email |
| `ADMIN_PASSWORD` | No | *(auto-generated)* | Admin password |
| `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.
> **Note:** The `docker-deploy.sh` script automatically generates `JWT_SECRET`, `TOTP_ENCRYPTION_KEY`, and `POSTGRES_PASSWORD` for you.
### Easy Migration (Local Directory Version)
When using `docker-compose.local.yml`, all data is stored in local directories, making migration simple:
```bash
# On source server: Stop services and create archive
cd /path/to/deployment
docker-compose -f docker-compose.local.yml down
cd ..
tar czf sub2api-complete.tar.gz deployment/
# Transfer to new server
scp sub2api-complete.tar.gz user@new-server:/path/to/destination/
# On new server: Extract and start
tar xzf sub2api-complete.tar.gz
cd deployment/
docker-compose -f docker-compose.local.yml up -d
```
Your entire deployment (configuration + data) is migrated!
---
## 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).
1. Leave `GEMINI_OAUTH_CLIENT_ID` and `GEMINI_OAUTH_CLIENT_SECRET` empty
2. In the Admin UI, create a Gemini OAuth account and select **"Code Assist"** type
3. Complete the OAuth flow in your browser
> Note: Even if you configure `GEMINI_OAUTH_CLIENT_ID` / `GEMINI_OAUTH_CLIENT_SECRET` for 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):**
1. Go to [Google Cloud Console](https://console.cloud.google.com/)
2. Click the project dropdown at the top of the page
3. Copy the Project ID (not the project name) from the list
4. Common formats: `my-project-123456` or `cloud-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**
1. Go to [Google Cloud Console - Credentials](https://console.cloud.google.com/apis/credentials)
2. Create a new project or select an existing one
3. **Enable the Generative Language API:**
- Go to "APIs & Services" → "Library"
- Search for "Generative Language API"
- Click "Enable"
4. **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 optionally `https://www.googleapis.com/auth/cloud-platform`)
- Add test users (your Google account email)
5. **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`
6. Copy the **Client ID** and **Client Secret**
7. **⚠️ 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**
```bash
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**
1. Create a Gemini OAuth account and select **"AI Studio"** type
2. 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 `code` and paste it back into the Admin UI
### Method 3: API Key (Simplest)
1. Go to [Google AI Studio](https://aistudio.google.com/app/apikey)
2. Click "Create API key"
3. In Admin UI, create a Gemini **API Key** account
4. 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
```bash
curl -sSL https://raw.githubusercontent.com/Wei-Shaw/sub2api/main/deploy/install.sh | sudo bash
```
### Manual Installation
1. Download the latest release from [GitHub Releases](https://github.com/Wei-Shaw/sub2api/releases)
2. Extract and copy the binary to `/opt/sub2api/`
3. Copy `sub2api.service` to `/etc/systemd/system/`
4. Run:
```bash
sudo systemctl daemon-reload
sudo systemctl enable sub2api
sudo systemctl start sub2api
```
5. Open the Setup Wizard in your browser to complete configuration
### Commands
```bash
# Install
sudo ./install.sh
# Upgrade
sudo ./install.sh upgrade
# Uninstall
sudo ./install.sh uninstall
```
### Service Management
```bash
# 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:
1. Edit the systemd service:
```bash
sudo systemctl edit sub2api
```
2. Add or modify:
```ini
[Service]
Environment=SERVER_HOST=0.0.0.0
Environment=SERVER_PORT=3000
```
3. Reload and restart:
```bash
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:
1. Edit the service file:
```bash
sudo nano /etc/systemd/system/sub2api.service
```
2. Add your OAuth credentials in the `[Service]` section (after the existing `Environment=` lines):
```ini
Environment=GEMINI_OAUTH_CLIENT_ID=your-client-id.apps.googleusercontent.com
Environment=GEMINI_OAUTH_CLIENT_SECRET=GOCSPX-your-client-secret
```
3. Reload and restart:
```bash
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](#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
For **local directory version**:
```bash
# Check container status
docker-compose -f docker-compose.local.yml ps
# View detailed logs
docker-compose -f docker-compose.local.yml logs --tail=100 sub2api
# Check database connection
docker-compose -f docker-compose.local.yml exec postgres pg_isready
# Check Redis connection
docker-compose -f docker-compose.local.yml exec redis redis-cli ping
# Restart all services
docker-compose -f docker-compose.local.yml restart
# Check data directories
ls -la data/ postgres_data/ redis_data/
```
For **named volumes version**:
```bash
# 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
```bash
# 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
1. **Port already in use**: Change `SERVER_PORT` in `.env` or systemd config
2. **Database connection failed**: Check PostgreSQL is running and credentials are correct
3. **Redis connection failed**: Check Redis is running and password is correct
4. **Permission denied**: Ensure proper file ownership for binary install
---
## TLS Fingerprint Configuration
Sub2API supports TLS fingerprint simulation to make requests appear as if they come from the official Claude CLI (Node.js client).
> **💡 Tip:** Visit **[tls.sub2api.org](https://tls.sub2api.org/)** to get TLS fingerprint information for different devices and browsers.
### Default Behavior
- Built-in `claude_cli_v2` profile simulates Node.js 20.x + OpenSSL 3.x
- JA3 Hash: `1a28e69016765d92e3b381168d68922c`
- JA4: `t13d5911h1_a33745022dd6_1f22a2ca17c4`
- Profile selection: `accountID % profileCount`
### Configuration
```yaml
gateway:
tls_fingerprint:
enabled: true # Global switch
profiles:
# Simple profile (uses default cipher suites)
profile_1:
name: "Profile 1"
# Profile with custom cipher suites (use compact array format)
profile_2:
name: "Profile 2"
cipher_suites: [4866, 4867, 4865, 49199, 49195, 49200, 49196]
curves: [29, 23, 24]
point_formats: [0]
# Another custom profile
profile_3:
name: "Profile 3"
cipher_suites: [4865, 4866, 4867, 49199, 49200]
curves: [29, 23, 24, 25]
```
### Profile Fields
| Field | Type | Description |
|-------|------|-------------|
| `name` | string | Display name (required) |
| `cipher_suites` | []uint16 | Cipher suites in decimal. Empty = default |
| `curves` | []uint16 | Elliptic curves in decimal. Empty = default |
| `point_formats` | []uint8 | EC point formats. Empty = default |
### Common Values Reference
**Cipher Suites (TLS 1.3):** `4865` (AES_128_GCM), `4866` (AES_256_GCM), `4867` (CHACHA20)
**Cipher Suites (TLS 1.2):** `49195`, `49196`, `49199`, `49200` (ECDHE variants)
**Curves:** `29` (X25519), `23` (P-256), `24` (P-384), `25` (P-521)