Files

308 lines
7.5 KiB
Go

package config
import (
"fmt"
"os"
"strconv"
"strings"
"time"
"smoop-api/internal/vault"
)
type MediaMTXConfig struct {
APIBase string // e.g. "http://mediamtx:9997"
WebRTCBaseURL string // e.g. "http://mediamtx:8889"
PublicBaseURL string // e.g. "https://your-host" (for HLS/WHEP URLs returned to SPA)
TokenTTL time.Duration // default ~180s
}
type Config struct {
DB struct {
DSN string
}
MinIO struct {
Endpoint string
AccessKey string
SecretKey string
UseSSL bool
RecordsBucket string
LivestreamBucket string
PresignTTL time.Duration
}
MediaMTX MediaMTXConfig
JWTSecret []byte
PkiIot vault.PKIClient
}
func Load() (*Config, error) {
addr := os.Getenv("VAULT_ADDR")
token := os.Getenv("VAULT_TOKEN")
// New style: explicit KV v2 mount + key
mount := os.Getenv("VAULT_KV_MOUNT") // e.g. "kv" or "secret"
key := os.Getenv("VAULT_KV_KEY") // e.g. "snoop"
// Back-compat: allow legacy VAULT_KV_PATH like "kv/data/snoop" and derive mount+key
if (mount == "" || key == "") && os.Getenv("VAULT_KV_PATH") != "" {
legacy := strings.Trim(os.Getenv("VAULT_KV_PATH"), "/")
parts := strings.Split(legacy, "/")
if len(parts) >= 2 {
mount = parts[0]
key = parts[len(parts)-1]
}
}
if addr == "" || token == "" || mount == "" || key == "" {
return nil, fmt.Errorf("VAULT_ADDR, VAULT_TOKEN, VAULT_KV_MOUNT and VAULT_KV_KEY must be set (or provide legacy VAULT_KV_PATH)")
}
pki, err := vault.NewPKI(addr, token, "pki_iot", "device", 30*time.Second)
if err != nil {
return nil, err
}
raw, err := vault.ReadKVv2(addr, token, mount, key)
if err != nil {
return nil, err
}
getStr := func(k string) (string, error) {
v, ok := raw[k].(string)
if !ok || v == "" {
return "", fmt.Errorf("missing secret key: %s", k)
}
return v, nil
}
// getStrOpt := func(k, def string) string {
// if v, ok := raw[k].(string); ok && v != "" {
// return v
// }
// return def
// }
getBool := func(k string) (bool, error) {
v, ok := raw[k]
if !ok {
return false, fmt.Errorf("missing secret key: %s", k)
}
switch t := v.(type) {
case bool:
return t, nil
case string:
if t == "true" || t == "1" {
return true, nil
}
return false, nil
default:
return false, fmt.Errorf("invalid bool for key %s", k)
}
}
getTTL := func(k string, def time.Duration) time.Duration {
if v, ok := raw[k].(string); ok && strings.TrimSpace(v) != "" {
if n, err := strconv.Atoi(strings.TrimSpace(v)); err == nil && n > 0 {
return time.Duration(n) * time.Second
}
}
return def
}
// --- NEW: MediaMTX config FROM ENV (NOT from Vault)
getRequiredEnv := func(k string) (string, error) {
v := strings.TrimSpace(os.Getenv(k))
if v == "" {
return "", fmt.Errorf("missing required env %s", k)
}
return v, nil
}
getIntEnv := func(k string, def int) int {
if v := strings.TrimSpace(os.Getenv(k)); v != "" {
if n, err := strconv.Atoi(v); err == nil {
return n
}
}
return def
}
dbDSN, err := getStr("db_dsn")
if err != nil {
return nil, err
}
endpoint, err := getStr("minio_endpoint")
if err != nil {
return nil, err
}
ak, err := getStr("minio_access_key")
if err != nil {
return nil, err
}
sk, err := getStr("minio_secret_key")
if err != nil {
return nil, err
}
useSSL, err := getBool("minio_use_ssl")
if err != nil {
return nil, err
}
jwt, err := getStr("jwt_secret")
if err != nil {
return nil, err
}
recordsBucket := "records"
if v, ok := raw["minio_records_bucket"].(string); ok && v != "" {
recordsBucket = v
}
liveBucket := "livestream"
if v, ok := raw["minio_livestream_bucket"].(string); ok && v != "" {
liveBucket = v
}
// presignTTL := 15 * time.Minute
// if v, ok := raw["minio_presign_ttl_seconds"].(string); ok && v != "" {
// var sec int
// fmt.Sscanf(v, "%d", &sec)
// if sec > 0 {
// presignTTL = time.Duration(sec) * time.Second
// }
// }
presignTTL := getTTL("minio_presign_ttl_seconds", 15*time.Minute)
apiBase, err := getRequiredEnv("MEDIAMTX_API_BASE")
if err != nil {
return nil, err
}
webrtcBase, err := getRequiredEnv("MEDIAMTX_WEBRTC_BASE_URL")
if err != nil {
return nil, err
}
publicBase, err := getRequiredEnv("PUBLIC_BASE_URL")
if err != nil {
return nil, err
}
tokenTTL := getIntEnv("MEDIAMTX_TOKEN_TTL_SECONDS", 180)
cfg := &Config{}
cfg.DB.DSN = dbDSN
cfg.MinIO.Endpoint = endpoint
cfg.MinIO.AccessKey = ak
cfg.MinIO.SecretKey = sk
cfg.MinIO.UseSSL = useSSL
cfg.MinIO.RecordsBucket = recordsBucket
cfg.MinIO.LivestreamBucket = liveBucket
cfg.MinIO.PresignTTL = presignTTL
cfg.JWTSecret = []byte(jwt)
cfg.MediaMTX = MediaMTXConfig{
APIBase: apiBase,
WebRTCBaseURL: webrtcBase,
PublicBaseURL: publicBase,
TokenTTL: time.Duration(tokenTTL),
}
cfg.PkiIot = *pki
return cfg, nil
}
func LoadDev() (*Config, error) {
getRequired := func(k string) (string, error) {
v := os.Getenv(k)
if v == "" {
return "", fmt.Errorf("missing required env %s", k)
}
return v, nil
}
addr := os.Getenv("VAULT_ADDR")
token := os.Getenv("VAULT_TOKEN")
pki, err := vault.NewPKI(addr, token, "pki_iot", "device", 30*time.Second)
if err != nil {
return nil, err
}
getBoolEnv := func(k string, def bool) bool {
v := strings.ToLower(strings.TrimSpace(os.Getenv(k)))
if v == "true" || v == "1" || v == "yes" {
return true
}
if v == "false" || v == "0" || v == "no" {
return false
}
return def
}
getIntEnv := func(k string, def int) int {
if v := strings.TrimSpace(os.Getenv(k)); v != "" {
if n, err := strconv.Atoi(v); err == nil {
return n
}
}
return def
}
dbDSN, err := getRequired("DB_DSN")
if err != nil {
return nil, err
}
endpoint, err := getRequired("MINIO_ENDPOINT")
if err != nil {
return nil, err
}
ak, err := getRequired("MINIO_ACCESS_KEY")
if err != nil {
return nil, err
}
sk, err := getRequired("MINIO_SECRET_KEY")
if err != nil {
return nil, err
}
jwt, err := getRequired("JWT_SECRET")
if err != nil {
return nil, err
}
useSSL := getBoolEnv("MINIO_USE_SSL", false)
recordsBucket := os.Getenv("MINIO_RECORDS_BUCKET")
if recordsBucket == "" {
recordsBucket = "records"
}
liveBucket := os.Getenv("MINIO_LIVESTREAM_BUCKET")
if liveBucket == "" {
liveBucket = "livestream"
}
presignTTL := time.Duration(getIntEnv("MINIO_PRESIGN_TTL_SECONDS", 900)) * time.Second
// NEW: MediaMTX envs
apiBase, err := getRequired("MEDIAMTX_API_BASE")
if err != nil {
return nil, err
}
webrtcBase, err := getRequired("MEDIAMTX_WEBRTC_BASE_URL")
if err != nil {
return nil, err
}
publicBase, err := getRequired("PUBLIC_BASE_URL")
if err != nil {
return nil, err
}
tokenTTL := getIntEnv("MEDIAMTX_TOKEN_TTL_SECONDS", 180)
cfg := &Config{}
cfg.DB.DSN = dbDSN
cfg.MinIO.Endpoint = endpoint
cfg.MinIO.AccessKey = ak
cfg.MinIO.SecretKey = sk
cfg.MinIO.UseSSL = useSSL
cfg.MinIO.RecordsBucket = recordsBucket
cfg.MinIO.LivestreamBucket = liveBucket
cfg.MinIO.PresignTTL = presignTTL
cfg.JWTSecret = []byte(jwt)
cfg.MediaMTX = MediaMTXConfig{
APIBase: apiBase,
WebRTCBaseURL: webrtcBase,
PublicBaseURL: publicBase,
TokenTTL: time.Duration(tokenTTL),
}
cfg.PkiIot = *pki
return cfg, nil
}