mirror of
https://github.com/DeBrosOfficial/orama.git
synced 2026-03-17 10:46:58 +00:00
294 lines
9.0 KiB
Go
294 lines
9.0 KiB
Go
package main
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/DeBrosOfficial/network/pkg/config"
|
|
"github.com/DeBrosOfficial/network/pkg/gateway"
|
|
"github.com/DeBrosOfficial/network/pkg/logging"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// For transition, alias main.GatewayConfig to pkg/gateway.Config
|
|
// server.go will be removed; this keeps compatibility until then.
|
|
type GatewayConfig = gateway.Config
|
|
|
|
func getEnvDefault(key, def string) string {
|
|
if v := os.Getenv(key); strings.TrimSpace(v) != "" {
|
|
return v
|
|
}
|
|
return def
|
|
}
|
|
|
|
func getEnvBoolDefault(key string, def bool) bool {
|
|
v := strings.TrimSpace(os.Getenv(key))
|
|
if v == "" {
|
|
return def
|
|
}
|
|
switch strings.ToLower(v) {
|
|
case "1", "true", "t", "yes", "y", "on":
|
|
return true
|
|
case "0", "false", "f", "no", "n", "off":
|
|
return false
|
|
default:
|
|
return def
|
|
}
|
|
}
|
|
|
|
// parseGatewayConfig loads gateway.yaml from ~/.orama exclusively.
|
|
// It accepts an optional --config flag for absolute paths (used by systemd services).
|
|
func parseGatewayConfig(logger *logging.ColoredLogger) *gateway.Config {
|
|
// Parse --config flag (optional, for systemd services that pass absolute paths)
|
|
configFlag := flag.String("config", "", "Config file path (absolute path or filename in ~/.orama)")
|
|
flag.Parse()
|
|
|
|
// Determine config path
|
|
var configPath string
|
|
var err error
|
|
if *configFlag != "" {
|
|
// If --config flag is provided, use it (handles both absolute and relative paths)
|
|
if filepath.IsAbs(*configFlag) {
|
|
configPath = *configFlag
|
|
} else {
|
|
configPath, err = config.DefaultPath(*configFlag)
|
|
if err != nil {
|
|
logger.ComponentError(logging.ComponentGeneral, "Failed to determine config path", zap.Error(err))
|
|
fmt.Fprintf(os.Stderr, "Configuration error: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
} else {
|
|
// Default behavior: look for gateway.yaml in ~/.orama/data/, ~/.orama/configs/, or ~/.orama/
|
|
configPath, err = config.DefaultPath("gateway.yaml")
|
|
if err != nil {
|
|
logger.ComponentError(logging.ComponentGeneral, "Failed to determine config path", zap.Error(err))
|
|
fmt.Fprintf(os.Stderr, "Configuration error: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
// Load YAML
|
|
type yamlICEServer struct {
|
|
URLs []string `yaml:"urls"`
|
|
Username string `yaml:"username,omitempty"`
|
|
Credential string `yaml:"credential,omitempty"`
|
|
}
|
|
|
|
type yamlTURN struct {
|
|
SharedSecret string `yaml:"shared_secret"`
|
|
TTL string `yaml:"ttl"`
|
|
ExternalHost string `yaml:"external_host"`
|
|
STUNURLs []string `yaml:"stun_urls"`
|
|
TURNURLs []string `yaml:"turn_urls"`
|
|
}
|
|
|
|
type yamlSFU struct {
|
|
Enabled bool `yaml:"enabled"`
|
|
MaxParticipants int `yaml:"max_participants"`
|
|
MediaTimeout string `yaml:"media_timeout"`
|
|
ICEServers []yamlICEServer `yaml:"ice_servers"`
|
|
}
|
|
|
|
type yamlCfg struct {
|
|
ListenAddr string `yaml:"listen_addr"`
|
|
ClientNamespace string `yaml:"client_namespace"`
|
|
RQLiteDSN string `yaml:"rqlite_dsn"`
|
|
Peers []string `yaml:"bootstrap_peers"`
|
|
EnableHTTPS bool `yaml:"enable_https"`
|
|
DomainName string `yaml:"domain_name"`
|
|
TLSCacheDir string `yaml:"tls_cache_dir"`
|
|
OlricServers []string `yaml:"olric_servers"`
|
|
OlricTimeout string `yaml:"olric_timeout"`
|
|
IPFSClusterAPIURL string `yaml:"ipfs_cluster_api_url"`
|
|
IPFSAPIURL string `yaml:"ipfs_api_url"`
|
|
IPFSTimeout string `yaml:"ipfs_timeout"`
|
|
IPFSReplicationFactor int `yaml:"ipfs_replication_factor"`
|
|
TURN yamlTURN `yaml:"turn"`
|
|
SFU yamlSFU `yaml:"sfu"`
|
|
}
|
|
|
|
data, err := os.ReadFile(configPath)
|
|
if err != nil {
|
|
logger.ComponentError(logging.ComponentGeneral, "Config file not found",
|
|
zap.String("path", configPath),
|
|
zap.Error(err))
|
|
fmt.Fprintf(os.Stderr, "\nConfig file not found at %s\n", configPath)
|
|
fmt.Fprintf(os.Stderr, "Generate it using: dbn config init --type gateway\n")
|
|
os.Exit(1)
|
|
}
|
|
|
|
var y yamlCfg
|
|
// Use strict YAML decoding to reject unknown fields
|
|
if err := config.DecodeStrict(strings.NewReader(string(data)), &y); err != nil {
|
|
logger.ComponentError(logging.ComponentGeneral, "Failed to parse gateway config", zap.Error(err))
|
|
fmt.Fprintf(os.Stderr, "Configuration parse error: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Build config from YAML
|
|
cfg := &gateway.Config{
|
|
ListenAddr: ":6001",
|
|
ClientNamespace: "default",
|
|
BootstrapPeers: nil,
|
|
RQLiteDSN: "",
|
|
EnableHTTPS: false,
|
|
DomainName: "",
|
|
TLSCacheDir: "",
|
|
OlricServers: nil,
|
|
OlricTimeout: 0,
|
|
IPFSClusterAPIURL: "",
|
|
IPFSAPIURL: "",
|
|
IPFSTimeout: 0,
|
|
IPFSReplicationFactor: 0,
|
|
}
|
|
|
|
if v := strings.TrimSpace(y.ListenAddr); v != "" {
|
|
cfg.ListenAddr = v
|
|
}
|
|
if v := strings.TrimSpace(y.ClientNamespace); v != "" {
|
|
cfg.ClientNamespace = v
|
|
}
|
|
if v := strings.TrimSpace(y.RQLiteDSN); v != "" {
|
|
cfg.RQLiteDSN = v
|
|
}
|
|
if len(y.Peers) > 0 {
|
|
var peers []string
|
|
for _, p := range y.Peers {
|
|
p = strings.TrimSpace(p)
|
|
if p != "" {
|
|
peers = append(peers, p)
|
|
}
|
|
}
|
|
if len(peers) > 0 {
|
|
cfg.BootstrapPeers = peers
|
|
}
|
|
}
|
|
|
|
// HTTPS configuration
|
|
cfg.EnableHTTPS = y.EnableHTTPS
|
|
if v := strings.TrimSpace(y.DomainName); v != "" {
|
|
cfg.DomainName = v
|
|
}
|
|
if v := strings.TrimSpace(y.TLSCacheDir); v != "" {
|
|
cfg.TLSCacheDir = v
|
|
} else if cfg.EnableHTTPS {
|
|
// Default TLS cache directory if HTTPS is enabled but not specified
|
|
homeDir, err := os.UserHomeDir()
|
|
if err == nil {
|
|
cfg.TLSCacheDir = filepath.Join(homeDir, ".orama", "tls-cache")
|
|
}
|
|
}
|
|
|
|
// Olric configuration
|
|
if len(y.OlricServers) > 0 {
|
|
cfg.OlricServers = y.OlricServers
|
|
}
|
|
if v := strings.TrimSpace(y.OlricTimeout); v != "" {
|
|
if parsed, err := time.ParseDuration(v); err == nil {
|
|
cfg.OlricTimeout = parsed
|
|
} else {
|
|
logger.ComponentWarn(logging.ComponentGeneral, "invalid olric_timeout, using default", zap.String("value", v), zap.Error(err))
|
|
}
|
|
}
|
|
|
|
// IPFS configuration
|
|
if v := strings.TrimSpace(y.IPFSClusterAPIURL); v != "" {
|
|
cfg.IPFSClusterAPIURL = v
|
|
}
|
|
if v := strings.TrimSpace(y.IPFSAPIURL); v != "" {
|
|
cfg.IPFSAPIURL = v
|
|
}
|
|
if v := strings.TrimSpace(y.IPFSTimeout); v != "" {
|
|
if parsed, err := time.ParseDuration(v); err == nil {
|
|
cfg.IPFSTimeout = parsed
|
|
} else {
|
|
logger.ComponentWarn(logging.ComponentGeneral, "invalid ipfs_timeout, using default", zap.String("value", v), zap.Error(err))
|
|
}
|
|
}
|
|
if y.IPFSReplicationFactor > 0 {
|
|
cfg.IPFSReplicationFactor = y.IPFSReplicationFactor
|
|
}
|
|
|
|
// TURN configuration
|
|
if y.TURN.SharedSecret != "" || len(y.TURN.STUNURLs) > 0 || len(y.TURN.TURNURLs) > 0 {
|
|
turnCfg := &config.TURNConfig{
|
|
SharedSecret: y.TURN.SharedSecret,
|
|
ExternalHost: y.TURN.ExternalHost,
|
|
STUNURLs: y.TURN.STUNURLs,
|
|
TURNURLs: y.TURN.TURNURLs,
|
|
}
|
|
// Check for environment variable overrides
|
|
if envSecret := os.Getenv("TURN_SHARED_SECRET"); envSecret != "" {
|
|
turnCfg.SharedSecret = envSecret
|
|
}
|
|
if envHost := os.Getenv("TURN_EXTERNAL_HOST"); envHost != "" {
|
|
turnCfg.ExternalHost = envHost
|
|
}
|
|
if v := strings.TrimSpace(y.TURN.TTL); v != "" {
|
|
if parsed, err := time.ParseDuration(v); err == nil {
|
|
turnCfg.TTL = parsed
|
|
} else {
|
|
logger.ComponentWarn(logging.ComponentGeneral, "invalid turn.ttl, using default", zap.String("value", v), zap.Error(err))
|
|
}
|
|
}
|
|
cfg.TURN = turnCfg
|
|
logger.ComponentInfo(logging.ComponentGeneral, "TURN configuration loaded",
|
|
zap.Int("stun_urls", len(turnCfg.STUNURLs)),
|
|
zap.Int("turn_urls", len(turnCfg.TURNURLs)),
|
|
zap.String("external_host", turnCfg.ExternalHost),
|
|
)
|
|
}
|
|
|
|
// SFU configuration
|
|
if y.SFU.Enabled {
|
|
sfuCfg := &config.SFUConfig{
|
|
Enabled: true,
|
|
MaxParticipants: y.SFU.MaxParticipants,
|
|
}
|
|
if v := strings.TrimSpace(y.SFU.MediaTimeout); v != "" {
|
|
if parsed, err := time.ParseDuration(v); err == nil {
|
|
sfuCfg.MediaTimeout = parsed
|
|
} else {
|
|
logger.ComponentWarn(logging.ComponentGeneral, "invalid sfu.media_timeout, using default", zap.String("value", v), zap.Error(err))
|
|
}
|
|
}
|
|
// Parse ICE servers
|
|
for _, iceServer := range y.SFU.ICEServers {
|
|
sfuCfg.ICEServers = append(sfuCfg.ICEServers, config.ICEServerConfig{
|
|
URLs: iceServer.URLs,
|
|
Username: iceServer.Username,
|
|
Credential: iceServer.Credential,
|
|
})
|
|
}
|
|
cfg.SFU = sfuCfg
|
|
logger.ComponentInfo(logging.ComponentGeneral, "SFU configuration loaded",
|
|
zap.Int("max_participants", sfuCfg.MaxParticipants),
|
|
zap.Int("ice_servers", len(sfuCfg.ICEServers)),
|
|
)
|
|
}
|
|
|
|
// Validate configuration
|
|
if errs := cfg.ValidateConfig(); len(errs) > 0 {
|
|
fmt.Fprintf(os.Stderr, "\nGateway configuration errors (%d):\n", len(errs))
|
|
for _, err := range errs {
|
|
fmt.Fprintf(os.Stderr, " - %s\n", err)
|
|
}
|
|
fmt.Fprintf(os.Stderr, "\nPlease fix the configuration and try again.\n")
|
|
os.Exit(1)
|
|
}
|
|
|
|
logger.ComponentInfo(logging.ComponentGeneral, "Loaded gateway configuration from YAML",
|
|
zap.String("path", configPath),
|
|
zap.String("addr", cfg.ListenAddr),
|
|
zap.String("namespace", cfg.ClientNamespace),
|
|
zap.Int("peer_count", len(cfg.BootstrapPeers)),
|
|
)
|
|
|
|
return cfg
|
|
}
|