mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 20:21:09 +02:00
mana-sync's billing middleware short-circuited every push/pull with
402 for users without a sync subscription. Cards promises free Sync
in its Phase-1 GUIDELINES, so it shouldn't gate its own users on a
mana-credits subscription it never sells.
Implementation:
• billing.NewChecker now takes an exemptApps slice. The middleware
extracts {appId} from the URL path and short-circuits before the
user lookup if the app is in the set.
• Configurable via the BILLING_EXEMPT_APPS env var (comma-separated).
• Set BILLING_EXEMPT_APPS=cards on the mana-sync container so the
cards.mana.how Sync loop stops 402-ing.
• Tests cover the exemption + the empty/whitespace edge cases. All
other apps keep the original behaviour (fail-open if mana-credits
is unreachable, 402 if it explicitly says inactive).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
58 lines
1.7 KiB
Go
58 lines
1.7 KiB
Go
package config
|
|
|
|
import (
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// Config holds all configuration for the sync server.
|
|
type Config struct {
|
|
Port int
|
|
DatabaseURL string
|
|
JWKSUrl string // mana-auth JWKS endpoint for JWT validation
|
|
ManaAuthURL string // mana-auth base URL for internal APIs (Space memberships)
|
|
CORSOrigins string
|
|
ManaCreditsURL string // mana-credits service URL for billing checks
|
|
ServiceKey string // Service-to-service auth key
|
|
BillingExemptApps []string // appIDs that bypass the sync-subscription billing gate
|
|
}
|
|
|
|
// Load reads configuration from environment variables with sensible defaults.
|
|
func Load() *Config {
|
|
port, _ := strconv.Atoi(getEnv("PORT", "3050"))
|
|
|
|
return &Config{
|
|
Port: port,
|
|
DatabaseURL: getEnv("DATABASE_URL", "postgresql://mana:devpassword@localhost:5432/mana_sync"),
|
|
JWKSUrl: getEnv("JWKS_URL", "http://localhost:3001/api/auth/jwks"),
|
|
ManaAuthURL: getEnv("MANA_AUTH_URL", "http://localhost:3001"),
|
|
CORSOrigins: getEnv("CORS_ORIGINS", "http://localhost:5173,http://localhost:5188"),
|
|
ManaCreditsURL: getEnv("MANA_CREDITS_URL", "http://localhost:3061"),
|
|
ServiceKey: getEnv("MANA_SERVICE_KEY", "dev-service-key"),
|
|
BillingExemptApps: splitCSV(getEnv("BILLING_EXEMPT_APPS", "")),
|
|
}
|
|
}
|
|
|
|
func getEnv(key, fallback string) string {
|
|
if v := os.Getenv(key); v != "" {
|
|
return v
|
|
}
|
|
return fallback
|
|
}
|
|
|
|
// splitCSV splits a comma-separated string into a trimmed, non-empty slice.
|
|
func splitCSV(raw string) []string {
|
|
if raw == "" {
|
|
return nil
|
|
}
|
|
parts := strings.Split(raw, ",")
|
|
out := make([]string, 0, len(parts))
|
|
for _, p := range parts {
|
|
t := strings.TrimSpace(p)
|
|
if t != "" {
|
|
out = append(out, t)
|
|
}
|
|
}
|
|
return out
|
|
}
|