feat(config): allow -1 to represent unlimited upload limits

Introduce support for configuring unlimited upload limits by allowing -1
as a valid value for anonymous and user upload MB limits.

Changes include:
- Added `envMegabytesLimitFloat` and helper functions to parse and validate limits where -1 is allowed.
- Updated validation logic to accept -1 for `AnonymousMaxUploadMB`, `AnonymousDailyUploadMB`, and `UserDailyUploadMB`.
- Added a test case to verify unlimited upload policy behavior.
This commit is contained in:
2026-05-31 14:01:38 +03:00
parent 61b7c283a4
commit f1c67c455b
12 changed files with 194 additions and 34 deletions

View File

@@ -72,9 +72,9 @@ func Load() (Config, error) {
MaxUploadSize: envMegabytes("WARPBOX_MAX_UPLOAD_SIZE_MB", 2048), // 2 GiB default.
DefaultSettings: SettingsDefaults{
AnonymousUploadsEnabled: envBool("WARPBOX_ANONYMOUS_UPLOADS_ENABLED", true),
AnonymousMaxUploadMB: envMegabytesFloat("WARPBOX_ANONYMOUS_MAX_UPLOAD_MB", 512),
AnonymousDailyUploadMB: envMegabytesFloat("WARPBOX_ANONYMOUS_DAILY_UPLOAD_MB", 2048),
UserDailyUploadMB: envMegabytesFloat("WARPBOX_USER_DAILY_UPLOAD_MB", 8192),
AnonymousMaxUploadMB: envMegabytesLimitFloat("WARPBOX_ANONYMOUS_MAX_UPLOAD_MB", 512),
AnonymousDailyUploadMB: envMegabytesLimitFloat("WARPBOX_ANONYMOUS_DAILY_UPLOAD_MB", 2048),
UserDailyUploadMB: envMegabytesLimitFloat("WARPBOX_USER_DAILY_UPLOAD_MB", 8192),
DefaultUserStorageMB: envMegabytesFloat("WARPBOX_DEFAULT_USER_STORAGE_MB", 51200),
UsageRetentionDays: envInt("WARPBOX_USAGE_RETENTION_DAYS", 30),
LocalStorageMaxGB: envGigabytesFloat("WARPBOX_LOCAL_STORAGE_MAX_GB", 100),
@@ -97,9 +97,9 @@ func Load() (Config, error) {
if cfg.MaxUploadSize <= 0 {
return Config{}, fmt.Errorf("WARPBOX_MAX_UPLOAD_SIZE_MB must be positive")
}
if cfg.DefaultSettings.AnonymousMaxUploadMB <= 0 ||
cfg.DefaultSettings.AnonymousDailyUploadMB <= 0 ||
cfg.DefaultSettings.UserDailyUploadMB <= 0 ||
if !validUnlimitedMegabyteLimit(cfg.DefaultSettings.AnonymousMaxUploadMB) ||
!validUnlimitedMegabyteLimit(cfg.DefaultSettings.AnonymousDailyUploadMB) ||
!validUnlimitedMegabyteLimit(cfg.DefaultSettings.UserDailyUploadMB) ||
cfg.DefaultSettings.DefaultUserStorageMB <= 0 ||
cfg.DefaultSettings.UsageRetentionDays <= 0 ||
cfg.DefaultSettings.LocalStorageMaxGB <= 0 ||
@@ -111,7 +111,7 @@ func Load() (Config, error) {
cfg.DefaultSettings.UserActiveBoxes <= 0 ||
cfg.DefaultSettings.ShortWindowRequests <= 0 ||
cfg.DefaultSettings.ShortWindowSeconds <= 0 {
return Config{}, fmt.Errorf("upload policy settings must be positive")
return Config{}, fmt.Errorf("upload policy settings must be positive, with -1 allowed for upload MB limits")
}
return cfg, nil
@@ -203,6 +203,18 @@ func envMegabytesFloat(key string, fallback float64) float64 {
return parsed
}
func envMegabytesLimitFloat(key string, fallback float64) float64 {
value := strings.TrimSpace(os.Getenv(key))
if value == "" {
return fallback
}
parsed, err := parseMegabytesLimitFloat(value)
if err != nil {
return fallback
}
return parsed
}
func envGigabytesFloat(key string, fallback float64) float64 {
value := strings.TrimSpace(os.Getenv(key))
if value == "" {
@@ -246,6 +258,35 @@ func parseMegabytesFloat(value string) (float64, error) {
return sizeMB, nil
}
func parseMegabytesLimitFloat(value string) (float64, error) {
sizeMB, err := parseMegabytesFloatAllowNegativeOne(value)
if err != nil {
return 0, err
}
if !validUnlimitedMegabyteLimit(sizeMB) {
return 0, fmt.Errorf("megabyte value must be positive or -1 for unlimited")
}
return sizeMB, nil
}
func parseMegabytesFloatAllowNegativeOne(value string) (float64, error) {
normalized := strings.TrimSpace(value)
normalized = strings.TrimSuffix(normalized, "MB")
normalized = strings.TrimSuffix(normalized, "Mb")
normalized = strings.TrimSuffix(normalized, "mb")
normalized = strings.TrimSpace(normalized)
sizeMB, err := strconv.ParseFloat(normalized, 64)
if err != nil {
return 0, fmt.Errorf("invalid megabyte value %q: %w", value, err)
}
return sizeMB, nil
}
func validUnlimitedMegabyteLimit(value float64) bool {
return value > 0 || value == -1
}
func megabytesToBytes(sizeMB float64) int64 {
return int64(math.Round(sizeMB * 1024 * 1024))
}