mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-01 23:15:27 +08:00
feat(ui): show env mode persistence status and document writeback
This commit is contained in:
@@ -587,6 +587,9 @@ Returns sanitized config.
|
||||
{
|
||||
"keys": ["k1", "k2"],
|
||||
"env_backed": false,
|
||||
"env_source_present": true,
|
||||
"env_writeback_enabled": true,
|
||||
"config_path": "/data/config.json",
|
||||
"accounts": [
|
||||
{
|
||||
"identifier": "user@example.com",
|
||||
|
||||
3
API.md
3
API.md
@@ -596,6 +596,9 @@ data: {"type":"message_stop"}
|
||||
{
|
||||
"keys": ["k1", "k2"],
|
||||
"env_backed": false,
|
||||
"env_source_present": true,
|
||||
"env_writeback_enabled": true,
|
||||
"config_path": "/data/config.json",
|
||||
"accounts": [
|
||||
{
|
||||
"identifier": "user@example.com",
|
||||
|
||||
@@ -320,6 +320,7 @@ cp opencode.json.example opencode.json
|
||||
| `DS2API_CONFIG_PATH` | 配置文件路径 | `config.json` |
|
||||
| `DS2API_CONFIG_JSON` | 直接注入配置(JSON 或 Base64) | — |
|
||||
| `CONFIG_JSON` | 旧版兼容配置注入 | — |
|
||||
| `DS2API_ENV_WRITEBACK` | 环境变量模式下自动写回配置文件并切换文件模式(`1/true/yes/on`) | 关闭 |
|
||||
| `DS2API_WASM_PATH` | PoW WASM 文件路径 | 自动查找 |
|
||||
| `DS2API_STATIC_ADMIN_DIR` | 管理台静态文件目录 | `static/admin` |
|
||||
| `DS2API_AUTO_BUILD_WEBUI` | 启动时自动构建 WebUI | 本地开启,Vercel 关闭 |
|
||||
@@ -342,6 +343,8 @@ cp opencode.json.example opencode.json
|
||||
| `VERCEL_TEAM_ID` | Vercel 团队 ID | — |
|
||||
| `DS2API_VERCEL_PROTECTION_BYPASS` | Vercel 部署保护绕过密钥(内部 Node→Go 调用) | — |
|
||||
|
||||
> 提示:当检测到 `DS2API_CONFIG_JSON/CONFIG_JSON` 时,管理台会显示当前模式风险与自动持久化状态(含 `DS2API_CONFIG_PATH` 路径与模式切换说明)。
|
||||
|
||||
## 鉴权模式
|
||||
|
||||
调用业务接口(`/v1/*`、`/anthropic/*`、Gemini 路由)时支持两种模式:
|
||||
|
||||
@@ -320,6 +320,7 @@ cp opencode.json.example opencode.json
|
||||
| `DS2API_CONFIG_PATH` | Config file path | `config.json` |
|
||||
| `DS2API_CONFIG_JSON` | Inline config (JSON or Base64) | — |
|
||||
| `CONFIG_JSON` | Legacy compatibility config input | — |
|
||||
| `DS2API_ENV_WRITEBACK` | Auto-write env-backed config to file and transition to file mode (`1/true/yes/on`) | Disabled |
|
||||
| `DS2API_WASM_PATH` | PoW WASM file path | Auto-detect |
|
||||
| `DS2API_STATIC_ADMIN_DIR` | Admin static assets dir | `static/admin` |
|
||||
| `DS2API_AUTO_BUILD_WEBUI` | Auto-build WebUI on startup | Enabled locally, disabled on Vercel |
|
||||
@@ -339,6 +340,8 @@ cp opencode.json.example opencode.json
|
||||
| `VERCEL_TEAM_ID` | Vercel team ID | — |
|
||||
| `DS2API_VERCEL_PROTECTION_BYPASS` | Vercel deployment protection bypass for internal Node→Go calls | — |
|
||||
|
||||
> Note: when `DS2API_CONFIG_JSON/CONFIG_JSON` is detected, the Admin UI shows mode risk and auto-persistence status (including `DS2API_CONFIG_PATH` and mode-transition hints).
|
||||
|
||||
## Authentication Modes
|
||||
|
||||
For business endpoints (`/v1/*`, `/anthropic/*`, Gemini routes), DS2API supports two modes:
|
||||
|
||||
@@ -248,6 +248,7 @@ VERCEL_TEAM_ID=team_xxxxxxxxxxxx # optional for personal accounts
|
||||
| `DS2API_ACCOUNT_QUEUE_SIZE` | Alias (legacy compat) | — |
|
||||
| `DS2API_GLOBAL_MAX_INFLIGHT` | Global inflight limit | `recommended_concurrency` |
|
||||
| `DS2API_MAX_INFLIGHT` | Alias (legacy compat) | — |
|
||||
| `DS2API_ENV_WRITEBACK` | When `DS2API_CONFIG_JSON` is present, auto-write to `DS2API_CONFIG_PATH` and switch to file-backed mode after success (`1/true/yes/on`) | Disabled |
|
||||
| `DS2API_VERCEL_INTERNAL_SECRET` | Hybrid streaming internal auth | Falls back to `DS2API_ADMIN_KEY` |
|
||||
| `DS2API_VERCEL_STREAM_LEASE_TTL_SECONDS` | Stream lease TTL | `900` |
|
||||
| `VERCEL_TOKEN` | Vercel sync token | — |
|
||||
|
||||
@@ -248,6 +248,7 @@ VERCEL_TEAM_ID=team_xxxxxxxxxxxx # 个人账号可留空
|
||||
| `DS2API_ACCOUNT_QUEUE_SIZE` | 同上(兼容别名) | — |
|
||||
| `DS2API_GLOBAL_MAX_INFLIGHT` | 全局并发上限 | `recommended_concurrency` |
|
||||
| `DS2API_MAX_INFLIGHT` | 同上(兼容别名) | — |
|
||||
| `DS2API_ENV_WRITEBACK` | 检测到 `DS2API_CONFIG_JSON` 时自动写入 `DS2API_CONFIG_PATH`,并在成功后转为文件模式(`1/true/yes/on`) | 关闭 |
|
||||
| `DS2API_VERCEL_INTERNAL_SECRET` | 混合流式内部鉴权 | 回退用 `DS2API_ADMIN_KEY` |
|
||||
| `DS2API_VERCEL_STREAM_LEASE_TTL_SECONDS` | 流式 lease TTL | `900` |
|
||||
| `VERCEL_TOKEN` | Vercel 同步 token | — |
|
||||
|
||||
@@ -21,6 +21,9 @@ type ConfigStore interface {
|
||||
Update(mutator func(*config.Config) error) error
|
||||
ExportJSONAndBase64() (string, string, error)
|
||||
IsEnvBacked() bool
|
||||
IsEnvWritebackEnabled() bool
|
||||
HasEnvConfigSource() bool
|
||||
ConfigPath() string
|
||||
SetVercelSync(hash string, ts int64) error
|
||||
AdminPasswordHash() string
|
||||
AdminJWTExpireHours() int
|
||||
|
||||
@@ -8,9 +8,12 @@ import (
|
||||
func (h *Handler) getConfig(w http.ResponseWriter, _ *http.Request) {
|
||||
snap := h.Store.Snapshot()
|
||||
safe := map[string]any{
|
||||
"keys": snap.Keys,
|
||||
"accounts": []map[string]any{},
|
||||
"env_backed": h.Store.IsEnvBacked(),
|
||||
"keys": snap.Keys,
|
||||
"accounts": []map[string]any{},
|
||||
"env_backed": h.Store.IsEnvBacked(),
|
||||
"env_source_present": h.Store.HasEnvConfigSource(),
|
||||
"env_writeback_enabled": h.Store.IsEnvWritebackEnabled(),
|
||||
"config_path": h.Store.ConfigPath(),
|
||||
"claude_mapping": func() map[string]string {
|
||||
if len(snap.ClaudeMapping) > 0 {
|
||||
return snap.ClaudeMapping
|
||||
|
||||
@@ -267,6 +267,22 @@ func (s *Store) IsEnvBacked() bool {
|
||||
return s.fromEnv
|
||||
}
|
||||
|
||||
func (s *Store) IsEnvWritebackEnabled() bool {
|
||||
return envWritebackEnabled()
|
||||
}
|
||||
|
||||
func (s *Store) HasEnvConfigSource() bool {
|
||||
rawCfg := strings.TrimSpace(os.Getenv("DS2API_CONFIG_JSON"))
|
||||
if rawCfg == "" {
|
||||
rawCfg = strings.TrimSpace(os.Getenv("CONFIG_JSON"))
|
||||
}
|
||||
return rawCfg != ""
|
||||
}
|
||||
|
||||
func (s *Store) ConfigPath() string {
|
||||
return s.path
|
||||
}
|
||||
|
||||
func (s *Store) SetVercelSync(hash string, ts int64) error {
|
||||
return s.Update(func(c *Config) error {
|
||||
c.VercelSyncHash = hash
|
||||
|
||||
@@ -64,6 +64,27 @@ export default function AccountManagerContainer({ config, onRefresh, onMessage,
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{Boolean(config?.env_source_present) && (
|
||||
<div className={`rounded-xl border px-4 py-3 text-sm ${
|
||||
config?.env_writeback_enabled
|
||||
? (config?.env_backed ? 'border-amber-500/30 bg-amber-500/10 text-amber-600' : 'border-emerald-500/30 bg-emerald-500/10 text-emerald-600')
|
||||
: 'border-amber-500/30 bg-amber-500/10 text-amber-600'
|
||||
}`}>
|
||||
<p className="font-medium">
|
||||
{config?.env_writeback_enabled
|
||||
? (config?.env_backed
|
||||
? t('accountManager.envModeWritebackPendingTitle')
|
||||
: t('accountManager.envModeWritebackActiveTitle'))
|
||||
: t('accountManager.envModeRiskTitle')}
|
||||
</p>
|
||||
<p className="mt-1 text-xs opacity-90">
|
||||
{config?.env_writeback_enabled
|
||||
? t('accountManager.envModeWritebackDesc', { path: config?.config_path || 'config.json' })
|
||||
: t('accountManager.envModeRiskDesc')}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<QueueCards queueStatus={queueStatus} t={t} />
|
||||
|
||||
<ApiKeysPanel
|
||||
|
||||
@@ -138,7 +138,12 @@
|
||||
"sessionCount": "Sessions: {count}",
|
||||
"deleteAllSessions": "Delete all sessions",
|
||||
"deleteAllSessionsConfirm": "Are you sure you want to delete all sessions for this account? This action cannot be undone.",
|
||||
"deleteAllSessionsSuccess": "Successfully deleted all sessions"
|
||||
"deleteAllSessionsSuccess": "Successfully deleted all sessions",
|
||||
"envModeRiskTitle": "Environment-variable config mode detected (persistence risk)",
|
||||
"envModeRiskDesc": "Detected DS2API_CONFIG_JSON/CONFIG_JSON. If DS2API_ENV_WRITEBACK is not enabled, Admin UI edits are in-memory only and may be lost after restart.",
|
||||
"envModeWritebackPendingTitle": "Env mode + auto-persistence enabled (pending file handoff)",
|
||||
"envModeWritebackActiveTitle": "Env mode + auto-persistence active",
|
||||
"envModeWritebackDesc": "The app will auto-create/write the config file and transition to file-backed mode. Current persistence path: {path}"
|
||||
},
|
||||
"apiTester": {
|
||||
"defaultMessage": "Hello, please introduce yourself in one sentence.",
|
||||
|
||||
@@ -138,7 +138,12 @@
|
||||
"sessionCount": "会话: {count}",
|
||||
"deleteAllSessions": "删除所有会话",
|
||||
"deleteAllSessionsConfirm": "确定要删除该账号的所有会话吗?此操作不可恢复。",
|
||||
"deleteAllSessionsSuccess": "删除成功"
|
||||
"deleteAllSessionsSuccess": "删除成功",
|
||||
"envModeRiskTitle": "当前为环境变量配置模式(有持久化风险)",
|
||||
"envModeRiskDesc": "检测到 DS2API_CONFIG_JSON/CONFIG_JSON。若未开启 DS2API_ENV_WRITEBACK,管理台改动仅在内存生效,重启可能丢失。",
|
||||
"envModeWritebackPendingTitle": "环境变量模式 + 自动持久化已开启(等待落盘)",
|
||||
"envModeWritebackActiveTitle": "环境变量模式 + 自动持久化已生效",
|
||||
"envModeWritebackDesc": "程序会自动创建/写入配置文件并在后续切换为文件模式。当前持久化路径:{path}"
|
||||
},
|
||||
"apiTester": {
|
||||
"defaultMessage": "你好,请用一句话介绍你自己。",
|
||||
|
||||
Reference in New Issue
Block a user