48a99d58ee
DUMPSTER_PORT=8080 (internal) was baked into email reset links, producing http://dumpster.dustin.coffee:8080/reset-password which doesn't resolve through Caddy. Added Config.AppURL() that reads APP_URL env var (set to https://dumpster.dustin.coffee on server), falls back to http://host:port for dev.
201 lines
4.3 KiB
Go
201 lines
4.3 KiB
Go
package config
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
func init() {
|
|
loadDotEnv()
|
|
}
|
|
|
|
func loadDotEnv() {
|
|
file, err := os.Open(".env")
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer file.Close()
|
|
|
|
scanner := bufio.NewScanner(file)
|
|
for scanner.Scan() {
|
|
line := strings.TrimSpace(scanner.Text())
|
|
if line == "" || strings.HasPrefix(line, "#") {
|
|
continue
|
|
}
|
|
parts := strings.SplitN(line, "=", 2)
|
|
if len(parts) != 2 {
|
|
continue
|
|
}
|
|
key := strings.TrimSpace(parts[0])
|
|
val := strings.TrimSpace(parts[1])
|
|
if (strings.HasPrefix(val, "\"") && strings.HasSuffix(val, "\"")) ||
|
|
(strings.HasPrefix(val, "'") && strings.HasSuffix(val, "'")) {
|
|
val = val[1 : len(val)-1]
|
|
}
|
|
if os.Getenv(key) == "" {
|
|
os.Setenv(key, val)
|
|
}
|
|
}
|
|
}
|
|
|
|
type Config struct {
|
|
Host string
|
|
Port string
|
|
Database DatabaseConfig
|
|
Valkey ValkeyConfig
|
|
MinIO MinIOConfig
|
|
LiveKit LiveKitConfig
|
|
WebPush WebPushConfig
|
|
Session SessionConfig
|
|
Giphy GiphyConfig
|
|
SMTP SMTPConfig
|
|
}
|
|
|
|
type SMTPConfig struct {
|
|
Host string
|
|
Port string
|
|
Username string
|
|
Password string
|
|
}
|
|
|
|
type DatabaseConfig struct {
|
|
Host string
|
|
Port string
|
|
User string
|
|
Password string
|
|
Database string
|
|
SSLMode string
|
|
}
|
|
|
|
type ValkeyConfig struct {
|
|
URL string
|
|
}
|
|
|
|
type MinIOConfig struct {
|
|
Endpoint string
|
|
AccessKey string
|
|
SecretKey string
|
|
Bucket string
|
|
UseSSL bool
|
|
}
|
|
|
|
type LiveKitConfig struct {
|
|
URL string
|
|
APIKey string
|
|
Secret string
|
|
}
|
|
|
|
type WebPushConfig struct {
|
|
PublicKey string
|
|
PrivateKey string
|
|
Subject string
|
|
}
|
|
|
|
type GiphyConfig struct {
|
|
APIKey string
|
|
}
|
|
|
|
type SessionConfig struct {
|
|
CookieName string
|
|
Duration time.Duration
|
|
}
|
|
|
|
func Load() *Config {
|
|
return &Config{
|
|
Host: getEnv("DUMPSTER_HOST", "localhost"),
|
|
Port: getEnv("DUMPSTER_PORT", "8080"),
|
|
Database: DatabaseConfig{
|
|
Host: getEnv("POSTGRES_HOST", "localhost"),
|
|
Port: getEnv("POSTGRES_PORT", "5432"),
|
|
User: getEnv("POSTGRES_USER", "dumpster"),
|
|
Password: getEnv("POSTGRES_PASSWORD", "dumpster"),
|
|
Database: getEnv("POSTGRES_DB", "dumpster"),
|
|
SSLMode: getEnv("POSTGRES_SSLMODE", "require"),
|
|
},
|
|
Valkey: ValkeyConfig{
|
|
URL: getEnv("VALKEY_URL", "redis://localhost:6379"),
|
|
},
|
|
MinIO: MinIOConfig{
|
|
Endpoint: getEnv("MINIO_ENDPOINT", ""),
|
|
AccessKey: getEnv("MINIO_ACCESS_KEY", ""),
|
|
SecretKey: getEnv("MINIO_SECRET_KEY", ""),
|
|
Bucket: getEnv("MINIO_BUCKET", "dumpster-files"),
|
|
UseSSL: getBool("MINIO_USE_SSL", false),
|
|
},
|
|
Session: SessionConfig{
|
|
CookieName: getEnv("DUMPSTER_SESSION_COOKIE", "dumpster_session"),
|
|
Duration: time.Duration(getInt("DUMPSTER_SESSION_DAYS", 30)) * 24 * time.Hour,
|
|
},
|
|
LiveKit: LiveKitConfig{
|
|
URL: getEnv("LIVEKIT_URL", ""),
|
|
APIKey: getEnv("LIVEKIT_API_KEY", ""),
|
|
Secret: getEnv("LIVEKIT_API_SECRET", ""),
|
|
},
|
|
WebPush: WebPushConfig{
|
|
PublicKey: getEnv("VAPID_PUBLIC_KEY", ""),
|
|
PrivateKey: getEnv("VAPID_PRIVATE_KEY", ""),
|
|
Subject: getEnv("VAPID_SUBJECT", ""),
|
|
},
|
|
Giphy: GiphyConfig{
|
|
APIKey: getEnv("GIPHY_API_KEY", ""),
|
|
},
|
|
SMTP: SMTPConfig{
|
|
Host: getEnv("SMTP_HOST", ""),
|
|
Port: getEnv("SMTP_PORT", ""),
|
|
Username: getEnv("SMTP_USERNAME", ""),
|
|
Password: getEnv("SMTP_PASSWORD", ""),
|
|
},
|
|
}
|
|
}
|
|
|
|
func (c *Config) AppURL() string {
|
|
if v := getEnv("APP_URL", ""); v != "" {
|
|
return v
|
|
}
|
|
scheme := "http"
|
|
port := c.Port
|
|
if c.Database.SSLMode == "require" {
|
|
// ponytail: using SSLMode as a rough https hint; add explicit APP_URL for real setups
|
|
scheme = "https"
|
|
}
|
|
if port == "80" || port == "443" {
|
|
return fmt.Sprintf("%s://%s", scheme, c.Host)
|
|
}
|
|
return fmt.Sprintf("%s://%s:%s", scheme, c.Host, port)
|
|
}
|
|
|
|
func (c *Config) DatabaseDSN() string {
|
|
return "postgres://" + c.Database.User + ":" + c.Database.Password + "@" + c.Database.Host + ":" + c.Database.Port + "/" + c.Database.Database + "?sslmode=" + c.Database.SSLMode
|
|
}
|
|
|
|
func getEnv(key, fallback string) string {
|
|
if v := os.Getenv(key); v != "" {
|
|
return strings.TrimSpace(v)
|
|
}
|
|
return fallback
|
|
}
|
|
|
|
func getBool(key string, fallback bool) bool {
|
|
s := strings.ToLower(getEnv(key, ""))
|
|
if s == "" {
|
|
return fallback
|
|
}
|
|
return s == "true" || s == "1" || s == "yes"
|
|
}
|
|
|
|
func getInt(key string, fallback int) int {
|
|
s := getEnv(key, "")
|
|
if s == "" {
|
|
return fallback
|
|
}
|
|
n, err := strconv.Atoi(s)
|
|
if err != nil {
|
|
return fallback
|
|
}
|
|
return n
|
|
}
|