package main import ( "bufio" "io" "os" "strings" ) // dotenv.go: чтение backend/.env (gitignored) в окружение. Парсер выделен чистой // функцией (пакет №4): правила «парные кавычки» и «не перекрывать выставленное» // — находки прошлых ревью, жившие только в комментариях; теперь они закреплены // юнитами (регресс «упростили в strings.Trim» ломал бы значения ключей API и // всплывал бы необъяснимыми 401 посреди приёмочного прогона). // dotenvPair is one KEY=VALUE line of a .env file. type dotenvPair struct{ K, V string } // parseDotEnv reads KEY=VALUE lines: пропускает пустые/комментарии/без «=», // триммит ключ и значение, снимает только ПАРНЫЕ обрамляющие кавычки — Trim по // набору символов откусил бы легитимную кавычку в конце значения (находка ревью). func parseDotEnv(r io.Reader) []dotenvPair { var out []dotenvPair sc := bufio.NewScanner(r) for sc.Scan() { line := strings.TrimSpace(sc.Text()) if line == "" || strings.HasPrefix(line, "#") { continue } k, v, ok := strings.Cut(line, "=") if !ok { continue } k, v = strings.TrimSpace(k), strings.TrimSpace(v) if n := len(v); n >= 2 && (v[0] == '"' || v[0] == '\'') && v[n-1] == v[0] { v = v[1 : n-1] } out = append(out, dotenvPair{K: k, V: v}) } return out } // loadDotEnv reads KEY=VALUE lines into the environment without overriding // already-set variables. Ключи — из backend/.env (gitignored); секреты никогда // не попадают в конфиги/репо. Заметь: переменная, выставленная в ПУСТУЮ строку, // считается невыставленной и будет перекрыта (текущее поведение, заморожено). func loadDotEnv(path string) { f, err := os.Open(path) if err != nil { return } defer f.Close() for _, p := range parseDotEnv(f) { if os.Getenv(p.K) == "" { os.Setenv(p.K, p.V) } } }