security: stop tracking env files; admin gate via VITE_SITE_ADMIN_GATE; Redis/cache/docs
Remove InfoGenie-frontend and Go .env from version control; add .env.example templates; ignore .claude local settings. Admin UI reads site gate from env only. Note: rotate secrets if repo history was ever public. Made-with: Cursor
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
@@ -17,10 +18,21 @@ type AppConfig struct {
|
||||
Mail MailConfig
|
||||
AI AIConfig
|
||||
AuthCenter AuthCenterConfig
|
||||
Redis RedisConfig
|
||||
// SiteAdminToken 与前端管理员口令一致,用于更新站点展示配置(如 60s 功能开关);为空则禁止写入
|
||||
SiteAdminToken string
|
||||
}
|
||||
|
||||
// RedisConfig 可选缓存;Enabled 为 false 时不连接 Redis,行为与未接入缓存时一致
|
||||
type RedisConfig struct {
|
||||
Enabled bool
|
||||
Addr string
|
||||
Password string
|
||||
DB int
|
||||
KeyPrefix string
|
||||
SiteTTL time.Duration
|
||||
}
|
||||
|
||||
type DBConfig struct {
|
||||
Host string
|
||||
Port string
|
||||
@@ -67,6 +79,10 @@ const (
|
||||
defaultDevDBName = "infogenie-test"
|
||||
defaultDevDBUser = "infogenie-test"
|
||||
defaultDevDBPassword = "infogenie-test"
|
||||
|
||||
defaultDevRedisAddr = "10.1.1.100:6379"
|
||||
defaultRedisDB = 10
|
||||
defaultRedisKeyPrefix = "infogenie:go:v1:"
|
||||
)
|
||||
|
||||
func Load() (*AppConfig, error) {
|
||||
@@ -129,6 +145,12 @@ func Load() (*AppConfig, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
redisCfg, err := loadRedisConfig(env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cfg.Redis = redisCfg
|
||||
|
||||
// AI配置现在完全从数据库读取,不再加载ai_config.json文件
|
||||
cfg.AI = AIConfig{Providers: make(map[string]AIProviderConfig)}
|
||||
|
||||
@@ -136,6 +158,53 @@ func Load() (*AppConfig, error) {
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func loadRedisConfig(env string) (RedisConfig, error) {
|
||||
if !parseBoolEnv(os.Getenv("REDIS_ENABLED")) {
|
||||
return RedisConfig{}, nil
|
||||
}
|
||||
var addr string
|
||||
var err error
|
||||
if env == envProduction {
|
||||
addr, err = getEnvByEnvironment(env, "REDIS_ADDR", "")
|
||||
if err != nil {
|
||||
return RedisConfig{}, err
|
||||
}
|
||||
} else {
|
||||
addr = getEnv("REDIS_ADDR", defaultDevRedisAddr)
|
||||
}
|
||||
if strings.TrimSpace(addr) == "" {
|
||||
return RedisConfig{}, fmt.Errorf("已启用 REDIS_ENABLED 但 REDIS_ADDR 为空")
|
||||
}
|
||||
dbIdx, err := strconv.Atoi(strings.TrimSpace(getEnv("REDIS_DB", strconv.Itoa(defaultRedisDB))))
|
||||
if err != nil || dbIdx < 0 {
|
||||
return RedisConfig{}, fmt.Errorf("无效的 REDIS_DB: %s", getEnv("REDIS_DB", ""))
|
||||
}
|
||||
ttlSec, err := strconv.Atoi(strings.TrimSpace(getEnv("REDIS_SITE_TTL", "60")))
|
||||
if err != nil || ttlSec < 1 {
|
||||
return RedisConfig{}, fmt.Errorf("无效的 REDIS_SITE_TTL: %s", getEnv("REDIS_SITE_TTL", ""))
|
||||
}
|
||||
prefix := strings.TrimSpace(getEnv("REDIS_KEY_PREFIX", defaultRedisKeyPrefix))
|
||||
if prefix == "" {
|
||||
prefix = defaultRedisKeyPrefix
|
||||
}
|
||||
if !strings.HasSuffix(prefix, ":") {
|
||||
prefix += ":"
|
||||
}
|
||||
return RedisConfig{
|
||||
Enabled: true,
|
||||
Addr: addr,
|
||||
Password: getEnv("REDIS_PASSWORD", ""),
|
||||
DB: dbIdx,
|
||||
KeyPrefix: prefix,
|
||||
SiteTTL: time.Duration(ttlSec) * time.Second,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseBoolEnv(raw string) bool {
|
||||
s := strings.ToLower(strings.TrimSpace(raw))
|
||||
return s == "1" || s == "true" || s == "yes" || s == "on"
|
||||
}
|
||||
|
||||
func loadEnvFile(env string) error {
|
||||
envFile := fmt.Sprintf(".env.%s", env)
|
||||
if _, err := os.Stat(envFile); err == nil {
|
||||
|
||||
Reference in New Issue
Block a user