// Package config provides shared configuration types. package config import ( "crypto/rand" "encoding/hex" "fmt" "os" "strconv" "strings" ) // Version info (set via -ldflags) var ( Version = "0.1.0" GitCommit = "unknown" BuildTime = "unknown" GoVersion = "unknown" ) const ( DefaultWSPort = 27183 // WSS signaling DefaultSTUNUDP1 = 27182 // UDP STUN port 1 DefaultSTUNUDP2 = 27183 // UDP STUN port 2 DefaultSTUNTCP1 = 27180 // TCP STUN port 1 DefaultSTUNTCP2 = 27181 // TCP STUN port 2 DefaultWebPort = 10088 // Web console DefaultAPIPort = 10008 // REST API DefaultMaxRelayLoad = 20 DefaultRelayPort = 27185 HeartbeatInterval = 30 // seconds HeartbeatTimeout = 90 // seconds — 3x missed heartbeats → offline ) // ServerConfig holds inp2ps configuration. type ServerConfig struct { WSPort int `json:"wsPort"` STUNUDP1 int `json:"stunUDP1"` STUNUDP2 int `json:"stunUDP2"` STUNTCP1 int `json:"stunTCP1"` STUNTCP2 int `json:"stunTCP2"` WebPort int `json:"webPort"` APIPort int `json:"apiPort"` DBPath string `json:"dbPath"` CertFile string `json:"certFile"` KeyFile string `json:"keyFile"` LogLevel int `json:"logLevel"` // 0=debug, 1=info, 2=warn, 3=error Token uint64 `json:"token"` // master token for auth Tokens []uint64 `json:"tokens"` // additional tenant tokens JWTKey string `json:"jwtKey"` // auto-generated if empty AdminUser string `json:"adminUser"` AdminPass string `json:"adminPass"` } func DefaultServerConfig() ServerConfig { return ServerConfig{ WSPort: DefaultWSPort, STUNUDP1: DefaultSTUNUDP1, STUNUDP2: DefaultSTUNUDP2, STUNTCP1: DefaultSTUNTCP1, STUNTCP2: DefaultSTUNTCP2, WebPort: DefaultWebPort, APIPort: DefaultAPIPort, DBPath: "inp2ps.db", LogLevel: 1, AdminUser: "admin", AdminPass: "admin123", } } func (c *ServerConfig) FillFromEnv() { if v := os.Getenv("INP2PS_WS_PORT"); v != "" { c.WSPort, _ = strconv.Atoi(v) } if v := os.Getenv("INP2PS_WEB_PORT"); v != "" { c.WebPort, _ = strconv.Atoi(v) } if v := os.Getenv("INP2PS_DB_PATH"); v != "" { c.DBPath = v } if v := os.Getenv("INP2PS_TOKEN"); v != "" { c.Token, _ = strconv.ParseUint(v, 10, 64) } if v := os.Getenv("INP2PS_TOKENS"); v != "" { parts := strings.Split(v, ",") for _, p := range parts { p = strings.TrimSpace(p) if p == "" { continue } if tv, err := strconv.ParseUint(p, 10, 64); err == nil { c.Tokens = append(c.Tokens, tv) } } } if v := os.Getenv("INP2PS_CERT"); v != "" { c.CertFile = v } if v := os.Getenv("INP2PS_KEY"); v != "" { c.KeyFile = v } if c.JWTKey == "" { b := make([]byte, 32) rand.Read(b) c.JWTKey = hex.EncodeToString(b) } } func (c *ServerConfig) Validate() error { if c.Token == 0 && len(c.Tokens) == 0 { return fmt.Errorf("token is required (INP2PS_TOKEN or INP2PS_TOKENS)") } return nil } // ClientConfig holds inp2pc configuration. type ClientConfig struct { ServerHost string `json:"serverHost"` ServerPort int `json:"serverPort"` Node string `json:"node"` Token uint64 `json:"token"` NodeSecret string `json:"nodeSecret,omitempty"` User string `json:"user,omitempty"` Insecure bool `json:"insecure"` // skip TLS verify // STUN ports (defaults match server defaults) STUNUDP1 int `json:"stunUDP1,omitempty"` STUNUDP2 int `json:"stunUDP2,omitempty"` STUNTCP1 int `json:"stunTCP1,omitempty"` STUNTCP2 int `json:"stunTCP2,omitempty"` RelayEnabled bool `json:"relayEnabled"` // --relay SuperRelay bool `json:"superRelay"` // --super RelayOfficial bool `json:"relayOfficial"` // official relay tag RelayPort int `json:"relayPort"` MaxRelayLoad int `json:"maxRelayLoad"` ShareBandwidth int `json:"shareBandwidth"` // Mbps LogLevel int `json:"logLevel"` Apps []AppConfig `json:"apps"` } type AppConfig struct { AppName string `json:"appName"` Protocol string `json:"protocol"` // tcp, udp SrcPort int `json:"srcPort"` PeerNode string `json:"peerNode"` DstHost string `json:"dstHost"` DstPort int `json:"dstPort"` Enabled bool `json:"enabled"` } func DefaultClientConfig() ClientConfig { return ClientConfig{ ServerPort: DefaultWSPort, STUNUDP1: DefaultSTUNUDP1, STUNUDP2: DefaultSTUNUDP2, STUNTCP1: DefaultSTUNTCP1, STUNTCP2: DefaultSTUNTCP2, ShareBandwidth: 10, RelayPort: DefaultRelayPort, MaxRelayLoad: DefaultMaxRelayLoad, RelayEnabled: true, RelayOfficial: false, LogLevel: 1, } } func (c *ClientConfig) Validate() error { if c.ServerHost == "" { return fmt.Errorf("serverHost is required") } if c.Token == 0 && c.NodeSecret == "" { return fmt.Errorf("token or nodeSecret is required") } if c.Node == "" { hostname, _ := os.Hostname() c.Node = hostname } return nil }