134 lines
3.5 KiB
Go
134 lines
3.5 KiB
Go
package app
|
|
|
|
import (
|
|
"fmt"
|
|
"net/url"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type Config struct {
|
|
ListenAddr string
|
|
PublicURL *url.URL
|
|
DatabaseDSN string
|
|
GiteaURL *url.URL
|
|
GiteaClientID string
|
|
GiteaClientSecret string
|
|
GiteaPackageOwner string
|
|
GiteaPackageUser string
|
|
GiteaPackageToken string
|
|
AllowedGiteaUsers map[string]struct{}
|
|
CookieSecret []byte
|
|
DefaultDuration time.Duration
|
|
DefaultHostLimit int
|
|
MaxHostLimit int
|
|
TrustProxyHeaders bool
|
|
}
|
|
|
|
func LoadConfig() (Config, error) {
|
|
var cfg Config
|
|
var err error
|
|
|
|
cfg.ListenAddr = envDefault("TAPM_LISTEN_ADDR", ":8080")
|
|
cfg.DatabaseDSN = os.Getenv("TAPM_DATABASE_DSN")
|
|
cfg.GiteaClientID = os.Getenv("TAPM_GITEA_CLIENT_ID")
|
|
cfg.GiteaClientSecret = os.Getenv("TAPM_GITEA_CLIENT_SECRET")
|
|
cfg.GiteaPackageOwner = envDefault("TAPM_GITEA_PACKAGE_OWNER", "TAI")
|
|
cfg.GiteaPackageUser = os.Getenv("TAPM_GITEA_PACKAGE_USERNAME")
|
|
cfg.GiteaPackageToken = os.Getenv("TAPM_GITEA_PACKAGE_TOKEN")
|
|
cfg.CookieSecret = []byte(os.Getenv("TAPM_COOKIE_SECRET"))
|
|
|
|
cfg.PublicURL, err = parseAbsoluteURL("TAPM_PUBLIC_URL")
|
|
if err != nil {
|
|
return cfg, err
|
|
}
|
|
cfg.GiteaURL, err = parseAbsoluteURL("TAPM_GITEA_URL")
|
|
if err != nil {
|
|
return cfg, err
|
|
}
|
|
|
|
cfg.DefaultDuration, err = time.ParseDuration(
|
|
envDefault("TAPM_DEFAULT_DURATION", "3h"),
|
|
)
|
|
if err != nil || cfg.DefaultDuration <= 0 {
|
|
return cfg, fmt.Errorf("TAPM_DEFAULT_DURATION must be positive")
|
|
}
|
|
cfg.DefaultHostLimit, err = envInt("TAPM_DEFAULT_HOST_LIMIT", 3)
|
|
if err != nil {
|
|
return cfg, err
|
|
}
|
|
cfg.MaxHostLimit, err = envInt("TAPM_MAX_HOST_LIMIT", 25)
|
|
if err != nil {
|
|
return cfg, err
|
|
}
|
|
if cfg.DefaultHostLimit < 1 || cfg.MaxHostLimit < cfg.DefaultHostLimit {
|
|
return cfg, fmt.Errorf("invalid default or maximum host limit")
|
|
}
|
|
|
|
cfg.TrustProxyHeaders, err = strconv.ParseBool(
|
|
envDefault("TAPM_TRUST_PROXY_HEADERS", "false"),
|
|
)
|
|
if err != nil {
|
|
return cfg, fmt.Errorf("TAPM_TRUST_PROXY_HEADERS: %w", err)
|
|
}
|
|
|
|
cfg.AllowedGiteaUsers = make(map[string]struct{})
|
|
for _, user := range strings.Split(
|
|
envDefault("TAPM_ALLOWED_GITEA_USERS", "taiadmin"),
|
|
",",
|
|
) {
|
|
user = strings.ToLower(strings.TrimSpace(user))
|
|
if user != "" {
|
|
cfg.AllowedGiteaUsers[user] = struct{}{}
|
|
}
|
|
}
|
|
|
|
required := map[string]string{
|
|
"TAPM_DATABASE_DSN": cfg.DatabaseDSN,
|
|
"TAPM_GITEA_CLIENT_ID": cfg.GiteaClientID,
|
|
"TAPM_GITEA_CLIENT_SECRET": cfg.GiteaClientSecret,
|
|
"TAPM_GITEA_PACKAGE_USERNAME": cfg.GiteaPackageUser,
|
|
"TAPM_GITEA_PACKAGE_TOKEN": cfg.GiteaPackageToken,
|
|
}
|
|
for name, value := range required {
|
|
if strings.TrimSpace(value) == "" {
|
|
return cfg, fmt.Errorf("%s is required", name)
|
|
}
|
|
}
|
|
if len(cfg.CookieSecret) < 32 {
|
|
return cfg, fmt.Errorf("TAPM_COOKIE_SECRET must contain at least 32 bytes")
|
|
}
|
|
if len(cfg.AllowedGiteaUsers) == 0 {
|
|
return cfg, fmt.Errorf("at least one Gitea user must be allowed")
|
|
}
|
|
|
|
return cfg, nil
|
|
}
|
|
|
|
func parseAbsoluteURL(name string) (*url.URL, error) {
|
|
raw := os.Getenv(name)
|
|
parsed, err := url.Parse(raw)
|
|
if err != nil || parsed.Scheme != "https" || parsed.Host == "" {
|
|
return nil, fmt.Errorf("%s must be an absolute HTTPS URL", name)
|
|
}
|
|
return parsed, nil
|
|
}
|
|
|
|
func envDefault(name, fallback string) string {
|
|
if value := os.Getenv(name); value != "" {
|
|
return value
|
|
}
|
|
return fallback
|
|
}
|
|
|
|
func envInt(name string, fallback int) (int, error) {
|
|
raw := envDefault(name, strconv.Itoa(fallback))
|
|
value, err := strconv.Atoi(raw)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("%s must be an integer", name)
|
|
}
|
|
return value, nil
|
|
}
|