From b3e34c6e36ad3782dfa8be2ae9688bd85b22321c Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 21 Apr 2026 09:34:29 +0800 Subject: [PATCH] feat(ci): normalize shared environment semantics --- .../2026-04-21-env-normalization-checklist.md | 98 +++++++++++++++++++ ...formance-ops-optimization-execution-log.md | 19 ++++ ...ality-performance-ops-optimization-plan.md | 16 +-- gateway/internal/app/bootstrap.go | 9 +- gateway/internal/config/config.go | 29 ++++-- gateway/internal/config/config_test.go | 30 ++++++ scripts/devtest/start_dev_stack.sh | 4 +- supply-api/cmd/supply-api/main.go | 26 +++++ supply-api/cmd/supply-api/main_test.go | 21 ++++ supply-api/config/config.prod.example.yaml | 56 +++++++++++ supply-api/config/config.staging.example.yaml | 40 ++++++++ 11 files changed, 326 insertions(+), 22 deletions(-) create mode 100644 docs/plans/2026-04-21-env-normalization-checklist.md create mode 100644 supply-api/config/config.prod.example.yaml create mode 100644 supply-api/config/config.staging.example.yaml diff --git a/docs/plans/2026-04-21-env-normalization-checklist.md b/docs/plans/2026-04-21-env-normalization-checklist.md new file mode 100644 index 00000000..532550ed --- /dev/null +++ b/docs/plans/2026-04-21-env-normalization-checklist.md @@ -0,0 +1,98 @@ +# 2026-04-21 Env Normalization Checklist + +## P2-C-01 三服务环境枚举与别名盘点 + +1. `gateway` + - 当前输入源是 `GATEWAY_ENV`。 + - 共享环境判定历史上同时接受 `production`、`prod`、`online`。 + - `staging` 也被视为非开发共享环境,禁止 `inmemory` token runtime。 +2. `supply-api` + - `ResolveEnv` 只接受 `dev`、`staging`、`prod`。 + - 默认配置路径按 `config..yaml` 组装。 + - 现有 devtest 脚本曾存在 `-env=staging -config=config.dev.yaml` 的错配。 +3. `platform-token-runtime` + - `TOKEN_RUNTIME_ENV` 只接受 `dev`、`staging`、`prod`。 + - `staging/prod` 需要显式 PostgreSQL store,`dev` 才允许内存 store。 +4. 本次盘点覆盖的枚举全集: + - 规范值:`dev`、`staging`、`prod` + - 兼容别名:`production`、`online` + +## P2-C-02 统一枚举 + +仓库内部只保留三种规范值: + +1. `dev` +2. `staging` +3. `prod` + +统一规则: + +1. `production`、`online` 只允许作为兼容输入存在。 +2. 兼容输入一进入服务边界就必须立即折叠为 `prod`。 +3. 文档、模板、脚本、报告、CI 产物里不再引入新的别名。 + +## P2-C-03 gateway 归一化入口 + +唯一入口定在 `gateway/internal/config/config.go`: + +1. `LoadConfig` 读取 `GATEWAY_ENV` 后立刻归一化。 +2. `ValidateAuthConfig` 与启动安全校验只消费归一化后的值。 +3. `bootstrap.go` 不再维护独立的 `production/online` 判定分支。 + +## P2-C-04 supply-api 错配拒绝规则 + +拒绝条件: + +1. 当 `-env` 为 `staging` 或 `prod` 时,`-config` 不得指向 `config.dev.yaml` 或 `config.dev.yml`。 + +错误信息草稿: + +`config path "" is a dev template and cannot be used with -env=; use config..yaml or a example template instead` + +## P2-C-05 staging 模板骨架 + +`supply-api/config/config.staging.example.yaml` 至少保留以下段落: + +1. `server` +2. `database` +3. `redis` +4. `token` +5. `audit` + +## P2-C-06 prod 模板骨架 + +`supply-api/config/config.prod.example.yaml` 必须额外显式包含: + +1. `server.default_supplier_id: 0` +2. `token.algorithm: RS256` +3. `token.public_key` +4. `settlement.withdraw_enabled` +5. `sms` 占位字段 + +## P2-C-07 devtest 参数设计 + +`scripts/devtest/start_dev_stack.sh` 改为: + +1. 使用 `LIJIAOQIAO_DEVTEST_SUPPLY_CONFIG` 作为 `supply-api` 配置路径参数。 +2. 默认值改为 `./config/config.staging.example.yaml`。 +3. 不再把 `config.dev.yaml` 硬编码进 staging 启动链路。 + +## P2-C-08 环境归一化检查清单 + +### 启动前检查 + +1. 所有新文档和样例文件只出现 `dev`、`staging`、`prod` 三种规范值。 +2. 兼容别名只存在于输入归一化代码,不存在于模板与脚本默认值。 +3. `supply-api` 的 `staging/prod` 启动命令不引用 `config.dev.yaml`。 + +### 启动后检查 + +1. `gateway` 在 `production` 或 `online` 输入下对外表现为 `prod` 语义。 +2. `supply-api` 在 `-env=staging -config=config.dev.yaml` 时必须 fail fast。 +3. `platform-token-runtime` 仍然只接受 `dev/staging/prod`,不新增额外别名。 + +### CI 检查 + +1. `gateway` 运行环境归一化相关单测。 +2. `supply-api` 运行命令行错配拒绝测试。 +3. `scripts/devtest/start_dev_stack.sh` 至少通过 `bash -n` 语法检查。 diff --git a/docs/plans/2026-04-21-project-quality-performance-ops-optimization-execution-log.md b/docs/plans/2026-04-21-project-quality-performance-ops-optimization-execution-log.md index f612598b..5dd005c7 100644 --- a/docs/plans/2026-04-21-project-quality-performance-ops-optimization-execution-log.md +++ b/docs/plans/2026-04-21-project-quality-performance-ops-optimization-execution-log.md @@ -113,3 +113,22 @@ git diff --check 1. 已创建 `docs/plans/2026-04-21-real-staging-gate-rules.md`,逐项覆盖 `P2-B-01` 到 `P2-B-08`,写死 rehearsal / real staging 术语边界、`PASS_REAL|PASS_REHEARSAL|FAIL` 状态枚举、override 约束和完成率口径。 2. 已在 `scripts/ci/superpowers_stage_validate.sh`、`scripts/ci/staging_real_readiness_check.sh`、`scripts/ci/superpowers_release_pipeline.sh`、`scripts/ci/final_decision_consistency_check.sh` 补入迁移设计注释,明确真实 staging 是唯一 release 硬门禁,且 `DEFERRED` / rehearsal 不得计入 release pass。 3. 四个脚本 `bash -n` 通过,且 `git diff --check` 无格式错误;本批次仅落设计规则与迁移约束,没有伪装成已完成实现。 + +## P2-C 环境归一化与模板骨架完成 + +执行命令: + +```bash +bash -n scripts/devtest/start_dev_stack.sh +go test ./internal/config ./internal/app +go test ./cmd/supply-api ./internal/app ./internal/config +git diff --check +``` + +执行结果: + +1. 已在 `docs/plans/2026-04-21-env-normalization-checklist.md` 盘点 `gateway`、`supply-api`、`platform-token-runtime` 的环境枚举,统一仓库内规范值为 `dev`、`staging`、`prod`,并补全启动前、启动后、CI 三类检查项。 +2. 已在 `gateway/internal/config/config.go` 增加单一 `NormalizeEnv` 入口,并让 `gateway/internal/app/bootstrap.go` 的生产安全判定复用该入口,收敛 `production/online -> prod` 兼容逻辑。 +3. 已在 `supply-api/cmd/supply-api/main.go` 增加 `staging/prod + config.dev.yaml` 的 fail-fast 拒绝规则,并补充命令行测试覆盖;同时新增 `supply-api/config/config.staging.example.yaml` 与 `supply-api/config/config.prod.example.yaml` 模板骨架。 +4. 已在 `scripts/devtest/start_dev_stack.sh` 引入 `LIJIAOQIAO_DEVTEST_SUPPLY_CONFIG` 参数,默认改用 `./config/config.staging.example.yaml`,去除 staging 启动链路里的 `config.dev.yaml` 硬编码。 +5. `bash -n scripts/devtest/start_dev_stack.sh`、`go test ./internal/config ./internal/app`、`go test ./cmd/supply-api ./internal/app ./internal/config` 均通过,且 `git diff --check` 无格式错误。 diff --git a/docs/plans/2026-04-21-project-quality-performance-ops-optimization-plan.md b/docs/plans/2026-04-21-project-quality-performance-ops-optimization-plan.md index e6cfac1e..d5e7d4d4 100644 --- a/docs/plans/2026-04-21-project-quality-performance-ops-optimization-plan.md +++ b/docs/plans/2026-04-21-project-quality-performance-ops-optimization-plan.md @@ -357,21 +357,21 @@ - Create: `supply-api/config/config.prod.example.yaml` - Create: `docs/plans/2026-04-21-env-normalization-checklist.md` -- [ ] `P2-C-01` 盘点三服务支持的环境枚举和值别名。 +- [x] `P2-C-01` 盘点三服务支持的环境枚举和值别名。 完成标准:清单覆盖 `dev`、`staging`、`prod`、`production`、`online`。 -- [ ] `P2-C-02` 定义统一枚举。 +- [x] `P2-C-02` 定义统一枚举。 完成标准:计划文档只保留 `dev`、`staging`、`prod`。 -- [ ] `P2-C-03` 设计 `gateway` 对 `production/online -> prod` 的归一化入口。 +- [x] `P2-C-03` 设计 `gateway` 对 `production/online -> prod` 的归一化入口。 完成标准:只保留一个真正规则。 -- [ ] `P2-C-04` 设计 `supply-api` 对 `-env=staging -config=config.dev.yaml` 的拒绝规则。 +- [x] `P2-C-04` 设计 `supply-api` 对 `-env=staging -config=config.dev.yaml` 的拒绝规则。 完成标准:规则中包含错误信息草稿。 -- [ ] `P2-C-05` 复制 `config.dev.yaml` 所需段落,生成 staging 模板骨架。 +- [x] `P2-C-05` 复制 `config.dev.yaml` 所需段落,生成 staging 模板骨架。 完成标准:`config.staging.example.yaml` 包含必要配置段。 -- [ ] `P2-C-06` 复制 prod 模板骨架。 +- [x] `P2-C-06` 复制 prod 模板骨架。 完成标准:`config.prod.example.yaml` 包含关键安全配置占位。 -- [ ] `P2-C-07` 在 `scripts/devtest/start_dev_stack.sh` 设计改用 staging 模板的参数。 +- [x] `P2-C-07` 在 `scripts/devtest/start_dev_stack.sh` 设计改用 staging 模板的参数。 完成标准:脚本不再硬编码 `config.dev.yaml`。 -- [ ] `P2-C-08` 写环境归一化检查清单。 +- [x] `P2-C-08` 写环境归一化检查清单。 完成标准:包括启动前检查、启动后检查、CI 检查三类项。 ### Task P2-D: 重构测试分类,补真实跨服务 smoke diff --git a/gateway/internal/app/bootstrap.go b/gateway/internal/app/bootstrap.go index 3379dccf..30368a55 100644 --- a/gateway/internal/app/bootstrap.go +++ b/gateway/internal/app/bootstrap.go @@ -273,12 +273,9 @@ func validateStartupSecurity(cfg config.Config) error { } func isProductionEnv(env string) bool { - switch strings.ToLower(strings.TrimSpace(env)) { - case "production", "prod", "online": - return true - default: - return false - } + // 共享环境别名归一化只允许在 config.NormalizeEnv 一处定义, + // 启动安全校验只消费归一化后的 prod 结果,避免多处规则漂移。 + return config.NormalizeEnv(env) == "prod" } func isDefaultEncryptionKey() bool { diff --git a/gateway/internal/config/config.go b/gateway/internal/config/config.go index 33174b3b..03f128f8 100644 --- a/gateway/internal/config/config.go +++ b/gateway/internal/config/config.go @@ -38,11 +38,11 @@ type ServerConfig struct { // AuthConfig 鉴权运行时配置 type AuthConfig struct { - Env string - TokenRuntimeMode string - TokenRuntimeURL string - TrustedProxies []string // 可信的代理IP列表,用于IP伪造防护 - CORSAllowOrigins []string // 允许的CORS来源,为空则使用默认通配符 + Env string + TokenRuntimeMode string + TokenRuntimeURL string + TrustedProxies []string // 可信的代理IP列表,用于IP伪造防护 + CORSAllowOrigins []string // 允许的CORS来源,为空则使用默认通配符 } // DatabaseConfig 数据库配置 @@ -166,7 +166,7 @@ func LoadConfig(path string) (*Config, error) { IdleTimeout: 120 * time.Second, }, Auth: AuthConfig{ - Env: strings.ToLower(getEnv("GATEWAY_ENV", "dev")), + Env: NormalizeEnv(getEnv("GATEWAY_ENV", "dev")), TokenRuntimeMode: strings.ToLower(getEnv("GATEWAY_TOKEN_RUNTIME_MODE", "inmemory")), TokenRuntimeURL: strings.TrimSpace(getEnv("GATEWAY_TOKEN_RUNTIME_URL", "")), }, @@ -222,9 +222,24 @@ func LoadConfig(path string) (*Config, error) { return cfg, nil } +// NormalizeEnv 将兼容别名统一折叠到仓库内的唯一环境枚举。 +// Phase P2-C 之后,代码与文档内部只保留 dev/staging/prod。 +func NormalizeEnv(raw string) string { + switch strings.ToLower(strings.TrimSpace(raw)) { + case "", "dev": + return "dev" + case "staging": + return "staging" + case "production", "online", "prod": + return "prod" + default: + return strings.ToLower(strings.TrimSpace(raw)) + } +} + func ValidateAuthConfig(cfg AuthConfig) error { mode := strings.ToLower(strings.TrimSpace(cfg.TokenRuntimeMode)) - env := strings.ToLower(strings.TrimSpace(cfg.Env)) + env := NormalizeEnv(cfg.Env) switch mode { case "inmemory", "remote_introspection": diff --git a/gateway/internal/config/config_test.go b/gateway/internal/config/config_test.go index 9a7ee63c..eb0f54e6 100644 --- a/gateway/internal/config/config_test.go +++ b/gateway/internal/config/config_test.go @@ -444,6 +444,36 @@ func TestValidateAuthConfig_ProdRequiresRemoteIntrospection(t *testing.T) { } } +func TestValidateAuthConfig_OnlineAliasRequiresRemoteIntrospection(t *testing.T) { + cfg := AuthConfig{Env: "online", TokenRuntimeMode: "inmemory"} + if err := ValidateAuthConfig(cfg); err == nil { + t.Fatal("expected online alias to be normalized to prod and rejected") + } +} + +func TestLoadConfig_NormalizesProductionAliases(t *testing.T) { + t.Setenv("GATEWAY_TOKEN_RUNTIME_MODE", "remote_introspection") + t.Setenv("GATEWAY_TOKEN_RUNTIME_URL", "http://127.0.0.1:18081") + + t.Setenv("GATEWAY_ENV", "production") + cfg, err := LoadConfig("") + if err != nil { + t.Fatalf("unexpected error loading production alias: %v", err) + } + if cfg.Auth.Env != "prod" { + t.Fatalf("expected production alias to normalize to prod, got %q", cfg.Auth.Env) + } + + t.Setenv("GATEWAY_ENV", "online") + cfg, err = LoadConfig("") + if err != nil { + t.Fatalf("unexpected error loading online alias: %v", err) + } + if cfg.Auth.Env != "prod" { + t.Fatalf("expected online alias to normalize to prod, got %q", cfg.Auth.Env) + } +} + func TestLoadConfig_DefaultProvider(t *testing.T) { t.Setenv("OPENAI_BASE_URL", "https://api.openai.com") t.Setenv("OPENAI_MODELS", "gpt-4o-mini,gpt-4o") diff --git a/scripts/devtest/start_dev_stack.sh b/scripts/devtest/start_dev_stack.sh index eed2d3f9..0687d41f 100644 --- a/scripts/devtest/start_dev_stack.sh +++ b/scripts/devtest/start_dev_stack.sh @@ -25,6 +25,7 @@ GATEWAY_PORT="${LIJIAOQIAO_DEVTEST_GATEWAY_PORT:-18080}" SUPPLY_TOKEN_SECRET_KEY="${LIJIAOQIAO_DEVTEST_SUPPLY_TOKEN_SECRET_KEY:-devtest-secret-key-12345678901234567890}" SUPPLY_TOKEN_ISSUER="${LIJIAOQIAO_DEVTEST_SUPPLY_TOKEN_ISSUER:-lijiaoqiao/supply-api}" +SUPPLY_CONFIG_PATH="${LIJIAOQIAO_DEVTEST_SUPPLY_CONFIG:-./config/config.staging.example.yaml}" OPENAI_MODELS="${LIJIAOQIAO_DEVTEST_OPENAI_MODELS:-gpt-4o-mini,gpt-4o,gpt-4.1,o3-mini,claude-3-5-sonnet,claude-3-7-sonnet,gemini-2.0-flash,deepseek-chat}" @@ -132,6 +133,7 @@ export LIJIAOQIAO_DEVTEST_GATEWAY_PORT="${GATEWAY_PORT}" export LIJIAOQIAO_DEVTEST_OPENAI_MODELS="${OPENAI_MODELS}" export LIJIAOQIAO_DEVTEST_SUPPLY_TOKEN_SECRET_KEY="${SUPPLY_TOKEN_SECRET_KEY}" export LIJIAOQIAO_DEVTEST_SUPPLY_TOKEN_ISSUER="${SUPPLY_TOKEN_ISSUER}" +export LIJIAOQIAO_DEVTEST_SUPPLY_CONFIG="${SUPPLY_CONFIG_PATH}" EOF } @@ -187,7 +189,7 @@ start_process "platform-token-runtime" \ wait_http "platform-token-runtime" "http://${TOKEN_RUNTIME_ADDR}/actuator/health" start_process "supply-api" \ - "cd \"${ROOT_DIR}/supply-api\" && SUPPLY_API_ADDR=\"${SUPPLY_API_ADDR}\" SUPPLY_DB_HOST=\"${PG_HOST}\" SUPPLY_DB_PORT=\"${PG_PORT}\" SUPPLY_DB_USER=\"${PG_USER}\" SUPPLY_DB_PASSWORD=\"${PG_PASSWORD}\" SUPPLY_DB_NAME=\"${SUPPLY_DB}\" SUPPLY_API_IAM_ENABLED=\"true\" SUPPLY_TOKEN_SECRET_KEY=\"${SUPPLY_TOKEN_SECRET_KEY}\" SUPPLY_TOKEN_ISSUER=\"${SUPPLY_TOKEN_ISSUER}\" GOCACHE=\"${STATE_DIR}/go-cache/supply-api\" go run ./cmd/supply-api -env=staging -config ./config/config.dev.yaml" + "cd \"${ROOT_DIR}/supply-api\" && SUPPLY_API_ADDR=\"${SUPPLY_API_ADDR}\" SUPPLY_DB_HOST=\"${PG_HOST}\" SUPPLY_DB_PORT=\"${PG_PORT}\" SUPPLY_DB_USER=\"${PG_USER}\" SUPPLY_DB_PASSWORD=\"${PG_PASSWORD}\" SUPPLY_DB_NAME=\"${SUPPLY_DB}\" SUPPLY_API_IAM_ENABLED=\"true\" SUPPLY_TOKEN_SECRET_KEY=\"${SUPPLY_TOKEN_SECRET_KEY}\" SUPPLY_TOKEN_ISSUER=\"${SUPPLY_TOKEN_ISSUER}\" GOCACHE=\"${STATE_DIR}/go-cache/supply-api\" go run ./cmd/supply-api -env=staging -config \"${SUPPLY_CONFIG_PATH}\"" wait_http "supply-api" "http://127.0.0.1${SUPPLY_API_ADDR#:}/actuator/health" start_process "gateway" \ diff --git a/supply-api/cmd/supply-api/main.go b/supply-api/cmd/supply-api/main.go index 0d2f7aac..e78bda70 100644 --- a/supply-api/cmd/supply-api/main.go +++ b/supply-api/cmd/supply-api/main.go @@ -3,9 +3,12 @@ package main import ( "context" "flag" + "fmt" "net/http" "os" "os/signal" + "path/filepath" + "strings" "syscall" "time" @@ -30,6 +33,9 @@ func main() { if *configPath == "" { *configPath = "./config/config." + *env + ".yaml" } + if err := validateEnvConfigPath(*env, *configPath); err != nil { + logging.NewLogger("supply-api", logging.LogLevelInfo).Fatalf("%v", err) + } // P1-010修复: 初始化结构化日志 jsonLogger := logging.NewLogger("supply-api", logging.LogLevelInfo) @@ -105,3 +111,23 @@ func main() { jsonLogger.Info("shutdown complete") } + +func validateEnvConfigPath(envName, configPath string) error { + if envName == "dev" { + return nil + } + + base := strings.ToLower(strings.TrimSpace(filepath.Base(configPath))) + switch base { + case "config.dev.yaml", "config.dev.yml": + return fmt.Errorf( + "config path %q is a dev template and cannot be used with -env=%s; use config.%s.yaml or a %s example template instead", + configPath, + envName, + envName, + envName, + ) + default: + return nil + } +} diff --git a/supply-api/cmd/supply-api/main_test.go b/supply-api/cmd/supply-api/main_test.go index 377073cc..2e0437c7 100644 --- a/supply-api/cmd/supply-api/main_test.go +++ b/supply-api/cmd/supply-api/main_test.go @@ -78,6 +78,27 @@ func TestMain_RejectsUnsupportedEnvBeforeLoadingConfig(t *testing.T) { } } +func TestMain_RejectsDevConfigPathForStaging(t *testing.T) { + configPath := filepath.Join(t.TempDir(), "config.dev.yaml") + + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + + cmd := exec.CommandContext(ctx, os.Args[0], "-test.run=TestMainHelperProcess", "--", "-env", "staging", "-config", configPath) + cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1") + output, err := cmd.CombinedOutput() + + if ctx.Err() == context.DeadlineExceeded { + t.Fatalf("expected staging + dev config mismatch to fail fast, but process timed out. output=%s", string(output)) + } + if err == nil { + t.Fatalf("expected staging + dev config mismatch to fail, but process exited successfully. output=%s", string(output)) + } + if !strings.Contains(string(output), "dev template") { + t.Fatalf("expected output to mention dev template mismatch, got: %s", string(output)) + } +} + func TestMainHelperProcess(t *testing.T) { if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" { return diff --git a/supply-api/config/config.prod.example.yaml b/supply-api/config/config.prod.example.yaml new file mode 100644 index 00000000..ebaac3af --- /dev/null +++ b/supply-api/config/config.prod.example.yaml @@ -0,0 +1,56 @@ +# Supply API Production Example Configuration +# Production rules: +# - keep server.default_supplier_id at 0 +# - do not use HS256/HS384/HS512 +# - provide token.public_key for RSA verification + +server: + addr: ":18082" + read_timeout: 10s + write_timeout: 15s + idle_timeout: 30s + shutdown_timeout: 5s + default_supplier_id: 0 + +database: + host: "prod-postgres.internal" + port: 5432 + user: "supply_api" + password: "${SUPPLY_DB_PASSWORD}" + database: "supply_prod" + max_open_conns: 50 + max_idle_conns: 10 + conn_max_lifetime: 1h + conn_max_idle_time: 10m + +redis: + host: "prod-redis.internal" + port: 6379 + password: "${SUPPLY_REDIS_PASSWORD}" + db: 0 + pool_size: 20 + +token: + algorithm: "RS256" + public_key: | + ${SUPPLY_TOKEN_PUBLIC_KEY} + issuer: "lijiaoqiao/supply-api" + access_token_ttl: 1h + refresh_token_ttl: 168h + revocation_cache_ttl: 30s + +settlement: + withdraw_enabled: false + +sms: + enabled: false + provider: "" + app_id: "" + app_secret: "" + sign_name: "" + template_code: "" + +audit: + buffer_size: 1000 + flush_interval: 5s + export_timeout: 30s diff --git a/supply-api/config/config.staging.example.yaml b/supply-api/config/config.staging.example.yaml new file mode 100644 index 00000000..716fae75 --- /dev/null +++ b/supply-api/config/config.staging.example.yaml @@ -0,0 +1,40 @@ +# Supply API Staging Example Configuration +# Use with: +# go run ./cmd/supply-api -env=staging -config ./config/config.staging.example.yaml + +server: + addr: ":18082" + read_timeout: 10s + write_timeout: 15s + idle_timeout: 30s + shutdown_timeout: 5s + +database: + host: "staging-postgres.internal" + port: 5432 + user: "supply_api" + password: "${SUPPLY_DB_PASSWORD}" + database: "supply_staging" + max_open_conns: 25 + max_idle_conns: 5 + conn_max_lifetime: 1h + conn_max_idle_time: 10m + +redis: + host: "staging-redis.internal" + port: 6379 + password: "${SUPPLY_REDIS_PASSWORD}" + db: 0 + pool_size: 10 + +token: + secret_key: "${SUPPLY_TOKEN_SECRET_KEY}" + issuer: "lijiaoqiao/supply-api-staging" + access_token_ttl: 1h + refresh_token_ttl: 168h + revocation_cache_ttl: 30s + +audit: + buffer_size: 1000 + flush_interval: 5s + export_timeout: 30s