83 lines
2.4 KiB
Go
83 lines
2.4 KiB
Go
// Package config loads the game server configuration from environment
|
|
// variables.
|
|
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type Config struct {
|
|
// ListenAddr is the address the websocket HTTP server binds to
|
|
// (e.g. ":8081"). The server may sit behind a reverse proxy.
|
|
ListenAddr string
|
|
// PublicAddr is the public endpoint address of this server
|
|
// (e.g. "ws://example.com:8081/ws"). It is used only to identify this
|
|
// server: the "game_server" claim of a game token must match it
|
|
// strictly. The main server must be configured to put exactly this
|
|
// string into the tokens it issues.
|
|
PublicAddr string
|
|
// JWTSecret is the HS256 secret shared with the main server; both the
|
|
// game token and the user token are verified with it.
|
|
JWTSecret []byte
|
|
// JoinTimeout is how long the first player waits for the second one
|
|
// before the match is cancelled.
|
|
JoinTimeout time.Duration
|
|
// AllowedOrigins restricts the Origin header of websocket upgrade
|
|
// requests; ["*"] disables the check.
|
|
AllowedOrigins []string
|
|
}
|
|
|
|
func Load() (Config, error) {
|
|
var cfg Config
|
|
|
|
cfg.ListenAddr = getenv("LISTEN_ADDR", ":8081")
|
|
|
|
cfg.PublicAddr = os.Getenv("PUBLIC_ADDR")
|
|
if cfg.PublicAddr == "" {
|
|
return cfg, fmt.Errorf("PUBLIC_ADDR is required (public endpoint address, e.g. ws://example.com:8081/ws)")
|
|
}
|
|
|
|
secret := os.Getenv("JWT_SECRET")
|
|
if secret == "" {
|
|
return cfg, fmt.Errorf("JWT_SECRET is required (HS256 secret shared with the main server)")
|
|
}
|
|
cfg.JWTSecret = []byte(secret)
|
|
|
|
joinTimeout, err := parseDuration(getenv("JOIN_TIMEOUT", "2m"))
|
|
if err != nil {
|
|
return cfg, fmt.Errorf("invalid JOIN_TIMEOUT: %w", err)
|
|
}
|
|
cfg.JoinTimeout = joinTimeout
|
|
|
|
origins := getenv("ALLOWED_ORIGINS", "*")
|
|
cfg.AllowedOrigins = strings.Split(origins, ",")
|
|
for i := range cfg.AllowedOrigins {
|
|
cfg.AllowedOrigins[i] = strings.TrimSpace(cfg.AllowedOrigins[i])
|
|
}
|
|
|
|
return cfg, nil
|
|
}
|
|
|
|
func getenv(key, fallback string) string {
|
|
if v := os.Getenv(key); v != "" {
|
|
return v
|
|
}
|
|
return fallback
|
|
}
|
|
|
|
// parseDuration accepts either a Go duration string ("90s", "2m") or a plain
|
|
// number of seconds ("120").
|
|
func parseDuration(s string) (time.Duration, error) {
|
|
if d, err := time.ParseDuration(s); err == nil {
|
|
return d, nil
|
|
}
|
|
if secs, err := strconv.Atoi(s); err == nil {
|
|
return time.Duration(secs) * time.Second, nil
|
|
}
|
|
return 0, fmt.Errorf("not a duration: %q", s)
|
|
}
|