Skip to content

Commit 50c1c50

Browse files
committed
docs: document PostgreSQL-backed config/token store
1 parent 5123cfd commit 50c1c50

File tree

4 files changed

+47
-18
lines changed

4 files changed

+47
-18
lines changed

README.md

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -429,15 +429,33 @@ To enable this feature, set the `GITSTORE_GIT_URL` environment variable to the U
429429
| `GITSTORE_GIT_USERNAME` | No | | The username for Git authentication. |
430430
| `GITSTORE_GIT_TOKEN` | No | | The personal access token (or password) for Git authentication. |
431431

432-
433-
434432
**How it Works**
435433

436434
1. **Cloning:** On startup, the application clones the remote Git repository to the `GITSTORE_LOCAL_PATH`.
437435
2. **Configuration:** It then looks for a `config.yaml` inside a `config` directory within the cloned repository.
438436
3. **Bootstrapping:** If `config/config.yaml` does not exist in the repository, the application will copy the local `config.example.yaml` to that location, commit, and push it to the remote repository as an initial configuration. You must have `config.example.yaml` available.
439437
4. **Token Sync:** The `auth-dir` is also managed within this repository. Any changes to authentication tokens (e.g., through a new login) are automatically committed and pushed to the remote Git repository.
440438

439+
### PostgreSQL-backed Configuration and Token Store
440+
441+
You can also persist configuration and authentication data in PostgreSQL when running CLIProxyAPI in hosted environments that favor managed databases over local files.
442+
443+
**Environment Variables**
444+
445+
| Variable | Required | Default | Description |
446+
|-----------------------|----------|-----------------------|---------------------------------------------------------------------------------------------------------------|
447+
| `MANAGEMENT_PASSWORD` | Yes | | Password for the management web UI (required when remote management is enabled). |
448+
| `PGSTORE_DSN` | Yes | | PostgreSQL connection string (e.g. `postgresql://user:pass@host:5432/db`). |
449+
| `PGSTORE_SCHEMA` | No | public | Schema where the tables will be created. Leave empty to use the default schema. |
450+
| `PGSTORE_CACHE_DIR` | No | Current working directory | Root directory for the local mirror; the server writes to `<value>/pgstore`. If unset and CWD is unavailable, `/tmp/pgstore` is used. |
451+
452+
**How it Works**
453+
454+
1. **Initialization:** On startup the server connects via `PGSTORE_DSN`, ensures the schema exists, and creates the `config_store` / `auth_store` tables when missing.
455+
2. **Local Mirror:** A writable cache at `<PGSTORE_CACHE_DIR or CWD>/pgstore` mirrors `config/config.yaml` and `auths/` so the rest of the application can reuse the existing file-based logic.
456+
3. **Bootstrapping:** If no configuration row exists, `config.example.yaml` seeds the database using the fixed identifier `config`.
457+
4. **Token Sync:** Changes flow both ways—file updates are written to PostgreSQL and database records are mirrored back to disk so watchers and management APIs continue to operate.
458+
441459
### OpenAI Compatibility Providers
442460

443461
Configure upstream OpenAI-compatible providers (e.g., OpenRouter) via `openai-compatibility`.

README_CN.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,26 @@ openai-compatibility:
449449
3. **引导:** 如果仓库中不存在 `config/config.yaml`,应用程序会将本地的 `config.example.yaml` 复制到该位置,然后提交并推送到远程仓库作为初始配置。您必须确保 `config.example.yaml` 文件可用。
450450
4. **令牌同步:** `auth-dir` 也在此仓库中管理。对身份验证令牌的任何更改(例如,通过新的登录)都会自动提交并推送到远程 Git 仓库。
451451

452+
### PostgreSQL 支持的配置与令牌存储
453+
454+
在托管环境中运行服务时,可以选择使用 PostgreSQL 来保存配置与令牌,借助托管数据库减轻本地文件管理压力。
455+
456+
**环境变量**
457+
458+
| 变量 | 必需 | 默认值 | 描述 |
459+
|-------------------------|----|---------------|----------------------------------------------------------------------|
460+
| `MANAGEMENT_PASSWORD` | 是 | | 管理面板密码(启用远程管理时必需)。 |
461+
| `PGSTORE_DSN` | 是 | | PostgreSQL 连接串,例如 `postgresql://user:pass@host:5432/db`。 |
462+
| `PGSTORE_SCHEMA` | 否 | public | 创建表时使用的 schema;留空则使用默认 schema。 |
463+
| `PGSTORE_CACHE_DIR` | 否 | 当前工作目录 | 本地镜像根目录,服务将在 `<值>/pgstore` 下写入缓存;若无法获取工作目录则退回 `/tmp/pgstore`。 |
464+
465+
**工作原理**
466+
467+
1. **初始化:** 启动时通过 `PGSTORE_DSN` 连接数据库,确保 schema 存在,并在缺失时创建 `config_store` 与 `auth_store`。
468+
2. **本地镜像:** 在 `<PGSTORE_CACHE_DIR 或当前工作目录>/pgstore` 下建立可写缓存,复用 `config/config.yaml` 与 `auths/` 目录。
469+
3. **引导:** 若数据库中无配置记录,会使用 `config.example.yaml` 初始化,并以固定标识 `config` 写入。
470+
4. **令牌同步:** 配置与令牌的更改会写入 PostgreSQL,同时数据库中的内容也会反向同步至本地镜像,便于文件监听与管理接口继续工作。
471+
452472
### OpenAI 兼容上游提供商
453473

