Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ docker run --name new-api -d --restart always \
| `CRYPTO_SECRET` | Encryption secret (required for Redis) | - |
| `SQL_DSN` | Database connection string | - |
| `REDIS_CONN_STRING` | Redis connection string | - |
| `SERVER_ADDRESS` | First-run seed for the server address. Used only when the database has no `ServerAddress` option; existing admin settings are not overwritten | - |
| `STREAMING_TIMEOUT` | Streaming timeout (seconds) | `300` |
| `STREAM_SCANNER_MAX_BUFFER_MB` | Max per-line buffer (MB) for the stream scanner; increase when upstream sends huge image/base64 payloads | `64` |
| `MAX_REQUEST_BODY_MB` | Max request body size (MB, counted **after decompression**; prevents huge requests/zip bombs from exhausting memory). Exceeding it returns `413` | `32` |
Expand Down
1 change: 1 addition & 0 deletions README.fr.md
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ docker run --name new-api -d --restart always \
| `CRYPTO_SECRET` | Secret de chiffrement (requis pour Redis) | - |
| `SQL_DSN` | Chaine de connexion à la base de données | - |
| `REDIS_CONN_STRING` | Chaine de connexion Redis | - |
| `SERVER_ADDRESS` | Valeur initiale de l'adresse du serveur au premier démarrage. Utilisée uniquement si l'option `ServerAddress` n'existe pas en base ; les réglages administrateur existants ne sont pas écrasés | - |
| `STREAMING_TIMEOUT` | Délai d'expiration du streaming (secondes) | `300` |
| `STREAM_SCANNER_MAX_BUFFER_MB` | Taille max du buffer par ligne (Mo) pour le scanner SSE ; à augmenter quand les sorties image/base64 sont très volumineuses (ex. images 4K) | `64` |
| `MAX_REQUEST_BODY_MB` | Taille maximale du corps de requête (Mo, comptée **après décompression** ; évite les requêtes énormes/zip bombs qui saturent la mémoire). Dépassement ⇒ `413` | `32` |
Expand Down
1 change: 1 addition & 0 deletions README.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ docker run --name new-api -d --restart always \
| `CRYPTO_SECRET` | 暗号化シークレット(Redisに必須) | - |
| `SQL_DSN** | データベース接続文字列 | - |
| `REDIS_CONN_STRING` | Redis接続文字列 | - |
| `SERVER_ADDRESS` | サーバーアドレスの初回起動シード。データベースに `ServerAddress` オプションがない場合のみ書き込み、既存の管理者設定は上書きしません | - |
| `STREAMING_TIMEOUT` | ストリーミング応答のタイムアウト時間(秒) | `300` |
| `STREAM_SCANNER_MAX_BUFFER_MB` | ストリームスキャナの1行あたりバッファ上限(MB)。4K画像など巨大なbase64 `data:` ペイロードを扱う場合は値を増加させてください | `64` |
| `MAX_REQUEST_BODY_MB` | リクエストボディ最大サイズ(MB、**解凍後**に計測。巨大リクエスト/zip bomb によるメモリ枯渇を防止)。超過時は `413` | `32` |
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ docker run --name new-api -d --restart always \
| `CRYPTO_SECRET` | Encryption secret (required for Redis) | - |
| `SQL_DSN` | Database connection string | - |
| `REDIS_CONN_STRING` | Redis connection string | - |
| `SERVER_ADDRESS` | First-run seed for the server address. Used only when the database has no `ServerAddress` option; existing admin settings are not overwritten | - |
| `RELAY_IDLE_CONN_TIMEOUT` | Idle keep-alive timeout for relay HTTP clients, seconds. Defaults to Go standard library behavior; set `0` to disable | `90` |
| `STREAMING_TIMEOUT` | Streaming timeout (seconds) | `300` |
| `STREAM_SCANNER_MAX_BUFFER_MB` | Max per-line buffer (MB) for the stream scanner; increase when upstream sends huge image/base64 payloads | `64` |
Expand Down
1 change: 1 addition & 0 deletions README.zh_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ docker run --name new-api -d --restart always \
| `CRYPTO_SECRET` | 加密密钥(Redis 必须) | - |
| `SQL_DSN` | 数据库连接字符串 | - |
| `REDIS_CONN_STRING` | Redis 连接字符串 | - |
| `SERVER_ADDRESS` | 服务器地址的首次启动种子;仅当数据库中没有 `ServerAddress` 配置项时写入,已有管理员配置不会被覆盖 | - |
| `STREAMING_TIMEOUT` | 流式超时时间(秒) | `300` |
| `STREAM_SCANNER_MAX_BUFFER_MB` | 流式扫描器单行最大缓冲(MB),图像生成等超大 `data:` 片段(如 4K 图片 base64)需适当调大 | `64` |
| `MAX_REQUEST_BODY_MB` | 请求体最大大小(MB,**解压后**计;防止超大请求/zip bomb 导致内存暴涨),超过将返回 `413` | `32` |
Expand Down
1 change: 1 addition & 0 deletions README.zh_TW.md
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ docker run --name new-api -d --restart always \
| `CRYPTO_SECRET` | 加密密鑰(Redis 必須) | - |
| `SQL_DSN` | 資料庫連接字符串 | - |
| `REDIS_CONN_STRING` | Redis 連接字符串 | - |
| `SERVER_ADDRESS` | 伺服器地址的首次啟動種子;僅當資料庫中沒有 `ServerAddress` 設定項時寫入,既有管理員設定不會被覆蓋 | - |
| `STREAMING_TIMEOUT` | 流式超時時間(秒) | `300` |
| `STREAM_SCANNER_MAX_BUFFER_MB` | 流式掃描器單行最大緩衝(MB),圖像生成等超大 `data:` 片段(如 4K 圖片 base64)需適當調大 | `64` |
| `MAX_REQUEST_BODY_MB` | 請求體最大大小(MB,**解壓縮後**計;防止超大請求/zip bomb 導致記憶體暴漲),超過將返回 `413` | `32` |
Expand Down
1 change: 1 addition & 0 deletions docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ services:
environment:
- SQL_DSN=postgresql://root:123456@postgres:5432/new-api
- REDIS_CONN_STRING=redis://redis
# - SERVER_ADDRESS=http://localhost:3000 # Optional first-run seed; existing DB/admin setting wins
- TZ=Asia/Shanghai
- BATCH_UPDATE_ENABLED=true
depends_on:
Expand Down
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ services:
# - LOG_SQL_DSN=clickhouse://default:123456@clickhouse:9000/new_api_logs # OPTIONAL: Use ClickHouse for logs only; also uncomment clickhouse in depends_on and the clickhouse service below
# - LOG_SQL_CLICKHOUSE_TTL_DAYS=0 # OPTIONAL: ClickHouse log retention days. Unset or 0 disables automatic deletion; set to e.g. 30 to keep 30 days
- REDIS_CONN_STRING=redis://:123456@redis:6379 # ⚠️ IMPORTANT: Change the password in production!
# - SERVER_ADDRESS=https://your-domain.example.com # OPTIONAL: First-run seed for server address; DB/admin setting wins after it exists
- TZ=Asia/Shanghai
- ERROR_LOG_ENABLED=true # 是否启用错误日志记录 (Whether to enable error log recording)
- BATCH_UPDATE_ENABLED=true # 是否启用批量更新 (Whether to enable batch update)
Expand Down
2 changes: 1 addition & 1 deletion docs/installation/BT.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ docker-compose up -d
| `CRYPTO_SECRET` | 加密密钥,使用 Redis 时必填 | 条件必填 |
| `SQL_DSN` | 数据库连接字符串(使用外部数据库时) | 可选 |
| `REDIS_CONN_STRING` | Redis 连接字符串 | 可选 |
| `SERVER_ADDRESS` | 首次启动时写入服务器地址;仅在数据库没有 `ServerAddress` 配置项时生效 | 可选 |

### 生成随机密钥

Expand Down Expand Up @@ -148,4 +149,3 @@ docker-compose down && docker-compose up -d
![宝塔面板 Docker 安装](https://github.com/user-attachments/assets/7a6fc03e-c457-45e4-b8f9-184508fc26b0)

> ⚠️ 注意:密钥为环境变量 `SESSION_SECRET`,请务必设置!

19 changes: 18 additions & 1 deletion model/option.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package model

import (
"os"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -187,13 +188,29 @@ func InitOptionMap() {
}

func loadOptionsFromDatabase() {
options, _ := AllOption()
options, err := AllOption()
if err != nil {
common.SysLog("failed to load options from database: " + err.Error())
return
}
serverAddressExists := false
for _, option := range options {
if option.Key == "ServerAddress" {
serverAddressExists = true
}
err := updateOptionMap(option.Key, option.Value)
if err != nil {
common.SysLog("failed to update option map: " + err.Error())
}
}
if !serverAddressExists {
serverAddress := strings.TrimSpace(os.Getenv("SERVER_ADDRESS"))
if serverAddress != "" {
if err := UpdateOption("ServerAddress", serverAddress); err != nil {
common.SysLog("failed to seed ServerAddress from SERVER_ADDRESS: " + err.Error())
}
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

func SyncOptions(frequency int) {
Expand Down
79 changes: 79 additions & 0 deletions model/option_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package model

import (
"testing"

"github.com/QuantumNous/new-api/common"
"github.com/QuantumNous/new-api/setting/system_setting"
"github.com/glebarez/sqlite"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gorm.io/gorm"
)

func setupOptionTestDB(t *testing.T) {
t.Helper()

previousDB := DB
previousLogDB := LOG_DB
previousServerAddress := system_setting.ServerAddress

common.OptionMapRWMutex.RLock()
previousOptionMap := cloneOptionMap(common.OptionMap)
common.OptionMapRWMutex.RUnlock()

db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
require.NoError(t, err)
require.NoError(t, db.AutoMigrate(&Option{}))

DB = db
LOG_DB = db

t.Cleanup(func() {
if sqlDB, err := db.DB(); err == nil {
_ = sqlDB.Close()
}
DB = previousDB
LOG_DB = previousLogDB
system_setting.ServerAddress = previousServerAddress
common.OptionMapRWMutex.Lock()
common.OptionMap = cloneOptionMap(previousOptionMap)
common.OptionMapRWMutex.Unlock()
})
}

func cloneOptionMap(source map[string]string) map[string]string {
if source == nil {
return nil
}
cloned := make(map[string]string, len(source))
for key, value := range source {
cloned[key] = value
}
return cloned
}

func TestInitOptionMapSeedsServerAddressFromEnvWhenMissing(t *testing.T) {
setupOptionTestDB(t)
t.Setenv("SERVER_ADDRESS", "https://example.com")

InitOptionMap()

var option Option
require.NoError(t, DB.Where(&Option{Key: "ServerAddress"}).First(&option).Error)
assert.Equal(t, "https://example.com", option.Value)
assert.Equal(t, "https://example.com", system_setting.ServerAddress)
}

func TestInitOptionMapDoesNotOverrideExistingServerAddress(t *testing.T) {
setupOptionTestDB(t)
t.Setenv("SERVER_ADDRESS", "https://env.example.com")
require.NoError(t, DB.Create(&Option{Key: "ServerAddress", Value: "https://db.example.com"}).Error)

InitOptionMap()

var option Option
require.NoError(t, DB.Where(&Option{Key: "ServerAddress"}).First(&option).Error)
assert.Equal(t, "https://db.example.com", option.Value)
assert.Equal(t, "https://db.example.com", system_setting.ServerAddress)
}