feat(storage): add S3 backend support and advanced upload limits

- Introduce S3-compatible storage backend support using minio-go.
- Add configuration options for local storage limits, box limits, and rate limiting.
- Implement storage backend selection (local vs S3) for anonymous and registered users.
- Add an `/admin/storage` management interface.
- Update documentation and environment examples with the new configuration variables.
This commit is contained in:
2026-05-31 02:14:10 +03:00
parent 830d2a885c
commit c3558fd353
34 changed files with 2668 additions and 168 deletions

View File

@@ -38,6 +38,17 @@ type SettingsDefaults struct {
UserDailyUploadMB float64
DefaultUserStorageMB float64
UsageRetentionDays int
LocalStorageMaxGB float64
AnonymousMaxDays int
UserMaxDays int
AnonymousDailyBoxes int
UserDailyBoxes int
AnonymousActiveBoxes int
UserActiveBoxes int
ShortWindowRequests int
ShortWindowSeconds int
AnonymousStorageBackend string
UserStorageBackend string
}
func Load() (Config, error) {
@@ -66,6 +77,17 @@ func Load() (Config, error) {
UserDailyUploadMB: envMegabytesFloat("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),
AnonymousMaxDays: envInt("WARPBOX_ANONYMOUS_MAX_DAYS", 30),
UserMaxDays: envInt("WARPBOX_USER_MAX_DAYS", 90),
AnonymousDailyBoxes: envInt("WARPBOX_ANONYMOUS_DAILY_BOXES", 100),
UserDailyBoxes: envInt("WARPBOX_USER_DAILY_BOXES", 250),
AnonymousActiveBoxes: envInt("WARPBOX_ANONYMOUS_ACTIVE_BOXES", 500),
UserActiveBoxes: envInt("WARPBOX_USER_ACTIVE_BOXES", 1000),
ShortWindowRequests: envInt("WARPBOX_SHORT_WINDOW_REQUESTS", 60),
ShortWindowSeconds: envInt("WARPBOX_SHORT_WINDOW_SECONDS", 60),
AnonymousStorageBackend: envString("WARPBOX_ANONYMOUS_STORAGE_BACKEND", "local"),
UserStorageBackend: envString("WARPBOX_USER_STORAGE_BACKEND", "local"),
},
}
@@ -79,7 +101,16 @@ func Load() (Config, error) {
cfg.DefaultSettings.AnonymousDailyUploadMB <= 0 ||
cfg.DefaultSettings.UserDailyUploadMB <= 0 ||
cfg.DefaultSettings.DefaultUserStorageMB <= 0 ||
cfg.DefaultSettings.UsageRetentionDays <= 0 {
cfg.DefaultSettings.UsageRetentionDays <= 0 ||
cfg.DefaultSettings.LocalStorageMaxGB <= 0 ||
cfg.DefaultSettings.AnonymousMaxDays <= 0 ||
cfg.DefaultSettings.UserMaxDays <= 0 ||
cfg.DefaultSettings.AnonymousDailyBoxes <= 0 ||
cfg.DefaultSettings.UserDailyBoxes <= 0 ||
cfg.DefaultSettings.AnonymousActiveBoxes <= 0 ||
cfg.DefaultSettings.UserActiveBoxes <= 0 ||
cfg.DefaultSettings.ShortWindowRequests <= 0 ||
cfg.DefaultSettings.ShortWindowSeconds <= 0 {
return Config{}, fmt.Errorf("upload policy settings must be positive")
}
@@ -172,6 +203,23 @@ func envMegabytesFloat(key string, fallback float64) float64 {
return parsed
}
func envGigabytesFloat(key string, fallback float64) float64 {
value := strings.TrimSpace(os.Getenv(key))
if value == "" {
return fallback
}
normalized := strings.TrimSpace(value)
normalized = strings.TrimSuffix(normalized, "GB")
normalized = strings.TrimSuffix(normalized, "Gb")
normalized = strings.TrimSuffix(normalized, "gb")
normalized = strings.TrimSpace(normalized)
parsed, err := strconv.ParseFloat(normalized, 64)
if err != nil || parsed <= 0 {
return fallback
}
return parsed
}
func parseMegabytes(value string) (int64, error) {
sizeMB, err := parseMegabytesFloat(value)
if err != nil {