Files
ttstd-infinite-canvas/backend/internal/config/dotenv.go

84 lines
2.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Package config 提供轻量的运行时配置辅助能力。
//
// 后端只从环境变量读取配置;为方便本地开发(不依赖启动脚本注入),
// 这里提供一个零依赖的 .env 加载器,供 server / migrate-schema 等命令共用。
package config
import (
"bufio"
"log"
"os"
"path/filepath"
"strings"
)
// LoadDotEnv 从若干候选路径读取 .env 并注入进程环境变量。
//
// 规则:
// - 已存在的环境变量优先,不会被覆盖(Docker/脚本注入的值始终生效);
// - 跳过空行与 # 注释,兼容 export 前缀与首尾引号;
// - 找不到 .env 时静默跳过,不影响生产部署。
func LoadDotEnv() {
path := findDotEnvPath()
if path == "" {
return
}
file, err := os.Open(path)
if err != nil {
return
}
defer file.Close()
log.Printf("已从 %s 加载 .env 配置(已存在的环境变量优先,不覆盖)", path)
scanner := bufio.NewScanner(file)
// 连接串可能较长,放宽单行上限
scanner.Buffer(make([]byte, 0, 64*1024), 1024*1024)
for scanner.Scan() {
line := strings.TrimPrefix(scanner.Text(), "\ufeff") // 去掉可能的 UTF-8 BOM
line = strings.TrimSpace(line)
if line == "" || strings.HasPrefix(line, "#") {
continue
}
line = strings.TrimSpace(strings.TrimPrefix(line, "export "))
idx := strings.IndexByte(line, '=')
if idx <= 0 {
continue
}
key := strings.TrimSpace(line[:idx])
value := strings.TrimSpace(line[idx+1:])
if len(value) >= 2 && value[0] == '"' && value[len(value)-1] == '"' {
value = value[1 : len(value)-1]
}
if key == "" {
continue
}
if _, exists := os.LookupEnv(key); exists {
continue
}
_ = os.Setenv(key, value)
}
}
// findDotEnvPath 依次查找:显式指定的 CANVAS_ENV_FILE、当前目录、上级目录、上上级目录。
func findDotEnvPath() string {
if explicit := strings.TrimSpace(os.Getenv("CANVAS_ENV_FILE")); explicit != "" {
if info, err := os.Stat(explicit); err == nil && !info.IsDir() {
return explicit
}
}
candidates := []string{
".env",
filepath.Join("..", ".env"),
filepath.Join("..", "..", ".env"),
}
for _, candidate := range candidates {
if info, err := os.Stat(candidate); err == nil && !info.IsDir() {
return candidate
}
}
return ""
}