enki 76979d055b
Some checks are pending
CI Pipeline / Run Tests (push) Waiting to run
CI Pipeline / Lint Code (push) Waiting to run
CI Pipeline / Security Scan (push) Waiting to run
CI Pipeline / Build Docker Images (push) Blocked by required conditions
CI Pipeline / E2E Tests (push) Blocked by required conditions
Transcoding and Nip71 update
2025-08-21 19:32:26 -07:00

381 lines
12 KiB
Go

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
}