feat: ratelimit, ghcr package support, media path sensitization, LID to CN migration (#6)

* feat: ratelimit, ghcr package and proper project refactor from askarzh

* fix(bridge): validate media path

* update: dependencies

* fix: added LID to contact number migration
This commit is contained in:
Atul Singh
2026-05-13 12:54:31 +02:00
committed by GitHub
parent 680ef32b31
commit 193e0d3c77
20 changed files with 1245 additions and 173 deletions
+99 -27
View File
@@ -2,7 +2,9 @@ package config
import (
"fmt"
"log/slog"
"os"
"strconv"
)
type dbConfig struct {
@@ -14,41 +16,66 @@ type dbConfig struct {
}
type Config struct {
DB dbConfig
JWTSecret []byte
APIKey string
WebhookUrl string
DB dbConfig
JWTSecret []byte
APIKey string
WebhookUrl string
Host string
Port int
AuthLoginRate string
}
func LoadConfig() (*Config, error) {
user, ok := os.LookupEnv("POSTGRES_USER")
if !ok {
return nil, fmt.Errorf("missing POSTGRES_USER")
}
pass, ok := os.LookupEnv("POSTGRES_PASS")
if !ok {
return nil, fmt.Errorf("missing POSTGRES_PASS")
}
host, ok := os.LookupEnv("POSTGRES_HOST")
if !ok {
return nil, fmt.Errorf("missing POSTGRES_HOST")
}
port, ok := os.LookupEnv("POSTGRES_PORT")
if !ok {
return nil, fmt.Errorf("missing POSTGRES_PORT")
isPostgres := os.Getenv("IS_POSTGRES") == "true"
var user, pass, host, port string
if isPostgres {
var ok bool
user, ok = os.LookupEnv("POSTGRES_USER")
if !ok {
return nil, fmt.Errorf("missing POSTGRES_USER")
}
pass, ok = os.LookupEnv("POSTGRES_PASS")
if !ok {
return nil, fmt.Errorf("missing POSTGRES_PASS")
}
host, ok = os.LookupEnv("POSTGRES_HOST")
if !ok {
return nil, fmt.Errorf("missing POSTGRES_HOST")
}
port, ok = os.LookupEnv("POSTGRES_PORT")
if !ok {
return nil, fmt.Errorf("missing POSTGRES_PORT")
}
}
jwtSecret, ok := os.LookupEnv("JWT_SECRET")
jwtSecret, ok := lookupEither("WHATSAPP_JWT_SECRET", "JWT_SECRET")
if !ok {
return nil, fmt.Errorf("missing JWT_SECRET")
return nil, fmt.Errorf("missing WHATSAPP_JWT_SECRET")
}
apiKey, ok := os.LookupEnv("API_KEY")
apiKey, ok := lookupEither("WHATSAPP_API_KEY", "API_KEY")
if !ok {
return nil, fmt.Errorf("missing API_KEY")
return nil, fmt.Errorf("missing WHATSAPP_API_KEY")
}
if err := validateSecret("WHATSAPP_JWT_SECRET (or deprecated alias JWT_SECRET)", jwtSecret); err != nil {
return nil, err
}
if err := validateSecret("WHATSAPP_API_KEY (or deprecated alias API_KEY)", apiKey); err != nil {
return nil, err
}
webhookUrl := os.Getenv("WEBHOOK_URL")
isPostgres := os.Getenv("IS_POSTGRES") == "true"
serverHost := os.Getenv("HOST")
serverPort := 8080
if v, ok := os.LookupEnv("PORT"); ok {
p, err := strconv.Atoi(v)
if err != nil {
return nil, fmt.Errorf("invalid PORT %q: %w", v, err)
}
serverPort = p
}
authLoginRate := os.Getenv("AUTH_LOGIN_RATE") // parsed in auth package; empty -> default
return &Config{
DB: dbConfig{
@@ -58,8 +85,53 @@ func LoadConfig() (*Config, error) {
Port: port,
IsPostgres: isPostgres,
},
JWTSecret: []byte(jwtSecret),
APIKey: apiKey,
WebhookUrl: webhookUrl,
JWTSecret: []byte(jwtSecret),
APIKey: apiKey,
WebhookUrl: webhookUrl,
Host: serverHost,
Port: serverPort,
AuthLoginRate: authLoginRate,
}, nil
}
const minSecretLen = 32
// knownPlaceholders are the example values shipped in docker-compose.yaml.
// Operators who forget to override them must see startup fail loudly.
var knownPlaceholders = []string{
"c3VwZXItbG9uZy1yYW5kb20tc3RyaW5nLW1pbmltdW0tb2YtNjQtY2hhcmFjdGVycy15b3UtbmVlZC10by1wYXN0ZS1oZXJl",
"YW5vdGhlci1zdXBlci1sb25nLXJhbmRvbS1zdHJpbmctbWluaW11bS1vZi02NC1jaGFyYWN0ZXJzLXlvdS1uZWVkLXRvLXBhc3RlLWhlcmU=",
}
func validateSecret(name, value string) error {
for _, ph := range knownPlaceholders {
if value == ph {
return fmt.Errorf("%s is set to a placeholder value; generate a real one with `openssl rand -base64 48`", name)
}
}
if len(value) < minSecretLen {
return fmt.Errorf("%s is too short (%d chars, need ≥%d)", name, len(value), minSecretLen)
}
return nil
}
// envWarnFn is replaced in tests; in production it logs via slog.
var envWarnFn = func(msg string, args ...any) {
slog.Warn(msg, args...)
}
// lookupEither returns the value for `primary` if set, otherwise falls back
// to `deprecated` and emits a deprecation warning. Returns ("", false) only
// when neither is set.
func lookupEither(primary, deprecated string) (string, bool) {
if v, ok := os.LookupEnv(primary); ok {
return v, true
}
if v, ok := os.LookupEnv(deprecated); ok {
envWarnFn("env var is deprecated, use the new name",
"deprecated", deprecated,
"use_instead", primary)
return v, true
}
return "", false
}