Files
glchat/internal/config/config.go
T
grendervill 3d1816a640 feat(config): схема URL в meta зависит от TLS_ENABLED
- при установке без TLS (--skip-tls, стенд без домена) клиент получает
  http:// и ws:// вместо неработающих https:// и wss://
- установщик пишет TLS_ENABLED в .env; тесты фиксируют обе схемы
2026-09-19 19:58:32 +03:00

164 lines
3.9 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
import (
"fmt"
"os"
"strconv"
"strings"
"time"
)
const (
DefaultListenAddr = "127.0.0.1:8080"
DefaultDataDir = "/opt/glchat/data"
DefaultLogLevel = "info"
)
type Config struct {
Domain string
FilesDomain string
InstanceName string
ListenAddr string
DataDir string
WebRoot string
DatabasePath string
SQLiteCache string
SQLiteMmap string
LogLevel string
LogFormat string
ReadPoolSize int
MaxUploadSize int64
TLSEnabled bool
Version string
Commit string
BuildDate string
ShutdownTTL time.Duration
Maintenance time.Duration
}
func Load() (Config, error) {
cfg := Config{
Domain: env("DOMAIN", "localhost"),
FilesDomain: env("FILES_DOMAIN", ""),
InstanceName: env("INSTANCE_NAME", "glchat"),
ListenAddr: env("LISTEN_ADDR", DefaultListenAddr),
DataDir: env("DATA_DIR", DefaultDataDir),
WebRoot: env("WEB_ROOT", "/app/web"),
DatabasePath: env("DATABASE_PATH", ""),
SQLiteCache: env("SQLITE_CACHE_SIZE", "-8000"),
SQLiteMmap: env("SQLITE_MMAP_SIZE", "134217728"),
LogLevel: strings.ToLower(env("LOG_LEVEL", DefaultLogLevel)),
LogFormat: strings.ToLower(env("LOG_FORMAT", "json")),
ReadPoolSize: envInt("SQLITE_READ_POOL", 4),
MaxUploadSize: envInt64("MAX_UPLOAD_SIZE", 26214400),
TLSEnabled: envBool("TLS_ENABLED", true),
Version: env("APP_VERSION", "dev"),
Commit: env("APP_COMMIT", "none"),
BuildDate: env("APP_BUILD_DATE", "unknown"),
ShutdownTTL: time.Duration(envInt("SHUTDOWN_TIMEOUT_SECONDS", 20)) * time.Second,
Maintenance: time.Duration(envInt("DB_MAINTENANCE_INTERVAL_SECONDS", 21600)) * time.Second,
}
if cfg.FilesDomain == "" {
cfg.FilesDomain = "files." + cfg.Domain
}
if cfg.DatabasePath == "" {
cfg.DatabasePath = cfg.DataDir + "/db/glchat.db"
}
if cfg.ReadPoolSize < 1 {
return Config{}, fmt.Errorf("SQLITE_READ_POOL must be >= 1, got %d", cfg.ReadPoolSize)
}
if _, err := parseLogLevel(cfg.LogLevel); err != nil {
return Config{}, err
}
if cfg.LogFormat != "json" && cfg.LogFormat != "text" {
return Config{}, fmt.Errorf("LOG_FORMAT must be json or text, got %q", cfg.LogFormat)
}
return cfg, nil
}
// Scheme выбирается установщиком: при установке без TLS (--skip-tls,
// например стенд без домена) клиент должен получать http/ws, а не https/wss.
func (c Config) Scheme() string {
if c.TLSEnabled {
return "https"
}
return "http"
}
func (c Config) BaseURL() string { return c.Scheme() + "://" + c.Domain }
func (c Config) FilesURL() string { return c.Scheme() + "://" + c.FilesDomain }
func (c Config) GatewayURL() string {
scheme := "ws"
if c.TLSEnabled {
scheme = "wss"
}
return scheme + "://" + c.Domain + "/gateway"
}
func (c Config) LogLevelNumeric() int {
level, err := parseLogLevel(c.LogLevel)
if err != nil {
return 0
}
return level
}
func parseLogLevel(level string) (int, error) {
switch level {
case "debug":
return -4, nil
case "info":
return 0, nil
case "warn", "warning":
return 4, nil
case "error":
return 8, nil
default:
return 0, fmt.Errorf("unknown LOG_LEVEL %q", level)
}
}
func env(key, fallback string) string {
if v, ok := os.LookupEnv(key); ok && v != "" {
return v
}
return fallback
}
func envInt(key string, fallback int) int {
v, ok := os.LookupEnv(key)
if !ok || v == "" {
return fallback
}
n, err := strconv.Atoi(v)
if err != nil {
return fallback
}
return n
}
func envBool(key string, fallback bool) bool {
v, ok := os.LookupEnv(key)
if !ok || v == "" {
return fallback
}
parsed, err := strconv.ParseBool(v)
if err != nil {
return fallback
}
return parsed
}
func envInt64(key string, fallback int64) int64 {
v, ok := os.LookupEnv(key)
if !ok || v == "" {
return fallback
}
n, err := strconv.ParseInt(v, 10, 64)
if err != nil {
return fallback
}
return n
}