162 lines
3.8 KiB
Go
162 lines
3.8 KiB
Go
package config
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"net/url"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type Config struct {
|
|
ListenAddr string
|
|
AppSecret string
|
|
HTTPSOnly bool
|
|
FrameAncestors []string
|
|
TrustedProxies []*net.IPNet
|
|
TSQueryHost string
|
|
TSQueryPort int
|
|
TSVoiceHost string
|
|
TSVoicePort int
|
|
TSQueryUser string
|
|
TSQueryPassword string
|
|
TSChannelAdminGID int
|
|
TSQueryTimeout time.Duration
|
|
RRDTool string
|
|
RRDPath string
|
|
GraphDir string
|
|
StatsInterval time.Duration
|
|
}
|
|
|
|
func Load() (Config, error) {
|
|
var c Config
|
|
c.ListenAddr = env("LISTEN_ADDR", "127.0.0.1:8080")
|
|
c.AppSecret = os.Getenv("APP_SECRET")
|
|
if len(c.AppSecret) < 32 {
|
|
return c, errors.New("APP_SECRET must contain at least 32 characters")
|
|
}
|
|
c.HTTPSOnly = envBool("HTTPS_ONLY", true)
|
|
|
|
var err error
|
|
c.FrameAncestors, err = frameAncestors(os.Getenv("FRAME_ANCESTORS"))
|
|
if err != nil {
|
|
return c, err
|
|
}
|
|
c.TrustedProxies, err = cidrs(os.Getenv("TRUSTED_PROXIES"))
|
|
if err != nil {
|
|
return c, err
|
|
}
|
|
|
|
c.TSQueryHost = env("TS_QUERY_HOST", "127.0.0.1")
|
|
c.TSQueryPort, err = envInt("TS_QUERY_PORT", 10011)
|
|
if err != nil {
|
|
return c, err
|
|
}
|
|
c.TSVoiceHost = env("TS_VOICE_HOST", "linuxiarz.pl")
|
|
c.TSVoicePort, err = envInt("TS_VOICE_PORT", 9987)
|
|
if err != nil {
|
|
return c, err
|
|
}
|
|
c.TSQueryUser = os.Getenv("TS_QUERY_USER")
|
|
c.TSQueryPassword = os.Getenv("TS_QUERY_PASSWORD")
|
|
if c.TSQueryUser == "" || c.TSQueryPassword == "" {
|
|
return c, errors.New("TS_QUERY_USER and TS_QUERY_PASSWORD are required")
|
|
}
|
|
c.TSChannelAdminGID, err = envInt("TS_CHANNEL_ADMIN_GID", 5)
|
|
if err != nil {
|
|
return c, err
|
|
}
|
|
c.TSQueryTimeout, err = envDuration("TS_QUERY_TIMEOUT", 3*time.Second)
|
|
if err != nil {
|
|
return c, err
|
|
}
|
|
|
|
c.RRDTool = env("RRDTOOL", "rrdtool")
|
|
c.RRDPath = env("RRD_PATH", "/var/lib/ts3monolith/linuxiarz.rrd")
|
|
c.GraphDir = env("GRAPH_DIR", "/var/lib/ts3monolith/graphs")
|
|
c.StatsInterval, err = envDuration("STATS_INTERVAL", time.Minute)
|
|
if err != nil {
|
|
return c, err
|
|
}
|
|
if c.StatsInterval < 10*time.Second {
|
|
return c, errors.New("STATS_INTERVAL must be at least 10s")
|
|
}
|
|
return c, nil
|
|
}
|
|
|
|
func env(k, d string) string {
|
|
if v := strings.TrimSpace(os.Getenv(k)); v != "" {
|
|
return v
|
|
}
|
|
return d
|
|
}
|
|
|
|
func envBool(k string, d bool) bool {
|
|
v := strings.TrimSpace(os.Getenv(k))
|
|
if v == "" {
|
|
return d
|
|
}
|
|
b, err := strconv.ParseBool(v)
|
|
if err != nil {
|
|
return d
|
|
}
|
|
return b
|
|
}
|
|
|
|
func envInt(k string, d int) (int, error) {
|
|
v := strings.TrimSpace(os.Getenv(k))
|
|
if v == "" {
|
|
return d, nil
|
|
}
|
|
n, err := strconv.Atoi(v)
|
|
if err != nil || n < 1 || n > 65535 {
|
|
return 0, fmt.Errorf("invalid %s", k)
|
|
}
|
|
return n, nil
|
|
}
|
|
|
|
func envDuration(k string, d time.Duration) (time.Duration, error) {
|
|
v := strings.TrimSpace(os.Getenv(k))
|
|
if v == "" {
|
|
return d, nil
|
|
}
|
|
x, err := time.ParseDuration(v)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("invalid %s: %w", k, err)
|
|
}
|
|
return x, nil
|
|
}
|
|
|
|
func frameAncestors(raw string) ([]string, error) {
|
|
if strings.TrimSpace(raw) == "" {
|
|
return []string{"'self'"}, nil
|
|
}
|
|
out := []string{"'self'"}
|
|
for _, part := range strings.Fields(raw) {
|
|
u, err := url.Parse(part)
|
|
if err != nil || (u.Scheme != "https" && u.Scheme != "http") || u.Host == "" || u.Path != "" || u.RawQuery != "" || u.Fragment != "" {
|
|
return nil, fmt.Errorf("invalid FRAME_ANCESTORS origin: %q", part)
|
|
}
|
|
out = append(out, u.Scheme+"://"+u.Host)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func cidrs(raw string) ([]*net.IPNet, error) {
|
|
var out []*net.IPNet
|
|
for _, part := range strings.FieldsFunc(raw, func(r rune) bool { return r == ',' || r == ' ' || r == '\t' || r == '\n' }) {
|
|
if strings.TrimSpace(part) == "" {
|
|
continue
|
|
}
|
|
_, n, err := net.ParseCIDR(strings.TrimSpace(part))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid TRUSTED_PROXIES entry %q", part)
|
|
}
|
|
out = append(out, n)
|
|
}
|
|
return out, nil
|
|
}
|