All checks were successful
Build and Publish Docker Image / deploy (push) Successful in 1m8s
- Add environment variables to configure anonymous uploads, daily upload caps, and default user storage limits. - Update config loader to parse and validate the new settings. - Implement backend logic to track daily usage and active storage per user. - Update README and `.env.example` to document the new settings and admin panels.
204 lines
5.5 KiB
Go
204 lines
5.5 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type Config struct {
|
|
AppName string
|
|
Environment string
|
|
Addr string
|
|
BaseURL string
|
|
DataDir string
|
|
AdminToken string
|
|
StaticDir string
|
|
TemplateDir string
|
|
ReadTimeout time.Duration
|
|
WriteTimeout time.Duration
|
|
IdleTimeout time.Duration
|
|
JobsEnabled bool
|
|
CleanupEnabled bool
|
|
CleanupEvery time.Duration
|
|
ThumbnailEnabled bool
|
|
ThumbnailEvery time.Duration
|
|
MaxUploadSize int64
|
|
DefaultSettings SettingsDefaults
|
|
}
|
|
|
|
type SettingsDefaults struct {
|
|
AnonymousUploadsEnabled bool
|
|
AnonymousMaxUploadMB float64
|
|
AnonymousDailyUploadMB float64
|
|
UserDailyUploadMB float64
|
|
DefaultUserStorageMB float64
|
|
UsageRetentionDays int
|
|
}
|
|
|
|
func Load() (Config, error) {
|
|
cfg := Config{
|
|
AppName: envString("WARPBOX_APP_NAME", "warpbox.dev"),
|
|
Environment: envString("WARPBOX_ENV", "development"),
|
|
Addr: envString("WARPBOX_ADDR", ":8080"),
|
|
BaseURL: strings.TrimRight(envString("WARPBOX_BASE_URL", "http://localhost:8080"), "/"),
|
|
DataDir: envString("WARPBOX_DATA_DIR", defaultPath("data")),
|
|
AdminToken: envString("WARPBOX_ADMIN_TOKEN", ""),
|
|
StaticDir: envString("WARPBOX_STATIC_DIR", defaultPath("static")),
|
|
TemplateDir: envString("WARPBOX_TEMPLATE_DIR", defaultPath("templates")),
|
|
ReadTimeout: envDuration("WARPBOX_READ_TIMEOUT", 15*time.Second),
|
|
WriteTimeout: envDuration("WARPBOX_WRITE_TIMEOUT", 60*time.Second),
|
|
IdleTimeout: envDuration("WARPBOX_IDLE_TIMEOUT", 120*time.Second),
|
|
JobsEnabled: envBool("WARPBOX_JOBS_ENABLED", true),
|
|
CleanupEnabled: envBool("WARPBOX_CLEANUP_ENABLED", true),
|
|
CleanupEvery: envDuration("WARPBOX_CLEANUP_EVERY", time.Hour),
|
|
ThumbnailEnabled: envBool("WARPBOX_THUMBNAIL_ENABLED", true),
|
|
ThumbnailEvery: envDuration("WARPBOX_THUMBNAIL_EVERY", time.Minute),
|
|
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),
|
|
DefaultUserStorageMB: envMegabytesFloat("WARPBOX_DEFAULT_USER_STORAGE_MB", 51200),
|
|
UsageRetentionDays: envInt("WARPBOX_USAGE_RETENTION_DAYS", 30),
|
|
},
|
|
}
|
|
|
|
if cfg.BaseURL == "" {
|
|
return Config{}, fmt.Errorf("WARPBOX_BASE_URL cannot be empty")
|
|
}
|
|
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 ||
|
|
cfg.DefaultSettings.DefaultUserStorageMB <= 0 ||
|
|
cfg.DefaultSettings.UsageRetentionDays <= 0 {
|
|
return Config{}, fmt.Errorf("upload policy settings must be positive")
|
|
}
|
|
|
|
return cfg, nil
|
|
}
|
|
|
|
func defaultPath(name string) string {
|
|
candidates := []string{
|
|
filepath.Join("backend", name),
|
|
name,
|
|
}
|
|
|
|
for _, candidate := range candidates {
|
|
if info, err := os.Stat(candidate); err == nil && info.IsDir() {
|
|
return candidate
|
|
}
|
|
}
|
|
|
|
return filepath.Join("backend", name)
|
|
}
|
|
|
|
func envString(key, fallback string) string {
|
|
if value := strings.TrimSpace(os.Getenv(key)); value != "" {
|
|
return value
|
|
}
|
|
return fallback
|
|
}
|
|
|
|
func envDuration(key string, fallback time.Duration) time.Duration {
|
|
value := strings.TrimSpace(os.Getenv(key))
|
|
if value == "" {
|
|
return fallback
|
|
}
|
|
|
|
parsed, err := time.ParseDuration(value)
|
|
if err != nil {
|
|
return fallback
|
|
}
|
|
return parsed
|
|
}
|
|
|
|
func envBool(key string, fallback bool) bool {
|
|
value := strings.TrimSpace(os.Getenv(key))
|
|
if value == "" {
|
|
return fallback
|
|
}
|
|
|
|
parsed, err := strconv.ParseBool(value)
|
|
if err != nil {
|
|
return fallback
|
|
}
|
|
return parsed
|
|
}
|
|
|
|
func envInt(key string, fallback int) int {
|
|
value := strings.TrimSpace(os.Getenv(key))
|
|
if value == "" {
|
|
return fallback
|
|
}
|
|
|
|
parsed, err := strconv.Atoi(value)
|
|
if err != nil {
|
|
return fallback
|
|
}
|
|
return parsed
|
|
}
|
|
|
|
func envMegabytes(key string, fallback float64) int64 {
|
|
value := strings.TrimSpace(os.Getenv(key))
|
|
if value == "" {
|
|
return megabytesToBytes(fallback)
|
|
}
|
|
|
|
parsed, err := parseMegabytes(value)
|
|
if err != nil {
|
|
return megabytesToBytes(fallback)
|
|
}
|
|
return parsed
|
|
}
|
|
|
|
func envMegabytesFloat(key string, fallback float64) float64 {
|
|
value := strings.TrimSpace(os.Getenv(key))
|
|
if value == "" {
|
|
return fallback
|
|
}
|
|
parsed, err := parseMegabytesFloat(value)
|
|
if err != nil {
|
|
return fallback
|
|
}
|
|
return parsed
|
|
}
|
|
|
|
func parseMegabytes(value string) (int64, error) {
|
|
sizeMB, err := parseMegabytesFloat(value)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return megabytesToBytes(sizeMB), nil
|
|
}
|
|
|
|
func parseMegabytesFloat(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)
|
|
}
|
|
if sizeMB <= 0 {
|
|
return 0, fmt.Errorf("megabyte value must be positive")
|
|
}
|
|
|
|
return sizeMB, nil
|
|
}
|
|
|
|
func megabytesToBytes(sizeMB float64) int64 {
|
|
return int64(math.Round(sizeMB * 1024 * 1024))
|
|
}
|