2026-05-25 15:36:49 +03:00
|
|
|
package config
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"math"
|
|
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type Config struct {
|
2026-05-25 16:52:57 +03:00
|
|
|
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
|
|
|
|
|
CleanupEvery time.Duration
|
|
|
|
|
ThumbnailEvery time.Duration
|
|
|
|
|
MaxUploadSize int64
|
2026-05-25 15:36:49 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func Load() (Config, error) {
|
|
|
|
|
cfg := Config{
|
2026-05-25 16:52:57 +03:00
|
|
|
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),
|
|
|
|
|
CleanupEvery: envDuration("WARPBOX_CLEANUP_EVERY", time.Hour),
|
|
|
|
|
ThumbnailEvery: envDuration("WARPBOX_THUMBNAIL_EVERY", time.Minute),
|
|
|
|
|
MaxUploadSize: envMegabytes("WARPBOX_MAX_UPLOAD_SIZE_MB", 2048), // 2 GiB default.
|
2026-05-25 15:36:49 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 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 parseMegabytes(value string) (int64, 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 megabytesToBytes(sizeMB), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func megabytesToBytes(sizeMB float64) int64 {
|
|
|
|
|
return int64(math.Round(sizeMB * 1024 * 1024))
|
|
|
|
|
}
|