- 新增 cache.redis 配置(独立 host/port/password/db 字段),启用后正文缓存与音频代理缓存走 Redis,键前缀 fq:cache:/fq:audio:,TTL 自动过期 - 新增 cache.backend: sqlite|postgres 切换,PG 连接用 net/url 构造 DSN,密码特殊字符自动转义;SQLite 顺手启用 WAL 与 busy_timeout - SQL 方言收敛:q() 将 ? 占位符转换为 $n、INSERT OR IGNORE 与查询再插入统一为 ON CONFLICT、LastInsertId 改为 RETURNING id、建表 DDL 双版本分派 - PG 下 VACUUM 走 autovacuum 说明、状态页大小改查 pg_database_size - 修复 PG 下设备保存失败:is_valid 列为 INTEGER,写入点补 boolToInt 转换 - 新增 .env 支持:敏感配置(密码/主机/访问密钥)由环境变量覆盖 config.yaml,.env 进 gitignore,仓库附 .env.example 模板 - 删除死代码 InitDevicePoolTable;新增 rebind_test.go 单测 - 更新 README 与 config.yaml 配置示例,go.mod 新增 go-redis/v9 与 pgx/v5 依赖
50 lines
1.2 KiB
Go
50 lines
1.2 KiB
Go
package config
|
||
|
||
import (
|
||
"os"
|
||
"strings"
|
||
)
|
||
|
||
// loadDotenv 加载 .env 文件到环境变量(文件不存在则跳过;不覆盖已有环境变量)
|
||
func loadDotenv(path string) {
|
||
data, err := os.ReadFile(path)
|
||
if err != nil {
|
||
return
|
||
}
|
||
for _, line := range strings.Split(string(data), "\n") {
|
||
line = strings.TrimSpace(line)
|
||
if line == "" || strings.HasPrefix(line, "#") {
|
||
continue
|
||
}
|
||
k, v, ok := strings.Cut(line, "=")
|
||
if !ok {
|
||
continue
|
||
}
|
||
k = strings.TrimSpace(k)
|
||
v = strings.Trim(strings.TrimSpace(v), `"'`)
|
||
if os.Getenv(k) == "" {
|
||
os.Setenv(k, v)
|
||
}
|
||
}
|
||
}
|
||
|
||
// applyEnvOverrides 用环境变量覆盖敏感配置(配合 .env 使用,真实密码不进 config.yaml)
|
||
func applyEnvOverrides(c *Config) {
|
||
for _, o := range []struct {
|
||
env string
|
||
dst *string
|
||
}{
|
||
{"FQ_SERVER_ACCESS_KEY", &c.Server.AccessKey},
|
||
{"FQ_PG_HOST", &c.Cache.Postgres.Host},
|
||
{"FQ_PG_USER", &c.Cache.Postgres.User},
|
||
{"FQ_PG_PASSWORD", &c.Cache.Postgres.Password},
|
||
{"FQ_PG_DATABASE", &c.Cache.Postgres.Database},
|
||
{"FQ_REDIS_HOST", &c.Cache.Redis.Host},
|
||
{"FQ_REDIS_PASSWORD", &c.Cache.Redis.Password},
|
||
} {
|
||
if v := os.Getenv(o.env); v != "" {
|
||
*o.dst = v
|
||
}
|
||
}
|
||
}
|