Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
0170d19f
Commit
0170d19f
authored
Feb 02, 2026
by
song
Browse files
merge upstream main
parent
7ade9baa
Changes
319
Show whitespace changes
Inline
Side-by-side
backend/internal/setup/setup.go
View file @
0170d19f
...
...
@@ -3,6 +3,7 @@ package setup
import
(
"context"
"crypto/rand"
"crypto/tls"
"database/sql"
"encoding/hex"
"fmt"
...
...
@@ -83,6 +84,7 @@ type RedisConfig struct {
Port
int
`json:"port" yaml:"port"`
Password
string
`json:"password" yaml:"password"`
DB
int
`json:"db" yaml:"db"`
EnableTLS
bool
`json:"enable_tls" yaml:"enable_tls"`
}
type
AdminConfig
struct
{
...
...
@@ -199,11 +201,20 @@ func TestDatabaseConnection(cfg *DatabaseConfig) error {
// TestRedisConnection tests the Redis connection
func
TestRedisConnection
(
cfg
*
RedisConfig
)
error
{
rdb
:=
redis
.
NewClient
(
&
redis
.
Options
{
opts
:=
&
redis
.
Options
{
Addr
:
fmt
.
Sprintf
(
"%s:%d"
,
cfg
.
Host
,
cfg
.
Port
),
Password
:
cfg
.
Password
,
DB
:
cfg
.
DB
,
})
}
if
cfg
.
EnableTLS
{
opts
.
TLSConfig
=
&
tls
.
Config
{
MinVersion
:
tls
.
VersionTLS12
,
ServerName
:
cfg
.
Host
,
}
}
rdb
:=
redis
.
NewClient
(
opts
)
defer
func
()
{
if
err
:=
rdb
.
Close
();
err
!=
nil
{
log
.
Printf
(
"failed to close redis client: %v"
,
err
)
...
...
@@ -489,6 +500,7 @@ func AutoSetupFromEnv() error {
Port
:
getEnvIntOrDefault
(
"REDIS_PORT"
,
6379
),
Password
:
getEnvOrDefault
(
"REDIS_PASSWORD"
,
""
),
DB
:
getEnvIntOrDefault
(
"REDIS_DB"
,
0
),
EnableTLS
:
getEnvOrDefault
(
"REDIS_ENABLE_TLS"
,
"false"
)
==
"true"
,
},
Admin
:
AdminConfig
{
Email
:
getEnvOrDefault
(
"ADMIN_EMAIL"
,
"admin@sub2api.local"
),
...
...
backend/internal/util/urlvalidator/validator.go
View file @
0170d19f
...
...
@@ -46,7 +46,7 @@ func ValidateURLFormat(raw string, allowInsecureHTTP bool) (string, error) {
}
}
return
trimmed
,
nil
return
strings
.
TrimRight
(
trimmed
,
"/"
),
nil
}
func
ValidateHTTPSURL
(
raw
string
,
opts
ValidationOptions
)
(
string
,
error
)
{
...
...
backend/internal/util/urlvalidator/validator_test.go
View file @
0170d19f
...
...
@@ -21,4 +21,31 @@ func TestValidateURLFormat(t *testing.T) {
if
_
,
err
:=
ValidateURLFormat
(
"https://example.com:bad"
,
true
);
err
==
nil
{
t
.
Fatalf
(
"expected invalid port to fail"
)
}
// 验证末尾斜杠被移除
normalized
,
err
:=
ValidateURLFormat
(
"https://example.com/"
,
false
)
if
err
!=
nil
{
t
.
Fatalf
(
"expected trailing slash url to pass, got %v"
,
err
)
}
if
normalized
!=
"https://example.com"
{
t
.
Fatalf
(
"expected trailing slash to be removed, got %s"
,
normalized
)
}
// 验证多个末尾斜杠被移除
normalized
,
err
=
ValidateURLFormat
(
"https://example.com///"
,
false
)
if
err
!=
nil
{
t
.
Fatalf
(
"expected multiple trailing slashes to pass, got %v"
,
err
)
}
if
normalized
!=
"https://example.com"
{
t
.
Fatalf
(
"expected all trailing slashes to be removed, got %s"
,
normalized
)
}
// 验证带路径的 URL 末尾斜杠被移除
normalized
,
err
=
ValidateURLFormat
(
"https://example.com/api/v1/"
,
false
)
if
err
!=
nil
{
t
.
Fatalf
(
"expected trailing slash url with path to pass, got %v"
,
err
)
}
if
normalized
!=
"https://example.com/api/v1"
{
t
.
Fatalf
(
"expected trailing slash to be removed from path, got %s"
,
normalized
)
}
}
backend/migrations/006_add_users_allowed_groups_compat.sql
0 → 100644
View file @
0170d19f
-- 兼容旧库:若尚未创建 user_allowed_groups,则确保 users.allowed_groups 存在,避免 007 迁移回填失败。
DO
$$
BEGIN
IF
to_regclass
(
'public.user_allowed_groups'
)
IS
NULL
THEN
IF
EXISTS
(
SELECT
1
FROM
information_schema
.
tables
WHERE
table_schema
=
'public'
AND
table_name
=
'users'
)
THEN
ALTER
TABLE
users
ADD
COLUMN
IF
NOT
EXISTS
allowed_groups
BIGINT
[]
DEFAULT
NULL
;
END
IF
;
END
IF
;
END
$$
;
backend/migrations/006b_guard_users_allowed_groups.sql
0 → 100644
View file @
0170d19f
-- 兼容缺失 users.allowed_groups 的老库,确保 007 回填可执行。
DO
$$
BEGIN
IF
EXISTS
(
SELECT
1
FROM
information_schema
.
tables
WHERE
table_schema
=
'public'
AND
table_name
=
'users'
)
THEN
IF
NOT
EXISTS
(
SELECT
1
FROM
information_schema
.
columns
WHERE
table_schema
=
'public'
AND
table_name
=
'users'
AND
column_name
=
'allowed_groups'
)
THEN
IF
NOT
EXISTS
(
SELECT
1
FROM
schema_migrations
WHERE
filename
=
'014_drop_legacy_allowed_groups.sql'
)
THEN
ALTER
TABLE
users
ADD
COLUMN
IF
NOT
EXISTS
allowed_groups
BIGINT
[]
DEFAULT
NULL
;
END
IF
;
END
IF
;
END
IF
;
END
$$
;
backend/migrations/042_add_usage_cleanup_tasks.sql
0 → 100644
View file @
0170d19f
-- 042_add_usage_cleanup_tasks.sql
-- 使用记录清理任务表
CREATE
TABLE
IF
NOT
EXISTS
usage_cleanup_tasks
(
id
BIGSERIAL
PRIMARY
KEY
,
status
VARCHAR
(
20
)
NOT
NULL
,
filters
JSONB
NOT
NULL
,
created_by
BIGINT
NOT
NULL
REFERENCES
users
(
id
)
ON
DELETE
RESTRICT
,
deleted_rows
BIGINT
NOT
NULL
DEFAULT
0
,
error_message
TEXT
,
started_at
TIMESTAMPTZ
,
finished_at
TIMESTAMPTZ
,
created_at
TIMESTAMPTZ
NOT
NULL
DEFAULT
NOW
(),
updated_at
TIMESTAMPTZ
NOT
NULL
DEFAULT
NOW
()
);
CREATE
INDEX
IF
NOT
EXISTS
idx_usage_cleanup_tasks_status_created_at
ON
usage_cleanup_tasks
(
status
,
created_at
DESC
);
CREATE
INDEX
IF
NOT
EXISTS
idx_usage_cleanup_tasks_created_at
ON
usage_cleanup_tasks
(
created_at
DESC
);
backend/migrations/042_add_ops_system_metrics_switch_count.sql
→
backend/migrations/042
b
_add_ops_system_metrics_switch_count.sql
View file @
0170d19f
File moved
backend/migrations/043_add_usage_cleanup_cancel_audit.sql
0 → 100644
View file @
0170d19f
-- 043_add_usage_cleanup_cancel_audit.sql
-- usage_cleanup_tasks 取消任务审计字段
ALTER
TABLE
usage_cleanup_tasks
ADD
COLUMN
IF
NOT
EXISTS
canceled_by
BIGINT
REFERENCES
users
(
id
)
ON
DELETE
SET
NULL
,
ADD
COLUMN
IF
NOT
EXISTS
canceled_at
TIMESTAMPTZ
;
CREATE
INDEX
IF
NOT
EXISTS
idx_usage_cleanup_tasks_canceled_at
ON
usage_cleanup_tasks
(
canceled_at
DESC
);
backend/migrations/043_add_group_invalid_request_fallback.sql
→
backend/migrations/043
b
_add_group_invalid_request_fallback.sql
View file @
0170d19f
-- 043_add_group_invalid_request_fallback.sql
-- 043
b
_add_group_invalid_request_fallback.sql
-- 添加无效请求兜底分组配置
-- 添加 fallback_group_id_on_invalid_request 字段:无效请求兜底使用的分组
...
...
backend/migrations/044_add_user_totp.sql
0 → 100644
View file @
0170d19f
-- 为 users 表添加 TOTP 双因素认证字段
ALTER
TABLE
users
ADD
COLUMN
IF
NOT
EXISTS
totp_secret_encrypted
TEXT
DEFAULT
NULL
,
ADD
COLUMN
IF
NOT
EXISTS
totp_enabled
BOOLEAN
NOT
NULL
DEFAULT
FALSE
,
ADD
COLUMN
IF
NOT
EXISTS
totp_enabled_at
TIMESTAMPTZ
DEFAULT
NULL
;
COMMENT
ON
COLUMN
users
.
totp_secret_encrypted
IS
'AES-256-GCM 加密的 TOTP 密钥'
;
COMMENT
ON
COLUMN
users
.
totp_enabled
IS
'是否启用 TOTP 双因素认证'
;
COMMENT
ON
COLUMN
users
.
totp_enabled_at
IS
'TOTP 启用时间'
;
-- 创建索引以支持快速查询启用 2FA 的用户
CREATE
INDEX
IF
NOT
EXISTS
idx_users_totp_enabled
ON
users
(
totp_enabled
)
WHERE
deleted_at
IS
NULL
AND
totp_enabled
=
true
;
backend/migrations/044_add_group_mcp_xml_inject.sql
→
backend/migrations/044
b
_add_group_mcp_xml_inject.sql
View file @
0170d19f
File moved
backend/migrations/045_add_announcements.sql
0 → 100644
View file @
0170d19f
-- 创建公告表
CREATE
TABLE
IF
NOT
EXISTS
announcements
(
id
BIGSERIAL
PRIMARY
KEY
,
title
VARCHAR
(
200
)
NOT
NULL
,
content
TEXT
NOT
NULL
,
status
VARCHAR
(
20
)
NOT
NULL
DEFAULT
'draft'
,
targeting
JSONB
NOT
NULL
DEFAULT
'{}'
::
jsonb
,
starts_at
TIMESTAMPTZ
DEFAULT
NULL
,
ends_at
TIMESTAMPTZ
DEFAULT
NULL
,
created_by
BIGINT
DEFAULT
NULL
REFERENCES
users
(
id
)
ON
DELETE
SET
NULL
,
updated_by
BIGINT
DEFAULT
NULL
REFERENCES
users
(
id
)
ON
DELETE
SET
NULL
,
created_at
TIMESTAMPTZ
NOT
NULL
DEFAULT
NOW
(),
updated_at
TIMESTAMPTZ
NOT
NULL
DEFAULT
NOW
()
);
-- 公告已读表
CREATE
TABLE
IF
NOT
EXISTS
announcement_reads
(
id
BIGSERIAL
PRIMARY
KEY
,
announcement_id
BIGINT
NOT
NULL
REFERENCES
announcements
(
id
)
ON
DELETE
CASCADE
,
user_id
BIGINT
NOT
NULL
REFERENCES
users
(
id
)
ON
DELETE
CASCADE
,
read_at
TIMESTAMPTZ
NOT
NULL
DEFAULT
NOW
(),
created_at
TIMESTAMPTZ
NOT
NULL
DEFAULT
NOW
(),
UNIQUE
(
announcement_id
,
user_id
)
);
-- 索引
CREATE
INDEX
IF
NOT
EXISTS
idx_announcements_status
ON
announcements
(
status
);
CREATE
INDEX
IF
NOT
EXISTS
idx_announcements_starts_at
ON
announcements
(
starts_at
);
CREATE
INDEX
IF
NOT
EXISTS
idx_announcements_ends_at
ON
announcements
(
ends_at
);
CREATE
INDEX
IF
NOT
EXISTS
idx_announcements_created_at
ON
announcements
(
created_at
);
CREATE
INDEX
IF
NOT
EXISTS
idx_announcement_reads_announcement_id
ON
announcement_reads
(
announcement_id
);
CREATE
INDEX
IF
NOT
EXISTS
idx_announcement_reads_user_id
ON
announcement_reads
(
user_id
);
CREATE
INDEX
IF
NOT
EXISTS
idx_announcement_reads_read_at
ON
announcement_reads
(
read_at
);
COMMENT
ON
TABLE
announcements
IS
'系统公告'
;
COMMENT
ON
COLUMN
announcements
.
status
IS
'状态: draft, active, archived'
;
COMMENT
ON
COLUMN
announcements
.
targeting
IS
'展示条件(JSON 规则)'
;
COMMENT
ON
COLUMN
announcements
.
starts_at
IS
'开始展示时间(为空表示立即生效)'
;
COMMENT
ON
COLUMN
announcements
.
ends_at
IS
'结束展示时间(为空表示永久生效)'
;
COMMENT
ON
TABLE
announcement_reads
IS
'公告已读记录'
;
COMMENT
ON
COLUMN
announcement_reads
.
read_at
IS
'用户首次已读时间'
;
config.yaml
View file @
0170d19f
...
...
@@ -251,6 +251,27 @@ dashboard_aggregation:
# 日聚合保留天数
daily_days
:
730
# =============================================================================
# Usage Cleanup Task Configuration
# 使用记录清理任务配置(重启生效)
# =============================================================================
usage_cleanup
:
# Enable cleanup task worker
# 启用清理任务执行器
enabled
:
true
# Max date range (days) per task
# 单次任务最大时间跨度(天)
max_range_days
:
31
# Batch delete size
# 单批删除数量
batch_size
:
5000
# Worker interval (seconds)
# 执行器轮询间隔(秒)
worker_interval_seconds
:
10
# Task execution timeout (seconds)
# 单次任务最大执行时长(秒)
task_timeout_seconds
:
1800
# =============================================================================
# Concurrency Wait Configuration
# 并发等待配置
...
...
@@ -301,6 +322,9 @@ redis:
# Database number (0-15)
# 数据库编号(0-15)
db
:
0
# Enable TLS/SSL connection
# 是否启用 TLS/SSL 连接
enable_tls
:
false
# =============================================================================
# Ops Monitoring (Optional)
...
...
deploy/.env.example
View file @
0170d19f
...
...
@@ -40,6 +40,7 @@ POSTGRES_DB=sub2api
# Leave empty for no password (default for local development)
REDIS_PASSWORD=
REDIS_DB=0
REDIS_ENABLE_TLS=false
# -----------------------------------------------------------------------------
# Admin Account
...
...
@@ -61,6 +62,18 @@ ADMIN_PASSWORD=
JWT_SECRET=
JWT_EXPIRE_HOUR=24
# -----------------------------------------------------------------------------
# TOTP (2FA) Configuration
# TOTP(双因素认证)配置
# -----------------------------------------------------------------------------
# IMPORTANT: Set a fixed encryption key for TOTP secrets. If left empty, a
# random key will be generated on each startup, causing all existing TOTP
# configurations to become invalid (users won't be able to login with 2FA).
# Generate a secure key: openssl rand -hex 32
# 重要:设置固定的 TOTP 加密密钥。如果留空,每次启动将生成随机密钥,
# 导致现有的 TOTP 配置失效(用户无法使用双因素认证登录)。
TOTP_ENCRYPTION_KEY=
# -----------------------------------------------------------------------------
# Configuration File (Optional)
# -----------------------------------------------------------------------------
...
...
deploy/.gitignore
0 → 100644
View file @
0170d19f
# =============================================================================
# Sub2API Deploy Directory - Git Ignore
# =============================================================================
# Data directories (generated at runtime when using docker-compose.local.yml)
data/
postgres_data/
redis_data/
# Environment configuration (contains sensitive information)
.env
# Backup files
*.backup
*.bak
# Temporary files
*.tmp
*.log
deploy/README.md
View file @
0170d19f
...
...
@@ -13,7 +13,9 @@ This directory contains files for deploying Sub2API on Linux servers.
| File | Description |
|------|-------------|
|
`docker-compose.yml`
| Docker Compose configuration |
|
`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 |
...
...
@@ -24,7 +26,45 @@ This directory contains files for deploying Sub2API on Linux servers.
## Docker Deployment (Recommended)
### Quick Start
### 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
...
...
@@ -33,18 +73,36 @@ cd sub2api/deploy
# Configure environment
cp
.env.example .env
nano .env
# Set POSTGRES_PASSWORD
(required)
nano .env
# Set POSTGRES_PASSWORD
and other required variables
# Start all services
docker-compose up
-d
# 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 logs
-f
sub2api
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`
:
...
...
@@ -89,6 +147,32 @@ SELECT
### 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
...
...
@@ -115,10 +199,11 @@ docker-compose down -v
| 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 |
|
`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. |
...
...
@@ -127,6 +212,30 @@ docker-compose down -v
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
...
...
@@ -359,6 +468,30 @@ The main config file is at `/etc/sub2api/config.yaml` (created by Setup Wizard).
### 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
...
...
@@ -401,3 +534,60 @@ sudo systemctl status redis
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)
build_image.sh
→
deploy/
build_image.sh
View file @
0170d19f
File moved
deploy/config.example.yaml
View file @
0170d19f
...
...
@@ -210,6 +210,19 @@ gateway:
outbox_backlog_rebuild_rows
:
10000
# 全量重建周期(秒),0 表示禁用
full_rebuild_interval_seconds
:
300
# TLS fingerprint simulation / TLS 指纹伪装
# Default profile "claude_cli_v2" simulates Node.js 20.x
# 默认模板 "claude_cli_v2" 模拟 Node.js 20.x 指纹
tls_fingerprint
:
enabled
:
true
# profiles:
# profile_1:
# 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]
# =============================================================================
# API Key Auth Cache Configuration
...
...
@@ -292,6 +305,27 @@ dashboard_aggregation:
# 日聚合保留天数
daily_days
:
730
# =============================================================================
# Usage Cleanup Task Configuration
# 使用记录清理任务配置(重启生效)
# =============================================================================
usage_cleanup
:
# Enable cleanup task worker
# 启用清理任务执行器
enabled
:
true
# Max date range (days) per task
# 单次任务最大时间跨度(天)
max_range_days
:
31
# Batch delete size
# 单批删除数量
batch_size
:
5000
# Worker interval (seconds)
# 执行器轮询间隔(秒)
worker_interval_seconds
:
10
# Task execution timeout (seconds)
# 单次任务最大执行时长(秒)
task_timeout_seconds
:
1800
# =============================================================================
# Concurrency Wait Configuration
# 并发等待配置
...
...
@@ -342,6 +376,9 @@ redis:
# Database number (0-15)
# 数据库编号(0-15)
db
:
0
# Enable TLS/SSL connection
# 是否启用 TLS/SSL 连接
enable_tls
:
false
# =============================================================================
# Ops Monitoring (Optional)
...
...
@@ -369,6 +406,21 @@ jwt:
# 令牌过期时间(小时,最大 24)
expire_hour
:
24
# =============================================================================
# TOTP (2FA) Configuration
# TOTP 双因素认证配置
# =============================================================================
totp
:
# IMPORTANT: Set a fixed encryption key for TOTP secrets.
# 重要:设置固定的 TOTP 加密密钥。
# If left empty, a random key will be generated on each startup, causing all
# existing TOTP configurations to become invalid (users won't be able to
# login with 2FA).
# 如果留空,每次启动将生成随机密钥,导致现有的 TOTP 配置失效(用户无法使用
# 双因素认证登录)。
# Generate with / 生成命令: openssl rand -hex 32
encryption_key
:
"
"
# =============================================================================
# LinuxDo Connect OAuth Login (SSO)
# LinuxDo Connect OAuth 登录(用于 Sub2API 用户登录)
...
...
deploy/docker-compose.local.yml
0 → 100644
View file @
0170d19f
# =============================================================================
# Sub2API Docker Compose - Local Directory Version
# =============================================================================
# This configuration uses local directories for data storage instead of named
# volumes, making it easy to migrate the entire deployment by simply copying
# the deploy directory.
#
# Quick Start:
# 1. Copy .env.example to .env and configure
# 2. mkdir -p data postgres_data redis_data
# 3. docker-compose -f docker-compose.local.yml up -d
# 4. Check logs: docker-compose -f docker-compose.local.yml logs -f sub2api
# 5. Access: http://localhost:8080
#
# Migration to New Server:
# 1. docker-compose -f docker-compose.local.yml down
# 2. tar czf sub2api-deploy.tar.gz deploy/
# 3. Transfer to new server and extract
# 4. docker-compose -f docker-compose.local.yml up -d
# =============================================================================
services
:
# ===========================================================================
# Sub2API Application
# ===========================================================================
sub2api
:
image
:
weishaw/sub2api:latest
container_name
:
sub2api
restart
:
unless-stopped
ulimits
:
nofile
:
soft
:
100000
hard
:
100000
ports
:
-
"
${BIND_HOST:-0.0.0.0}:${SERVER_PORT:-8080}:8080"
volumes
:
# Local directory mapping for easy migration
-
./data:/app/data
# Optional: Mount custom config.yaml (uncomment and create the file first)
# Copy config.example.yaml to config.yaml, modify it, then uncomment:
# - ./config.yaml:/app/data/config.yaml:ro
environment
:
# =======================================================================
# Auto Setup (REQUIRED for Docker deployment)
# =======================================================================
-
AUTO_SETUP=true
# =======================================================================
# Server Configuration
# =======================================================================
-
SERVER_HOST=0.0.0.0
-
SERVER_PORT=8080
-
SERVER_MODE=${SERVER_MODE:-release}
-
RUN_MODE=${RUN_MODE:-standard}
# =======================================================================
# Database Configuration (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
# =======================================================================
# Redis Configuration
# =======================================================================
-
REDIS_HOST=redis
-
REDIS_PORT=6379
-
REDIS_PASSWORD=${REDIS_PASSWORD:-}
-
REDIS_DB=${REDIS_DB:-0}
-
REDIS_ENABLE_TLS=${REDIS_ENABLE_TLS:-false}
# =======================================================================
# Admin Account (auto-created on first run)
# =======================================================================
-
ADMIN_EMAIL=${ADMIN_EMAIL:-admin@sub2api.local}
-
ADMIN_PASSWORD=${ADMIN_PASSWORD:-}
# =======================================================================
# JWT Configuration
# =======================================================================
# IMPORTANT: Set a fixed JWT_SECRET to prevent login sessions from being
# invalidated after container restarts. If left empty, a random secret
# will be generated on each startup.
# Generate a secure secret: openssl rand -hex 32
-
JWT_SECRET=${JWT_SECRET:-}
-
JWT_EXPIRE_HOUR=${JWT_EXPIRE_HOUR:-24}
# =======================================================================
# TOTP (2FA) Configuration
# =======================================================================
# IMPORTANT: Set a fixed encryption key for TOTP secrets. If left empty,
# a random key will be generated on each startup, causing all existing
# TOTP configurations to become invalid (users won't be able to login
# with 2FA).
# Generate a secure key: openssl rand -hex 32
-
TOTP_ENCRYPTION_KEY=${TOTP_ENCRYPTION_KEY:-}
# =======================================================================
# Timezone Configuration
# This affects ALL time operations in the application:
# - Database timestamps
# - Usage statistics "today" boundary
# - Subscription expiry times
# - Log timestamps
# Common values: Asia/Shanghai, America/New_York, Europe/London, UTC
# =======================================================================
-
TZ=${TZ:-Asia/Shanghai}
# =======================================================================
# Gemini OAuth Configuration (for Gemini accounts)
# =======================================================================
-
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:-}
# =======================================================================
# Security Configuration (URL Allowlist)
# =======================================================================
# Enable URL allowlist validation (false to skip allowlist checks)
-
SECURITY_URL_ALLOWLIST_ENABLED=${SECURITY_URL_ALLOWLIST_ENABLED:-false}
# Allow insecure HTTP URLs when allowlist is disabled (default: false, requires https)
-
SECURITY_URL_ALLOWLIST_ALLOW_INSECURE_HTTP=${SECURITY_URL_ALLOWLIST_ALLOW_INSECURE_HTTP:-false}
# Allow private IP addresses for upstream/pricing/CRS (for internal deployments)
-
SECURITY_URL_ALLOWLIST_ALLOW_PRIVATE_HOSTS=${SECURITY_URL_ALLOWLIST_ALLOW_PRIVATE_HOSTS:-false}
# Upstream hosts whitelist (comma-separated, only used when enabled=true)
-
SECURITY_URL_ALLOWLIST_UPSTREAM_HOSTS=${SECURITY_URL_ALLOWLIST_UPSTREAM_HOSTS:-}
# =======================================================================
# Update Configuration (在线更新配置)
# =======================================================================
# Proxy for accessing GitHub (online updates + pricing data)
# Examples: http://host:port, socks5://host:port
-
UPDATE_PROXY_URL=${UPDATE_PROXY_URL:-}
depends_on
:
postgres
:
condition
:
service_healthy
redis
:
condition
:
service_healthy
networks
:
-
sub2api-network
healthcheck
:
test
:
[
"
CMD"
,
"
curl"
,
"
-f"
,
"
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
:
# Local directory mapping for easy migration
-
./postgres_data:/var/lib/postgresql/data
environment
:
-
POSTGRES_USER=${POSTGRES_USER:-sub2api}
-
POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}
-
POSTGRES_DB=${POSTGRES_DB:-sub2api}
-
PGDATA=/var/lib/postgresql/data
-
TZ=${TZ:-Asia/Shanghai}
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
:
10s
# 注意:不暴露端口到宿主机,应用通过内部网络连接
# 如需调试,可临时添加:ports: ["127.0.0.1:5433:5432"]
# ===========================================================================
# Redis Cache
# ===========================================================================
redis
:
image
:
redis:8-alpine
container_name
:
sub2api-redis
restart
:
unless-stopped
ulimits
:
nofile
:
soft
:
100000
hard
:
100000
volumes
:
# Local directory mapping for easy migration
-
./redis_data:/data
command
:
>
sh -c '
redis-server
--save 60 1
--appendonly yes
--appendfsync everysec
${REDIS_PASSWORD:+--requirepass "$REDIS_PASSWORD"}'
environment
:
-
TZ=${TZ:-Asia/Shanghai}
# REDISCLI_AUTH is used by redis-cli for authentication (safer than -a flag)
-
REDISCLI_AUTH=${REDIS_PASSWORD:-}
networks
:
-
sub2api-network
healthcheck
:
test
:
[
"
CMD"
,
"
redis-cli"
,
"
ping"
]
interval
:
10s
timeout
:
5s
retries
:
5
start_period
:
5s
# =============================================================================
# Networks
# =============================================================================
networks
:
sub2api-network
:
driver
:
bridge
deploy/docker-compose.standalone.yml
View file @
0170d19f
...
...
@@ -56,6 +56,7 @@ services:
-
REDIS_PORT=${REDIS_PORT:-6379}
-
REDIS_PASSWORD=${REDIS_PASSWORD:-}
-
REDIS_DB=${REDIS_DB:-0}
-
REDIS_ENABLE_TLS=${REDIS_ENABLE_TLS:-false}
# =======================================================================
# Admin Account (auto-created on first run)
...
...
Prev
1
…
8
9
10
11
12
13
14
15
16
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment