mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-03 04:50:52 +08:00
feat(store)!: Lock AuthDir when use gitstore/pgstore
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -16,3 +16,5 @@ temp/*
|
||||
cli-proxy-api
|
||||
static/*
|
||||
.env
|
||||
pgstore/*
|
||||
gitstore/*
|
||||
@@ -447,12 +447,12 @@ You can also persist configuration and authentication data in PostgreSQL when ru
|
||||
| `MANAGEMENT_PASSWORD` | Yes | | Password for the management web UI (required when remote management is enabled). |
|
||||
| `PGSTORE_DSN` | Yes | | PostgreSQL connection string (e.g. `postgresql://user:pass@host:5432/db`). |
|
||||
| `PGSTORE_SCHEMA` | No | public | Schema where the tables will be created. Leave empty to use the default schema. |
|
||||
| `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. |
|
||||
| `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. |
|
||||
|
||||
**How it Works**
|
||||
|
||||
1. **Initialization:** On startup the server connects via `PGSTORE_DSN`, ensures the schema exists, and creates the `config_store` / `auth_store` tables when missing.
|
||||
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.
|
||||
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.
|
||||
3. **Bootstrapping:** If no configuration row exists, `config.example.yaml` seeds the database using the fixed identifier `config`.
|
||||
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.
|
||||
|
||||
|
||||
@@ -460,12 +460,12 @@ openai-compatibility:
|
||||
| `MANAGEMENT_PASSWORD` | 是 | | 管理面板密码(启用远程管理时必需)。 |
|
||||
| `PGSTORE_DSN` | 是 | | PostgreSQL 连接串,例如 `postgresql://user:pass@host:5432/db`。 |
|
||||
| `PGSTORE_SCHEMA` | 否 | public | 创建表时使用的 schema;留空则使用默认 schema。 |
|
||||
| `PGSTORE_CACHE_DIR` | 否 | 当前工作目录 | 本地镜像根目录,服务将在 `<值>/pgstore` 下写入缓存;若无法获取工作目录则退回 `/tmp/pgstore`。 |
|
||||
| `PGSTORE_LOCAL_PATH` | 否 | 当前工作目录 | 本地镜像根目录,服务将在 `<值>/pgstore` 下写入缓存;若无法获取工作目录则退回 `/tmp/pgstore`。 |
|
||||
|
||||
**工作原理**
|
||||
|
||||
1. **初始化:** 启动时通过 `PGSTORE_DSN` 连接数据库,确保 schema 存在,并在缺失时创建 `config_store` 与 `auth_store`。
|
||||
2. **本地镜像:** 在 `<PGSTORE_CACHE_DIR 或当前工作目录>/pgstore` 下建立可写缓存,复用 `config/config.yaml` 与 `auths/` 目录。
|
||||
2. **本地镜像:** 在 `<PGSTORE_LOCAL_PATH 或当前工作目录>/pgstore` 下建立可写缓存,复用 `config/config.yaml` 与 `auths/` 目录。
|
||||
3. **引导:** 若数据库中无配置记录,会使用 `config.example.yaml` 初始化,并以固定标识 `config` 写入。
|
||||
4. **令牌同步:** 配置与令牌的更改会写入 PostgreSQL,同时数据库中的内容也会反向同步至本地镜像,便于文件监听与管理接口继续工作。
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ func main() {
|
||||
usePostgresStore bool
|
||||
pgStoreDSN string
|
||||
pgStoreSchema string
|
||||
pgStoreCacheDir string
|
||||
pgStoreLocalPath string
|
||||
pgStoreInst *store.PostgresStore
|
||||
gitStoreLocalPath string
|
||||
useGitStore bool
|
||||
@@ -139,8 +139,8 @@ func main() {
|
||||
if value, ok := lookupEnv("PGSTORE_SCHEMA", "pgstore_schema"); ok {
|
||||
pgStoreSchema = value
|
||||
}
|
||||
if value, ok := lookupEnv("PGSTORE_CACHE_DIR", "pgstore_cache_dir"); ok {
|
||||
pgStoreCacheDir = value
|
||||
if value, ok := lookupEnv("PGSTORE_LOCAL_PATH", "pgstore_local_path"); ok {
|
||||
pgStoreLocalPath = value
|
||||
}
|
||||
useGitStore = false
|
||||
}
|
||||
@@ -169,15 +169,15 @@ func main() {
|
||||
// Prefer the Postgres store when configured, otherwise fallback to git or local files.
|
||||
var configFilePath string
|
||||
if usePostgresStore {
|
||||
if pgStoreCacheDir == "" {
|
||||
pgStoreCacheDir = wd
|
||||
if pgStoreLocalPath == "" {
|
||||
pgStoreLocalPath = wd
|
||||
}
|
||||
pgStoreCacheDir = filepath.Join(pgStoreCacheDir, "pgstore")
|
||||
pgStoreLocalPath = filepath.Join(pgStoreLocalPath, "pgstore")
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
pgStoreInst, err = store.NewPostgresStore(ctx, store.PostgresStoreConfig{
|
||||
DSN: pgStoreDSN,
|
||||
Schema: pgStoreSchema,
|
||||
SpoolDir: pgStoreCacheDir,
|
||||
SpoolDir: pgStoreLocalPath,
|
||||
})
|
||||
cancel()
|
||||
if err != nil {
|
||||
|
||||
@@ -35,6 +35,10 @@ type storePersister interface {
|
||||
PersistAuthFiles(ctx context.Context, message string, paths ...string) error
|
||||
}
|
||||
|
||||
type authDirProvider interface {
|
||||
AuthDir() string
|
||||
}
|
||||
|
||||
// Watcher manages file watching for configuration and authentication files
|
||||
type Watcher struct {
|
||||
configPath string
|
||||
@@ -53,6 +57,7 @@ type Watcher struct {
|
||||
pendingOrder []string
|
||||
dispatchCancel context.CancelFunc
|
||||
storePersister storePersister
|
||||
mirroredAuthDir string
|
||||
oldConfigYaml []byte
|
||||
}
|
||||
|
||||
@@ -130,6 +135,12 @@ func NewWatcher(configPath, authDir string, reloadCallback func(*config.Config))
|
||||
w.storePersister = persister
|
||||
log.Debug("persistence-capable token store detected; watcher will propagate persisted changes")
|
||||
}
|
||||
if provider, ok := store.(authDirProvider); ok {
|
||||
if fixed := strings.TrimSpace(provider.AuthDir()); fixed != "" {
|
||||
w.mirroredAuthDir = fixed
|
||||
log.Debugf("mirrored auth directory locked to %s", fixed)
|
||||
}
|
||||
}
|
||||
}
|
||||
return w, nil
|
||||
}
|
||||
@@ -517,11 +528,15 @@ func (w *Watcher) reloadConfig() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
if w.mirroredAuthDir != "" {
|
||||
newConfig.AuthDir = w.mirroredAuthDir
|
||||
} else {
|
||||
if resolvedAuthDir, errResolveAuthDir := util.ResolveAuthDir(newConfig.AuthDir); errResolveAuthDir != nil {
|
||||
log.Errorf("failed to resolve auth directory from config: %v", errResolveAuthDir)
|
||||
} else {
|
||||
newConfig.AuthDir = resolvedAuthDir
|
||||
}
|
||||
}
|
||||
|
||||
w.clientsMutex.Lock()
|
||||
var oldConfig *config.Config
|
||||
|
||||
Reference in New Issue
Block a user