package app import ( "bytes" "io" "os" "strings" "git.sendico.io/tech/sxchnge/notifications/bus" "git.sendico.io/tech/sxchnge/notifications/email" db "git.sendico.io/tech/sxchnge/shared/database" merrors "git.sendico.io/tech/sxchnge/shared/errors" "git.sendico.io/tech/sxchnge/shared/messaging" "gopkg.in/yaml.v3" ) type Config struct { Queue bus.Config `yaml:"queue"` Messaging messaging.Config `yaml:"messaging"` Listen string `yaml:"listen"` TLSCert string `yaml:"tls_cert"` TLSKey string `yaml:"tls_key"` Database db.Config `yaml:"database"` Templates string `yaml:"templates"` Callers map[string]string `yaml:"callers"` // caller -> token environment variable Email email.Config `yaml:"email"` } func Load(path string) (Config, error) { c := Config{Queue: bus.DefaultConfig()} data, err := os.ReadFile(path) if err != nil { return c, merrors.OpaqueWrap(merrors.ErrInvalidArg, err, "cannot read configuration") } decoder := yaml.NewDecoder(bytes.NewReader(data)) decoder.KnownFields(true) if err = decoder.Decode(&c); err != nil { return c, merrors.InvalidArgument("invalid configuration") } if err = decoder.Decode(new(any)); err != io.EOF { return c, merrors.InvalidArgument("unexpected configuration content") } if c.Listen == "" || c.Database.Driver != db.Mongo || c.Messaging.Driver != messaging.BBNats || c.Templates == "" || len(c.Callers) == 0 { return c, merrors.InvalidArgument("missing notification configuration") } if (c.TLSCert == "") != (c.TLSKey == "") { return c, merrors.InvalidArgument("both TLS certificate and key must be configured together") } if err = c.Queue.Validate(); err != nil { return c, err } seen := map[string]bool{} for caller, env := range c.Callers { token := os.Getenv(env) if strings.TrimSpace(caller) == "" || len(strings.TrimSpace(token)) < 32 || seen[token] { return c, merrors.InvalidArgument("caller credentials must be unique and at least 32 bytes") } seen[token] = true } return c, nil }