Files
Nip-05-api/internal/config/config.go
Michilis 5dcd671043 Support comma-separated CORS_HEADER for multiple origins.
Parse CORS_HEADER as a list: * for all origins, or reflect matching
request Origin when multiple specific origins are configured. Add Vary:
Origin for the allowlist case. Update .env.example and CORS tests.
2026-05-06 20:38:28 +00:00

182 lines
4.2 KiB
Go

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
InvoiceMemoYearly string
InvoiceMemoLifetime string
}
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
// CORSOrigins is parsed from the CORS_HEADER env var (comma-separated).
// Use "*" to allow all origins, or list specific origins like
// "https://example.com,https://other.example".
CORSOrigins []string
}
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),
InvoiceMemoYearly: env("INVOICE_MEMO_YEARLY", "Noderunners Relay yearly Access"),
InvoiceMemoLifetime: env("INVOICE_MEMO_LIFETIME", "Noderunners Relay lifetime Access"),
},
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", "")),
CORSOrigins: csv(env("CORS_HEADER", "*")),
}
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) }