Many changes
This commit is contained in:
+50
-29
@@ -14,53 +14,58 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
defaultListenAddr = ":8080"
|
||||
defaultMaxUploadSize = "100MB"
|
||||
defaultTTL = "1h"
|
||||
defaultDataDir = "./data"
|
||||
defaultCleanupInterval = "1m"
|
||||
defaultRateLimitEnabled = true
|
||||
defaultRequestsPerMinute = 30
|
||||
defaultRateLimitBurst = 10
|
||||
maxDefaultTTLLimit = 24 * time.Hour
|
||||
minCleanupInterval = 10 * time.Second
|
||||
defaultListenAddr = ":8080"
|
||||
defaultMaxUploadSize = "100MiB"
|
||||
defaultTTL = "1h"
|
||||
defaultRawCacheMaxSize = "200MiB"
|
||||
defaultRawCacheMaxEntries = 32
|
||||
defaultDataDir = "./data"
|
||||
defaultCleanupInterval = "1m"
|
||||
defaultRateLimitEnabled = true
|
||||
defaultRequestsPerMinute = 30
|
||||
defaultRateLimitBurst = 10
|
||||
maxDefaultTTLLimit = 24 * time.Hour
|
||||
minCleanupInterval = 10 * time.Second
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Server ServerConfig `yaml:"server"`
|
||||
Limits LimitsConfig `yaml:"limits"`
|
||||
Storage StorageConfig `yaml:"storage"`
|
||||
Security SecurityConfig `yaml:"security"`
|
||||
Server ServerConfig `yaml:"server" comment:"HTTP listener settings."`
|
||||
Limits LimitsConfig `yaml:"limits" comment:"Upload, expiration, and raw cache defaults."`
|
||||
Storage StorageConfig `yaml:"storage" comment:"Filesystem storage location and cleanup cadence."`
|
||||
Security SecurityConfig `yaml:"security" comment:"Write-route IP restrictions and rate limiting."`
|
||||
}
|
||||
|
||||
type ServerConfig struct {
|
||||
ListenAddr string `yaml:"listen_addr"`
|
||||
ListenAddr string `yaml:"listen_addr" comment:"Address to bind the HTTP server to."`
|
||||
}
|
||||
|
||||
type LimitsConfig struct {
|
||||
MaxUploadSize string `yaml:"max_upload_size"`
|
||||
DefaultTTL string `yaml:"default_ttl"`
|
||||
MaxUploadSize string `yaml:"max_upload_size" comment:"Maximum upload request body size. Supports B, KB, MB, GB and KiB, MiB, GiB (case-insensitive). Defaults to bytes when unit is omitted (example: 1048576)."`
|
||||
DefaultTTL string `yaml:"default_ttl" comment:"Default expiration for new scratches. Must be > 0 and <= 24h."`
|
||||
RawCacheMaxSize string `yaml:"raw_cache_max_size" comment:"Maximum total decompressed bytes cached in memory for /api/raw range requests. Supports B, KB, MB, GB and KiB, MiB, GiB. Set this above limits.max_upload_size based on your expected concurrent large downloads."`
|
||||
RawCacheMaxEntries int `yaml:"raw_cache_max_entries" comment:"Maximum number of decompressed /api/raw entries retained in memory cache. Use this as a secondary cap alongside raw_cache_max_size."`
|
||||
MaxUploadSizeBytes int64 `yaml:"-"`
|
||||
DefaultTTLDuration time.Duration `yaml:"-"`
|
||||
RawCacheMaxBytes int64 `yaml:"-"`
|
||||
}
|
||||
|
||||
type StorageConfig struct {
|
||||
DataDir string `yaml:"data_dir"`
|
||||
CleanupInterval string `yaml:"cleanup_interval"`
|
||||
DataDir string `yaml:"data_dir" comment:"Directory where scratch files and metadata are stored."`
|
||||
CleanupInterval string `yaml:"cleanup_interval" comment:"How often expired scratches are deleted (minimum 10s)."`
|
||||
CleanupIntervalParsed time.Duration `yaml:"-"`
|
||||
}
|
||||
|
||||
type SecurityConfig struct {
|
||||
AllowedIPs []string `yaml:"allowed_ips"`
|
||||
TrustProxyHeaders bool `yaml:"trust_proxy_headers"`
|
||||
RateLimit RateLimitConfig `yaml:"rate_limit"`
|
||||
AllowedIPs []string `yaml:"allowed_ips" comment:"Allowlist for POST /api/scratch as IPs or CIDRs (examples: 127.0.0.1, 192.168.1.0/24). Keep 127.0.0.1 for local access; add trusted ranges as needed. Empty means unrestricted."`
|
||||
TrustProxyHeaders bool `yaml:"trust_proxy_headers" comment:"Honor X-Forwarded-For / X-Real-IP when true (trusted proxy only)."`
|
||||
RateLimit RateLimitConfig `yaml:"rate_limit" comment:"Per-client token bucket on POST /api/scratch."`
|
||||
AllowedPrefixes []netip.Prefix `yaml:"-"`
|
||||
}
|
||||
|
||||
type RateLimitConfig struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
RequestsPerMinute int `yaml:"requests_per_minute"`
|
||||
Burst int `yaml:"burst"`
|
||||
Enabled bool `yaml:"enabled" comment:"Enable per-client write-route rate limiting."`
|
||||
RequestsPerMinute int `yaml:"requests_per_minute" comment:"Steady refill rate per client IP."`
|
||||
Burst int `yaml:"burst" comment:"Maximum immediate burst capacity per client IP."`
|
||||
}
|
||||
|
||||
func Load(path string) (Config, error) {
|
||||
@@ -81,20 +86,29 @@ func Load(path string) (Config, error) {
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func Default() Config {
|
||||
return defaults()
|
||||
}
|
||||
|
||||
func defaults() Config {
|
||||
return Config{
|
||||
Server: ServerConfig{
|
||||
ListenAddr: defaultListenAddr,
|
||||
},
|
||||
Limits: LimitsConfig{
|
||||
MaxUploadSize: defaultMaxUploadSize,
|
||||
DefaultTTL: defaultTTL,
|
||||
MaxUploadSize: defaultMaxUploadSize,
|
||||
DefaultTTL: defaultTTL,
|
||||
RawCacheMaxSize: defaultRawCacheMaxSize,
|
||||
RawCacheMaxEntries: defaultRawCacheMaxEntries,
|
||||
},
|
||||
Storage: StorageConfig{
|
||||
DataDir: defaultDataDir,
|
||||
CleanupInterval: defaultCleanupInterval,
|
||||
},
|
||||
Security: SecurityConfig{
|
||||
AllowedIPs: []string{
|
||||
"127.0.0.1",
|
||||
},
|
||||
RateLimit: RateLimitConfig{
|
||||
Enabled: defaultRateLimitEnabled,
|
||||
RequestsPerMinute: defaultRequestsPerMinute,
|
||||
@@ -124,6 +138,15 @@ func (c *Config) Validate() error {
|
||||
}
|
||||
c.Limits.DefaultTTLDuration = ttl
|
||||
|
||||
rawCacheBytes, err := parseHumanSize(c.Limits.RawCacheMaxSize)
|
||||
if err != nil {
|
||||
return fmt.Errorf("limits.raw_cache_max_size invalid: %w", err)
|
||||
}
|
||||
c.Limits.RawCacheMaxBytes = rawCacheBytes
|
||||
if c.Limits.RawCacheMaxEntries <= 0 {
|
||||
return errors.New("limits.raw_cache_max_entries must be > 0")
|
||||
}
|
||||
|
||||
cleanupInterval, err := time.ParseDuration(strings.TrimSpace(c.Storage.CleanupInterval))
|
||||
if err != nil {
|
||||
return fmt.Errorf("storage.cleanup_interval invalid: %w", err)
|
||||
@@ -231,11 +254,9 @@ func parseHumanSize(raw string) (int64, error) {
|
||||
"KB": 1_000,
|
||||
"MB": 1_000_000,
|
||||
"GB": 1_000_000_000,
|
||||
"TB": 1_000_000_000_000,
|
||||
"KIB": 1024,
|
||||
"MIB": 1024 * 1024,
|
||||
"GIB": 1024 * 1024 * 1024,
|
||||
"TIB": 1024 * 1024 * 1024 * 1024,
|
||||
}
|
||||
|
||||
multiplier, ok := multipliers[unitPart]
|
||||
|
||||
Reference in New Issue
Block a user