- LEGAL_IMPRINT_/LEGAL_PRIVACY_ URL and Markdown (inline or *_FILE);
only http(s) URLs accepted
- /auth/status exposes legal info; public GET /legal/{imprint|privacy}
- Links on login/setup screen and in the settings footer: URL opens a new
tab, Markdown a reading modal; hidden when nothing is configured
- "Über MindSift" modal (version, license, repository) in settings and help
- Docs and compose/env examples
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
150 lines
4.3 KiB
Go
150 lines
4.3 KiB
Go
package config
|
|
|
|
import (
|
|
"log/slog"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
type Config struct {
|
|
Port string
|
|
DataDir string
|
|
DatabasePath string
|
|
JWTSecret string
|
|
BaseURL string
|
|
AllowRegistration bool
|
|
LogLevel string
|
|
Debug bool
|
|
DemoMode bool
|
|
|
|
// Optional SMTP environment variables (can also be overridden/configured in DB system_settings)
|
|
SMTPHost string
|
|
SMTPPort string
|
|
SMTPUser string
|
|
SMTPPass string
|
|
SMTPFrom string
|
|
SMTPTLS bool
|
|
// SMTPSkipVerify accepts self-signed or mismatching certificates (internal mail servers)
|
|
SMTPSkipVerify bool
|
|
|
|
// Legal notices (Impressum, Datenschutz): an external URL and/or Markdown text, shown even before login
|
|
Legal LegalTexts
|
|
}
|
|
|
|
type LegalTexts struct {
|
|
ImprintURL, ImprintContent string
|
|
PrivacyURL, PrivacyContent string
|
|
}
|
|
|
|
// legalContent reads Markdown from NAME or, for longer texts, from the file named in NAME_FILE.
|
|
func legalContent(name string) string {
|
|
if v := strings.TrimSpace(os.Getenv(name)); v != "" {
|
|
return v
|
|
}
|
|
if path := strings.TrimSpace(os.Getenv(name + "_FILE")); path != "" {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
slog.Warn("Rechtstext konnte nicht gelesen werden", "variable", name+"_FILE", "error", err)
|
|
return ""
|
|
}
|
|
return strings.TrimSpace(string(data))
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// legalURL accepts only http(s) links, so a misconfiguration cannot inject javascript: URLs.
|
|
func legalURL(name string) string {
|
|
v := strings.TrimSpace(os.Getenv(name))
|
|
if v == "" {
|
|
return ""
|
|
}
|
|
if !strings.HasPrefix(v, "https://") && !strings.HasPrefix(v, "http://") {
|
|
slog.Warn("Rechtstext-URL ignoriert: nur http(s) erlaubt", "variable", name)
|
|
return ""
|
|
}
|
|
return v
|
|
}
|
|
|
|
func Load() *Config {
|
|
port := getEnv("PORT", "8080")
|
|
dataDir := getEnv("DATA_DIR", "./data")
|
|
dbURL := getEnv("DATABASE_URL", "")
|
|
|
|
dbPath := filepath.Join(dataDir, "mindsift.db")
|
|
if strings.HasPrefix(dbURL, "sqlite://") {
|
|
dbPath = strings.TrimPrefix(dbURL, "sqlite://")
|
|
} else if dbURL != "" && !strings.HasPrefix(dbURL, "postgres") {
|
|
dbPath = dbURL
|
|
}
|
|
|
|
jwtSecret := getEnv("JWT_SECRET", "mindsift-default-selfhosted-jwt-secret-key-2026")
|
|
baseURL := getEnv("BASE_URL", "http://localhost:"+port)
|
|
allowReg := strings.ToLower(getEnv("ALLOW_REGISTRATION", "false")) == "true"
|
|
|
|
debugVal := strings.ToLower(getEnv("DEBUG", "false"))
|
|
debug := debugVal == "true" || debugVal == "1" || debugVal == "yes" || debugVal == "on"
|
|
|
|
logLevel := strings.ToLower(getEnv("LOG_LEVEL", "info"))
|
|
if debug {
|
|
logLevel = "debug"
|
|
}
|
|
|
|
demoVal := strings.ToLower(getEnv("MINDA_DEMO", getEnv("MINDSIFT_DEMO", getEnv("DEMO_MODE", "false"))))
|
|
demoMode := demoVal == "true" || demoVal == "1" || demoVal == "yes" || demoVal == "on"
|
|
|
|
smtpHost := getEnv("SMTP_HOST", "")
|
|
smtpPort := getEnv("SMTP_PORT", "587")
|
|
smtpUser := getEnv("SMTP_USER", "")
|
|
smtpPass := getEnv("SMTP_PASS", "")
|
|
smtpFrom := getEnv("SMTP_FROM", "")
|
|
smtpTLS := strings.ToLower(getEnv("SMTP_TLS", "true")) != "false"
|
|
smtpSkipVerify := strings.ToLower(getEnv("SMTP_SKIP_VERIFY", "false")) == "true"
|
|
|
|
return &Config{
|
|
Port: port,
|
|
DataDir: dataDir,
|
|
DatabasePath: dbPath,
|
|
JWTSecret: jwtSecret,
|
|
BaseURL: baseURL,
|
|
AllowRegistration: allowReg,
|
|
LogLevel: logLevel,
|
|
Debug: debug,
|
|
DemoMode: demoMode,
|
|
SMTPHost: smtpHost,
|
|
SMTPPort: smtpPort,
|
|
SMTPUser: smtpUser,
|
|
SMTPPass: smtpPass,
|
|
SMTPFrom: smtpFrom,
|
|
SMTPTLS: smtpTLS,
|
|
SMTPSkipVerify: smtpSkipVerify,
|
|
Legal: LegalTexts{
|
|
ImprintURL: legalURL("LEGAL_IMPRINT_URL"),
|
|
ImprintContent: legalContent("LEGAL_IMPRINT_CONTENT"),
|
|
PrivacyURL: legalURL("LEGAL_PRIVACY_URL"),
|
|
PrivacyContent: legalContent("LEGAL_PRIVACY_CONTENT"),
|
|
},
|
|
}
|
|
}
|
|
|
|
// SlogLevel parses Config.LogLevel into a standard slog.Level.
|
|
func (c *Config) SlogLevel() slog.Level {
|
|
switch strings.ToLower(strings.TrimSpace(c.LogLevel)) {
|
|
case "debug":
|
|
return slog.LevelDebug
|
|
case "warn", "warning":
|
|
return slog.LevelWarn
|
|
case "error":
|
|
return slog.LevelError
|
|
default:
|
|
return slog.LevelInfo
|
|
}
|
|
}
|
|
|
|
func getEnv(key, fallback string) string {
|
|
if val, ok := os.LookupEnv(key); ok && strings.TrimSpace(val) != "" {
|
|
return strings.TrimSpace(val)
|
|
}
|
|
return fallback
|
|
}
|