Skip to content

Commit 78989d6

Browse files
committed
feat(store)!: Lock AuthDir when use gitstore/pgstore
1 parent d6aa1e5 commit 78989d6

File tree

5 files changed

+48
-31
lines changed

5 files changed

+48
-31
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,5 @@ temp/*
1616
cli-proxy-api
1717
static/*
1818
.env
19+
pgstore/*
20+
gitstore/*

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -447,12 +447,12 @@ You can also persist configuration and authentication data in PostgreSQL when ru
447447
| `MANAGEMENT_PASSWORD` | Yes | | Password for the management web UI (required when remote management is enabled). |
448448
| `PGSTORE_DSN` | Yes | | PostgreSQL connection string (e.g. `postgresql://user:pass@host:5432/db`). |
449449
| `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. |
450+
| `PGSTORE_LOCAL_PATH` | 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. |
451451

452452
**How it Works**
453453

454454
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.
455+
2. **Local Mirror:** A writable cache at `<PGSTORE_LOCAL_PATH or CWD>/pgstore` mirrors `config/config.yaml` and `auths/` so the rest of the application can reuse the existing file-based logic.
456456
3. **Bootstrapping:** If no configuration row exists, `config.example.yaml` seeds the database using the fixed identifier `config`.
457457
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.
458458

README_CN.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -460,12 +460,12 @@ openai-compatibility:
460460
| `MANAGEMENT_PASSWORD` | 是 | | 管理面板密码(启用远程管理时必需)。 |
461461
| `PGSTORE_DSN` | 是 | | PostgreSQL 连接串,例如 `postgresql://user:pass@host:5432/db`。 |
462462
| `PGSTORE_SCHEMA` | 否 | public | 创建表时使用的 schema;留空则使用默认 schema。 |
463-
| `PGSTORE_CACHE_DIR` | 否 | 当前工作目录 | 本地镜像根目录,服务将在 `<值>/pgstore` 下写入缓存;若无法获取工作目录则退回 `/tmp/pgstore`。 |
463+
| `PGSTORE_LOCAL_PATH` | 否 | 当前工作目录 | 本地镜像根目录,服务将在 `<值>/pgstore` 下写入缓存;若无法获取工作目录则退回 `/tmp/pgstore`。 |
464464

465465
**工作原理**
466466

467467
1. **初始化:** 启动时通过 `PGSTORE_DSN` 连接数据库,确保 schema 存在,并在缺失时创建 `config_store` 与 `auth_store`。
468-
2. **本地镜像:** 在 `<PGSTORE_CACHE_DIR 或当前工作目录>/pgstore` 下建立可写缓存,复用 `config/config.yaml` 与 `auths/` 目录。
468+
2. **本地镜像:** 在 `<PGSTORE_LOCAL_PATH 或当前工作目录>/pgstore` 下建立可写缓存,复用 `config/config.yaml` 与 `auths/` 目录。
469469
3. **引导:** 若数据库中无配置记录,会使用 `config.example.yaml` 初始化,并以固定标识 `config` 写入。
470470
4. **令牌同步:** 配置与令牌的更改会写入 PostgreSQL,同时数据库中的内容也会反向同步至本地镜像,便于文件监听与管理接口继续工作。
471471

cmd/server/main.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ func main() {
105105
usePostgresStore bool
106106
pgStoreDSN string
107107
pgStoreSchema string
108-
pgStoreCacheDir string
108+
pgStoreLocalPath string
109109
pgStoreInst *store.PostgresStore
110110
gitStoreLocalPath string
111111
useGitStore bool
@@ -139,8 +139,8 @@ func main() {
139139
if value, ok := lookupEnv("PGSTORE_SCHEMA", "pgstore_schema"); ok {
140140
pgStoreSchema = value
141141
}
142-
if value, ok := lookupEnv("PGSTORE_CACHE_DIR", "pgstore_cache_dir"); ok {
143-
pgStoreCacheDir = value
142+
if value, ok := lookupEnv("PGSTORE_LOCAL_PATH", "pgstore_local_path"); ok {
143+
pgStoreLocalPath = value
144144
}
145145
useGitStore = false
146146
}
@@ -169,15 +169,15 @@ func main() {
169169
// Prefer the Postgres store when configured, otherwise fallback to git or local files.
170170
var configFilePath string
171171
if usePostgresStore {
172-
if pgStoreCacheDir == "" {
173-
pgStoreCacheDir = wd
172+
if pgStoreLocalPath == "" {
173+
pgStoreLocalPath = wd
174174
}
175-
pgStoreCacheDir = filepath.Join(pgStoreCacheDir, "pgstore")
175+
pgStoreLocalPath = filepath.Join(pgStoreLocalPath, "pgstore")
176176
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
177177
pgStoreInst, err = store.NewPostgresStore(ctx, store.PostgresStoreConfig{
178178
DSN: pgStoreDSN,
179179
Schema: pgStoreSchema,
180-
SpoolDir: pgStoreCacheDir,
180+
SpoolDir: pgStoreLocalPath,
181181
})
182182
cancel()
183183
if err != nil {

internal/watcher/watcher.go

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -35,25 +35,30 @@ type storePersister interface {
3535
PersistAuthFiles(ctx context.Context, message string, paths ...string) error
3636
}
3737

38+
type authDirProvider interface {
39+
AuthDir() string
40+
}
41+
3842
// Watcher manages file watching for configuration and authentication files
3943
type Watcher struct {
40-
configPath string
41-
authDir string
42-
config *config.Config
43-
clientsMutex sync.RWMutex
44-
reloadCallback func(*config.Config)
45-
watcher *fsnotify.Watcher
46-
lastAuthHashes map[string]string
47-
lastConfigHash string
48-
authQueue chan<- AuthUpdate
49-
currentAuths map[string]*coreauth.Auth
50-
dispatchMu sync.Mutex
51-
dispatchCond *sync.Cond
52-
pendingUpdates map[string]AuthUpdate
53-
pendingOrder []string
54-
dispatchCancel context.CancelFunc
55-
storePersister storePersister
56-
oldConfigYaml []byte
44+
configPath string
45+
authDir string
46+
config *config.Config
47+
clientsMutex sync.RWMutex
48+
reloadCallback func(*config.Config)
49+
watcher *fsnotify.Watcher
50+
lastAuthHashes map[string]string
51+
lastConfigHash string
52+
authQueue chan<- AuthUpdate
53+
currentAuths map[string]*coreauth.Auth
54+
dispatchMu sync.Mutex
55+
dispatchCond *sync.Cond
56+
pendingUpdates map[string]AuthUpdate
57+
pendingOrder []string
58+
dispatchCancel context.CancelFunc
59+
storePersister storePersister
60+
mirroredAuthDir string
61+
oldConfigYaml []byte
5762
}
5863

5964
type stableIDGenerator struct {
@@ -130,6 +135,12 @@ func NewWatcher(configPath, authDir string, reloadCallback func(*config.Config))
130135
w.storePersister = persister
131136
log.Debug("persistence-capable token store detected; watcher will propagate persisted changes")
132137
}
138+
if provider, ok := store.(authDirProvider); ok {
139+
if fixed := strings.TrimSpace(provider.AuthDir()); fixed != "" {
140+
w.mirroredAuthDir = fixed
141+
log.Debugf("mirrored auth directory locked to %s", fixed)
142+
}
143+
}
133144
}
134145
return w, nil
135146
}
@@ -517,10 +528,14 @@ func (w *Watcher) reloadConfig() bool {
517528
return false
518529
}
519530

520-
if resolvedAuthDir, errResolveAuthDir := util.ResolveAuthDir(newConfig.AuthDir); errResolveAuthDir != nil {
521-
log.Errorf("failed to resolve auth directory from config: %v", errResolveAuthDir)
531+
if w.mirroredAuthDir != "" {
532+
newConfig.AuthDir = w.mirroredAuthDir
522533
} else {
523-
newConfig.AuthDir = resolvedAuthDir
534+
if resolvedAuthDir, errResolveAuthDir := util.ResolveAuthDir(newConfig.AuthDir); errResolveAuthDir != nil {
535+
log.Errorf("failed to resolve auth directory from config: %v", errResolveAuthDir)
536+
} else {
537+
newConfig.AuthDir = resolvedAuthDir
538+
}
524539
}
525540

526541
w.clientsMutex.Lock()

0 commit comments

Comments
 (0)