99 lines
2.7 KiB
Go
99 lines
2.7 KiB
Go
package bulk
|
|
|
|
import (
|
|
"fmt"
|
|
"net/url"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type Config struct {
|
|
ListenAddress string
|
|
CoreInternalURL string
|
|
DisclosureBaseURL string
|
|
GeneratorURL string
|
|
PublicName string
|
|
MaxURLs int
|
|
Workers int
|
|
RequestTimeout time.Duration
|
|
}
|
|
|
|
func ConfigFromEnv() Config {
|
|
return Config{
|
|
ListenAddress: env("BULK_LISTEN_ADDRESS", ":8081"),
|
|
CoreInternalURL: strings.TrimRight(env("CORE_INTERNAL_URL", "http://app:8080"), "/"),
|
|
DisclosureBaseURL: strings.TrimRight(env("DISCLOSURE_BASE_URL", "http://localhost:8080"), "/"),
|
|
GeneratorURL: strings.TrimRight(env("GENERATOR_URL", env("DISCLOSURE_BASE_URL", "http://localhost:8080")), "/"),
|
|
PublicName: env("BULK_PUBLIC_NAME", "AI Disclosure Bulk"),
|
|
MaxURLs: intEnv("BULK_MAX_URLS", 500),
|
|
Workers: intEnv("BULK_WORKERS", 4),
|
|
RequestTimeout: durationEnv("BULK_REQUEST_TIMEOUT", 8*time.Second),
|
|
}
|
|
}
|
|
|
|
func ValidateConfig(cfg Config) error {
|
|
for name, value := range map[string]string{
|
|
"CORE_INTERNAL_URL": cfg.CoreInternalURL,
|
|
"DISCLOSURE_BASE_URL": cfg.DisclosureBaseURL,
|
|
"GENERATOR_URL": cfg.GeneratorURL,
|
|
} {
|
|
if err := validateOrigin(name, value); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if strings.TrimSpace(cfg.PublicName) == "" {
|
|
return fmt.Errorf("BULK_PUBLIC_NAME must not be empty")
|
|
}
|
|
if cfg.MaxURLs < 1 || cfg.MaxURLs > 5000 {
|
|
return fmt.Errorf("BULK_MAX_URLS must be between 1 and 5000")
|
|
}
|
|
if cfg.Workers < 1 || cfg.Workers > 32 {
|
|
return fmt.Errorf("BULK_WORKERS must be between 1 and 32")
|
|
}
|
|
if cfg.RequestTimeout < time.Second || cfg.RequestTimeout > 60*time.Second {
|
|
return fmt.Errorf("BULK_REQUEST_TIMEOUT must be between 1s and 60s")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validateOrigin(name, value string) error {
|
|
u, err := url.Parse(value)
|
|
if err != nil || (u.Scheme != "http" && u.Scheme != "https") || u.Host == "" || u.User != nil || (u.Path != "" && u.Path != "/") || u.RawQuery != "" || u.Fragment != "" {
|
|
return fmt.Errorf("%s must be an absolute http(s) origin without path, query, credentials or fragment", name)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func env(key, fallback string) string {
|
|
if value := strings.TrimSpace(os.Getenv(key)); value != "" {
|
|
return value
|
|
}
|
|
return fallback
|
|
}
|
|
|
|
func intEnv(key string, fallback int) int {
|
|
value := strings.TrimSpace(os.Getenv(key))
|
|
if value == "" {
|
|
return fallback
|
|
}
|
|
parsed, err := strconv.Atoi(value)
|
|
if err != nil {
|
|
return fallback
|
|
}
|
|
return parsed
|
|
}
|
|
|
|
func durationEnv(key string, fallback time.Duration) time.Duration {
|
|
value := strings.TrimSpace(os.Getenv(key))
|
|
if value == "" {
|
|
return fallback
|
|
}
|
|
parsed, err := time.ParseDuration(value)
|
|
if err != nil {
|
|
return fallback
|
|
}
|
|
return parsed
|
|
}
|