454474
通过 `openai-compatibility` 配置上游 OpenAI 兼容提供商(例如 OpenRouter)。

cmd/server/main.go

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,6 @@ func main() {
105105
usePostgresStore bool
106106
pgStoreDSN string
107107
pgStoreSchema string
108-
pgStoreConfigKey string
109108
pgStoreCacheDir string
110109
pgStoreInst *store.PostgresStore
111110
gitStoreLocalPath string
@@ -140,9 +139,6 @@ func main() {
140139
if value, ok := lookupEnv("PGSTORE_SCHEMA", "pgstore_schema"); ok {
141140
pgStoreSchema = value
142141
}
143-
if value, ok := lookupEnv("PGSTORE_CONFIG_KEY", "pgstore_config_key"); ok {
144-
pgStoreConfigKey = value
145-
}
146142
if value, ok := lookupEnv("PGSTORE_CACHE_DIR", "pgstore_cache_dir"); ok {
147143
pgStoreCacheDir = value
148144
}
@@ -179,10 +175,9 @@ func main() {
179175
pgStoreCacheDir = filepath.Join(pgStoreCacheDir, "pgstore")
180176
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
181177
pgStoreInst, err = store.NewPostgresStore(ctx, store.PostgresStoreConfig{
182-
DSN: pgStoreDSN,
183-
Schema: pgStoreSchema,
184-
ConfigKey: pgStoreConfigKey,
185-
SpoolDir: pgStoreCacheDir,
178+
DSN: pgStoreDSN,
179+
Schema: pgStoreSchema,
180+
SpoolDir: pgStoreCacheDir,
186181
})
187182
cancel()
188183
if err != nil {

internal/store/postgresstore.go

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import (
2222
const (
2323
defaultConfigTable = "config_store"
2424
defaultAuthTable = "auth_store"
25-
defaultConfigKey = "default"
25+
defaultConfigKey = "config"
2626
)
2727

2828
// PostgresStoreConfig captures configuration required to initialize a Postgres-backed store.
@@ -31,7 +31,6 @@ type PostgresStoreConfig struct {
3131
Schema string
3232
ConfigTable string
3333
AuthTable string
34-
ConfigKey string
3534
SpoolDir string
3635
}
3736

@@ -59,9 +58,6 @@ func NewPostgresStore(ctx context.Context, cfg PostgresStoreConfig) (*PostgresSt
5958
if cfg.AuthTable == "" {
6059
cfg.AuthTable = defaultAuthTable
6160
}
62-
if cfg.ConfigKey == "" {
63-
cfg.ConfigKey = defaultConfigKey
64-
}
6561

6662
spoolRoot := strings.TrimSpace(cfg.SpoolDir)
6763
if spoolRoot == "" {
@@ -399,7 +395,7 @@ func (s *PostgresStore) PersistConfig(ctx context.Context) error {
399395
func (s *PostgresStore) syncConfigFromDatabase(ctx context.Context, exampleConfigPath string) error {
400396
query := fmt.Sprintf("SELECT content FROM %s WHERE id = $1", s.fullTableName(s.cfg.ConfigTable))
401397
var content string
402-
err := s.db.QueryRowContext(ctx, query, s.cfg.ConfigKey).Scan(&content)
398+
err := s.db.QueryRowContext(ctx, query, defaultConfigKey).Scan(&content)
403399
switch {
404400
case errors.Is(err, sql.ErrNoRows):
405401
if _, errStat := os.Stat(s.configPath); errors.Is(errStat, fs.ErrNotExist) {
@@ -532,15 +528,15 @@ func (s *PostgresStore) persistConfig(ctx context.Context, data []byte) error {
532528
ON CONFLICT (id)
533529
DO UPDATE SET content = EXCLUDED.content, updated_at = NOW()
534530
`, s.fullTableName(s.cfg.ConfigTable))
535-
if _, err := s.db.ExecContext(ctx, query, s.cfg.ConfigKey, string(data)); err != nil {
531+
if _, err := s.db.ExecContext(ctx, query, defaultConfigKey, string(data)); err != nil {
536532
return fmt.Errorf("postgres store: upsert config: %w", err)
537533
}
538534
return nil
539535
}
540536

541537
func (s *PostgresStore) deleteConfigRecord(ctx context.Context) error {
542538
query := fmt.Sprintf("DELETE FROM %s WHERE id = $1", s.fullTableName(s.cfg.ConfigTable))
543-
if _, err := s.db.ExecContext(ctx, query, s.cfg.ConfigKey); err != nil {
539+
if _, err := s.db.ExecContext(ctx, query, defaultConfigKey); err != nil {
544540
return fmt.Errorf("postgres store: delete config: %w", err)
545541
}
546542
return nil

0 commit comments

Comments
 (0)