Files
Nip-05-api/internal/config/config.go

199 lines
4.5 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"
"github.com/joho/godotenv"
)
type LightningConfig struct {
Enabled bool
LNbitsURL string
LNbitsInvoiceKey string
PriceYearlySats int64
PriceLifetimeSats int64
InvoiceExpiryMins int
}
type NostrConfig struct {
Relays []string
UsernameSyncEnabled bool
SyncIntervalMins int
}
type DMConfig struct {
Enabled bool
Nsec string
Kind int
MessagesFile string
}
type ExpiryConfig struct {
ReminderDays []int
GraceDays int
CronHourUTC int
}
type WebhookConfig struct {
URL string
Secret string
TimeoutSecs int
MaxRetries int
}
type Config struct {
Domain string
Port int
AdminAPIKey string
FrontendURL string
DatabasePath string
Lightning LightningConfig
Nostr NostrConfig
DM DMConfig
Expiry ExpiryConfig
Webhook WebhookConfig
LogLevel string
RateLimitPerMin int
ReservedUsernames []string
// CORS: exact origin list = FRONTEND_URL CORS_ORIGINS; loopback hosts if CORS_ALLOW_LOCALHOST.
CORSExtraOrigins []string
CORSAllowLocalhost bool
CORSAllowCredentials bool
}
func Load() (*Config, error) {
_ = godotenv.Load()
c := &Config{
Domain: env("DOMAIN", ""),
Port: envInt("PORT", 8080),
AdminAPIKey: env("ADMIN_API_KEY", ""),
FrontendURL: env("FRONTEND_URL", ""),
DatabasePath: env("DATABASE_PATH", ".data/nip05.db"),
Lightning: LightningConfig{
Enabled: envBool("LIGHTNING_ENABLED", true),
LNbitsURL: env("LNBITS_URL", ""),
LNbitsInvoiceKey: env("LNBITS_INVOICE_KEY", ""),
PriceYearlySats: int64(envInt("PRICE_YEARLY_SATS", 1000)),
PriceLifetimeSats: int64(envInt("PRICE_LIFETIME_SATS", 10000)),
InvoiceExpiryMins: envInt("INVOICE_EXPIRY_MINUTES", 30),
},
Nostr: NostrConfig{
Relays: csv(env("RELAYS", "")),
UsernameSyncEnabled: envBool("USERNAME_SYNC_ENABLED", true),
SyncIntervalMins: envInt("SYNC_INTERVAL_MINUTES", 15),
},
DM: DMConfig{
Enabled: envBool("DM_ENABLED", true),
Nsec: env("DM_NSEC", ""),
Kind: envInt("DM_KIND", 1059),
MessagesFile: env("MESSAGES_FILE", "messages.yaml"),
},
Expiry: ExpiryConfig{
ReminderDays: csvInt(env("EXPIRY_REMINDER_DAYS", "7")),
GraceDays: envInt("USERNAME_GRACE_DAYS", 30),
CronHourUTC: envInt("EXPIRY_CRON_HOUR_UTC", 9),
},
Webhook: WebhookConfig{
URL: env("WEBHOOK_URL", ""),
Secret: env("WEBHOOK_SECRET", ""),
TimeoutSecs: envInt("WEBHOOK_TIMEOUT_SECONDS", 10),
MaxRetries: envInt("WEBHOOK_MAX_RETRIES", 5),
},
LogLevel: env("LOG_LEVEL", "info"),
RateLimitPerMin: envInt("RATE_LIMIT_PER_MIN", 30),
ReservedUsernames: csv(env("RESERVED_USERNAMES", "")),
CORSExtraOrigins: csv(env("CORS_ORIGINS", "")),
CORSAllowLocalhost: envBool("CORS_ALLOW_LOCALHOST", true),
CORSAllowCredentials: envBool("CORS_ALLOW_CREDENTIALS", false),
}
if err := Validate(c); err != nil {
return nil, err
}
return c, nil
}
func env(key, def string) string {
if v, ok := os.LookupEnv(key); ok && v != "" {
return v
}
return def
}
func envInt(key string, def int) int {
if v, ok := os.LookupEnv(key); ok && v != "" {
if n, err := strconv.Atoi(v); err == nil {
return n
}
}
return def
}
func envBool(key string, def bool) bool {
if v, ok := os.LookupEnv(key); ok && v != "" {
switch strings.ToLower(v) {
case "1", "true", "yes", "on":
return true
case "0", "false", "no", "off":
return false
}
}
return def
}
func csv(v string) []string {
if v == "" {
return nil
}
parts := strings.Split(v, ",")
out := make([]string, 0, len(parts))
for _, p := range parts {
p = strings.TrimSpace(p)
if p != "" {
out = append(out, p)
}
}
return out
}
func csvInt(v string) []int {
parts := csv(v)
out := make([]int, 0, len(parts))
for _, p := range parts {
n, err := strconv.Atoi(p)
if err != nil {
continue
}
out = append(out, n)
}
return out
}
func (c *Config) Addr() string { return fmt.Sprintf(":%d", c.Port) }
// CORSExactOrigins lists allowed browser Origins for exact match (before loopback wildcard).
func (c *Config) CORSExactOrigins() []string {
seen := make(map[string]bool)
out := make([]string, 0, 4+len(c.CORSExtraOrigins))
add := func(s string) {
s = strings.TrimSpace(s)
if s == "" || seen[s] {
return
}
seen[s] = true
out = append(out, s)
}
add(c.FrontendURL)
for _, o := range c.CORSExtraOrigins {
add(o)
}
return out
}