package config import ( "fmt" "os" "time" "gopkg.in/yaml.v2" ) // Config represents the unified configuration for all services type Config struct { Mode string `yaml:"mode"` Gateway GatewayConfig `yaml:"gateway"` BlossomServer BlossomServerConfig `yaml:"blossom_server"` DHT DHTConfig `yaml:"dht"` Storage StorageConfig `yaml:"storage"` Blossom BlossomConfig `yaml:"blossom"` Torrent TorrentConfig `yaml:"torrent"` Tracker TrackerConfig `yaml:"tracker"` Nostr NostrConfig `yaml:"nostr"` Proxy ProxyConfig `yaml:"proxy"` Admin AdminConfig `yaml:"admin"` RateLimiting RateLimitingConfig `yaml:"rate_limiting"` Branding BrandingConfig `yaml:"branding"` Transcoding TranscodingConfig `yaml:"transcoding"` } // GatewayConfig configures the HTTP API gateway type GatewayConfig struct { Enabled bool `yaml:"enabled"` Port int `yaml:"port"` MaxUploadSize string `yaml:"max_upload_size"` } // BlossomServerConfig configures the embedded Blossom server type BlossomServerConfig struct { Enabled bool `yaml:"enabled"` Port int `yaml:"port"` StoragePath string `yaml:"storage_path"` MaxBlobSize string `yaml:"max_blob_size"` RateLimit RateLimit `yaml:"rate_limit"` } // RateLimit configures rate limiting for the Blossom server type RateLimit struct { RequestsPerMinute int `yaml:"requests_per_minute"` BurstSize int `yaml:"burst_size"` } // DHTConfig configures the DHT node type DHTConfig struct { Enabled bool `yaml:"enabled"` Port int `yaml:"port"` NodeID string `yaml:"node_id"` // auto-generate if empty BootstrapSelf bool `yaml:"bootstrap_self"` // add self as bootstrap node BootstrapNodes []string `yaml:"bootstrap_nodes"` AnnounceInterval time.Duration `yaml:"announce_interval"` // torrent announce interval CleanupInterval time.Duration `yaml:"cleanup_interval"` // node cleanup interval MaxTorrents int `yaml:"max_torrents"` // max torrents to track MaxNodes int `yaml:"max_nodes"` // max nodes to store MaxPeersPerTorrent int `yaml:"max_peers_per_torrent"` } // StorageConfig configures shared storage settings type StorageConfig struct { BlobThreshold int64 `yaml:"blob_threshold"` ChunkSize int64 `yaml:"chunk_size"` MetadataDB string `yaml:"metadata_db"` BlobStorage string `yaml:"blob_storage"` ChunkStorage string `yaml:"chunk_storage"` Strategy StorageStrategy `yaml:"strategy"` } // StorageStrategy defines how files should be stored based on size type StorageStrategy struct { SmallFiles string `yaml:"small_files"` // "blob" LargeFiles string `yaml:"large_files"` // "torrent" } // BlossomConfig configures external Blossom servers type BlossomConfig struct { Servers []string `yaml:"servers"` } // TorrentConfig configures BitTorrent settings type TorrentConfig struct { Trackers []string `yaml:"trackers"` } // TrackerConfig configures the built-in BitTorrent tracker type TrackerConfig struct { Enabled bool `yaml:"enabled"` AnnounceInterval int `yaml:"announce_interval"` // seconds MinInterval int `yaml:"min_interval"` // seconds DefaultNumWant int `yaml:"default_numwant"` // peers to return MaxNumWant int `yaml:"max_numwant"` // maximum peers CleanupInterval time.Duration `yaml:"cleanup_interval"` // cleanup frequency PeerTimeout time.Duration `yaml:"peer_timeout"` // peer expiration } // NostrConfig configures Nostr relay settings type NostrConfig struct { Relays []string `yaml:"relays"` PublishNIP35 bool `yaml:"publish_nip35"` // Publish NIP-35 torrent events for DTAN PublishNIP71 bool `yaml:"publish_nip71"` // Publish NIP-71 video events VideoRelays []string `yaml:"video_relays"` // Specific relays for video content PrivateKey string `yaml:"private_key"` // Hex-encoded private key AutoPublish bool `yaml:"auto_publish"` // Auto-publish on upload ThumbnailsDir string `yaml:"thumbnails_dir"` // Directory for video thumbnails } // ProxyConfig configures smart proxy settings type ProxyConfig struct { Enabled bool `yaml:"enabled"` CacheSize int `yaml:"cache_size"` CacheMaxAge time.Duration `yaml:"cache_max_age"` } // AdminConfig configures admin functionality type AdminConfig struct { Enabled bool `yaml:"enabled"` Pubkeys []string `yaml:"pubkeys"` AutoCleanup bool `yaml:"auto_cleanup"` CleanupAge string `yaml:"cleanup_age"` MaxFileAge string `yaml:"max_file_age"` ReportThreshold int `yaml:"report_threshold"` DefaultUserStorageLimit string `yaml:"default_user_storage_limit"` } // RateLimitingConfig configures rate limiting for different operations type RateLimitingConfig struct { Upload UploadRateConfig `yaml:"upload"` Download DownloadRateConfig `yaml:"download"` Stream StreamRateConfig `yaml:"stream"` Auth AuthRateConfig `yaml:"auth"` } // UploadRateConfig configures upload rate limiting type UploadRateConfig struct { RequestsPerSecond float64 `yaml:"requests_per_second"` BurstSize int `yaml:"burst_size"` MaxFileSize string `yaml:"max_file_size"` } // DownloadRateConfig configures download rate limiting type DownloadRateConfig struct { RequestsPerSecond float64 `yaml:"requests_per_second"` BurstSize int `yaml:"burst_size"` } // StreamRateConfig configures streaming rate limiting type StreamRateConfig struct { RequestsPerSecond float64 `yaml:"requests_per_second"` BurstSize int `yaml:"burst_size"` MaxConcurrent int `yaml:"max_concurrent"` } // AuthRateConfig configures authentication rate limiting type AuthRateConfig struct { LoginAttemptsPerMinute int `yaml:"login_attempts_per_minute"` BurstSize int `yaml:"burst_size"` } // BrandingConfig configures site branding and appearance type BrandingConfig struct { SiteName string `yaml:"site_name"` LogoURL string `yaml:"logo_url"` LogoWidth string `yaml:"logo_width"` LogoHeight string `yaml:"logo_height"` FaviconURL string `yaml:"favicon_url"` Description string `yaml:"description"` FooterText string `yaml:"footer_text"` SupportURL string `yaml:"support_url"` } // TranscodingConfig configures video transcoding type TranscodingConfig struct { Enabled bool `yaml:"enabled"` ConcurrentJobs int `yaml:"concurrent_jobs"` WorkDir string `yaml:"work_dir"` AutoTranscode bool `yaml:"auto_transcode"` MinFileSize string `yaml:"min_file_size"` Qualities []QualityConfig `yaml:"qualities"` Retention RetentionConfig `yaml:"retention"` MaxCPUPercent int `yaml:"max_cpu_percent"` NiceLevel int `yaml:"nice_level"` } // QualityConfig represents a transcoding quality preset type QualityConfig struct { Name string `yaml:"name"` Width int `yaml:"width"` Height int `yaml:"height"` Bitrate string `yaml:"bitrate"` } // RetentionConfig configures file retention policies type RetentionConfig struct { TranscodedFiles string `yaml:"transcoded_files"` FailedJobs string `yaml:"failed_jobs"` } // LoadConfig loads configuration from a YAML file func LoadConfig(filename string) (*Config, error) { data, err := os.ReadFile(filename) if err != nil { return nil, fmt.Errorf("failed to read config file %s: %w", filename, err) } var config Config if err := yaml.Unmarshal(data, &config); err != nil { return nil, fmt.Errorf("failed to parse config file %s: %w", filename, err) } // Set defaults if config.Mode == "" { config.Mode = "unified" } // Set branding defaults if config.Branding.SiteName == "" { config.Branding.SiteName = "BitTorrent Gateway" } if config.Branding.Description == "" { config.Branding.Description = "Decentralized file sharing gateway" } // Set Nostr defaults if len(config.Nostr.Relays) == 0 && (config.Nostr.PublishNIP35 || config.Nostr.PublishNIP71) { config.Nostr.Relays = []string{ "wss://relay.damus.io", "wss://nos.lol", "wss://relay.nostr.band", } } if len(config.Nostr.VideoRelays) == 0 && config.Nostr.PublishNIP71 { config.Nostr.VideoRelays = config.Nostr.Relays // Use same relays by default } if config.Nostr.ThumbnailsDir == "" { config.Nostr.ThumbnailsDir = "data/thumbnails" } // Set transcoding defaults if config.Transcoding.WorkDir == "" { config.Transcoding.WorkDir = "./data/transcoded" } if config.Transcoding.ConcurrentJobs == 0 { config.Transcoding.ConcurrentJobs = 2 } if config.Transcoding.MinFileSize == "" { config.Transcoding.MinFileSize = "50MB" } if config.Transcoding.MaxCPUPercent == 0 { config.Transcoding.MaxCPUPercent = 80 } if config.Transcoding.NiceLevel == 0 { config.Transcoding.NiceLevel = 10 } return &config, nil } // IsServiceEnabled checks if a specific service should be enabled based on mode func (c *Config) IsServiceEnabled(service string) bool { switch c.Mode { case "unified": switch service { case "gateway": return c.Gateway.Enabled case "blossom": return c.BlossomServer.Enabled case "dht": return c.DHT.Enabled case "tracker": return c.Tracker.Enabled } case "gateway-only": return service == "gateway" && c.Gateway.Enabled case "blossom-only": return service == "blossom" && c.BlossomServer.Enabled case "dht-only": return service == "dht" && c.DHT.Enabled } return false } // GetBlobThreshold returns the blob threshold in bytes func (c *Config) GetBlobThreshold() int64 { return c.Storage.BlobThreshold } // GetChunkSize returns the chunk size in bytes func (c *Config) GetChunkSize() int64 { return c.Storage.ChunkSize } // GetMaxBlobSizeBytes converts the max blob size string to bytes func (c *Config) GetMaxBlobSizeBytes() (int64, error) { return parseSize(c.BlossomServer.MaxBlobSize) } // GetMaxUploadSizeBytes converts the max upload size string to bytes func (c *Config) GetMaxUploadSizeBytes() (int64, error) { return parseSize(c.Gateway.MaxUploadSize) } // GetDefaultUserStorageLimitBytes converts the default user storage limit to bytes func (c *Config) GetDefaultUserStorageLimitBytes() (int64, error) { if c.Admin.DefaultUserStorageLimit == "" { return 10 * 1024 * 1024 * 1024, nil // 10GB default } return parseSize(c.Admin.DefaultUserStorageLimit) } // parseSize parses size strings like "2MB", "100MB", "10GB" func parseSize(sizeStr string) (int64, error) { if sizeStr == "" { return 0, fmt.Errorf("empty size string") } var size int64 var unit string n, err := fmt.Sscanf(sizeStr, "%d%s", &size, &unit) if err != nil || n != 2 { return 0, fmt.Errorf("invalid size format: %s", sizeStr) } switch unit { case "B", "b": return size, nil case "KB", "kb", "K", "k": return size * 1024, nil case "MB", "mb", "M", "m": return size * 1024 * 1024, nil case "GB", "gb", "G", "g": return size * 1024 * 1024 * 1024, nil case "TB", "tb", "T", "t": return size * 1024 * 1024 * 1024 * 1024, nil default: return 0, fmt.Errorf("unknown unit: %s", unit) } } // GetRateLimitValues returns rate limiting values for middleware func (c *Config) GetRateLimitValues() (float64, int, float64, int, float64, int) { upload := c.RateLimiting.Upload download := c.RateLimiting.Download stream := c.RateLimiting.Stream // Provide defaults if not configured uploadRate := upload.RequestsPerSecond if uploadRate <= 0 { uploadRate = 1.0 } uploadBurst := upload.BurstSize if uploadBurst <= 0 { uploadBurst = 5 } downloadRate := download.RequestsPerSecond if downloadRate <= 0 { downloadRate = 50.0 } downloadBurst := download.BurstSize if downloadBurst <= 0 { downloadBurst = 100 } streamRate := stream.RequestsPerSecond if streamRate <= 0 { streamRate = 10.0 } streamBurst := stream.BurstSize if streamBurst <= 0 { streamBurst = 20 } return uploadRate, uploadBurst, downloadRate, downloadBurst, streamRate, streamBurst }