package web import ( "ResendIt/internal/config" "net/http" "strconv" "github.com/gin-gonic/gin" ) type ConfigPageData struct { Success bool Error string UploadMaxFileSizeMB int64 UploadMultiMaxFiles int UploadMaxHours int RateLimitLoginPerMinute int RateLimitLoginBurst int RateLimitApiPerMinute int RateLimitApiBurst int NtfyUse bool NtfyUrl string NtfyTopic string } // ConfigPage renders a modular admin config screen. func (h *Handler) ConfigPage(c *gin.Context) { cfg := h.configService maxBytes := cfg.GetInt64Default(config.KeyUploadMaxFileSizeBytes, config.DefaultUploadMaxFileSizeBytes) data := ConfigPageData{ Success: c.Query("saved") == "1", UploadMaxFileSizeMB: maxBytes / (1024 * 1024), UploadMultiMaxFiles: cfg.GetIntDefault(config.KeyUploadMultiMaxFiles, config.DefaultUploadMultiMaxFiles), UploadMaxHours: cfg.GetIntDefault(config.KeyUploadMaxHours, config.DefaultUploadMaxHours), RateLimitLoginPerMinute: cfg.GetIntDefault(config.KeyRateLimitLoginPerMinute, config.DefaultRateLimitLoginPerMinute), RateLimitLoginBurst: cfg.GetIntDefault(config.KeyRateLimitLoginBurst, config.DefaultRateLimitLoginBurst), RateLimitApiPerMinute: cfg.GetIntDefault(config.KeyRateLimitApiPerMinute, config.DefaultRateLimitApiPerMinute), RateLimitApiBurst: cfg.GetIntDefault(config.KeyRateLimitApiBurst, config.DefaultRateLimitApiBurst), NtfyUse: cfg.GetIntDefault(config.KeyUseNtfy, 0) != 0, NtfyUrl: cfg.GetStringDefault(config.KeyNtfyUrl, config.DefaultNtfyUrl), NtfyTopic: cfg.GetStringDefault(config.KeyNtfyTopic, config.DefaultNtfyTopic), } c.HTML(http.StatusOK, "config.html", data) } func (h *Handler) ConfigSave(c *gin.Context) { cfg := h.configService // Parse + validate. parseInt := func(name string, min, max int) (int, error) { v := c.PostForm(name) n, err := strconv.Atoi(v) if err != nil { return 0, err } if n < min { n = min } if max > 0 && n > max { n = max } return n, nil } parseInt64 := func(name string, min int64, max int64) (int64, error) { v := c.PostForm(name) n, err := strconv.ParseInt(v, 10, 64) if err != nil { return 0, err } if n < min { n = min } if max > 0 && n > max { n = max } return n, nil } maxMB, err := parseInt64("upload_max_file_size_mb", 1, 1024*1024) if err != nil { h.renderConfigError(c, "invalid max file size") return } maxFiles, err := parseInt("upload_multi_max_files", 1, 500) if err != nil { h.renderConfigError(c, "invalid max files") return } maxHours, err := parseInt("upload_max_hours", 1, 24*365) if err != nil { h.renderConfigError(c, "invalid max hours") return } // Rate limits: stored, but not applied dynamically yet. loginPerMin, err := parseInt("ratelimit_login_per_minute", 1, 10000) if err != nil { h.renderConfigError(c, "invalid login rate") return } loginBurst, err := parseInt("ratelimit_login_burst", 1, 10000) if err != nil { h.renderConfigError(c, "invalid login burst") return } apiPerMin, err := parseInt("ratelimit_api_per_minute", 1, 100000) if err != nil { h.renderConfigError(c, "invalid api rate") return } apiBurst, err := parseInt("ratelimit_api_burst", 1, 100000) if err != nil { h.renderConfigError(c, "invalid api burst") return } useNTFY, err := strconv.ParseBool(c.PostForm("ntfy_use")) if err != nil { h.renderConfigError(c, "invalid ntfy use value") return } ntfyUrl := c.PostForm("ntfy_url") ntfyTopic := c.PostForm("ntfy_topic") // Persist. if err := cfg.SetString(config.KeyUploadMaxFileSizeBytes, strconv.FormatInt(maxMB*1024*1024, 10)); err != nil { h.renderConfigError(c, err.Error()) return } if err := cfg.SetString(config.KeyUploadMultiMaxFiles, strconv.Itoa(maxFiles)); err != nil { h.renderConfigError(c, err.Error()) return } if err := cfg.SetString(config.KeyUploadMaxHours, strconv.Itoa(maxHours)); err != nil { h.renderConfigError(c, err.Error()) return } _ = cfg.SetString(config.KeyRateLimitLoginPerMinute, strconv.Itoa(loginPerMin)) _ = cfg.SetString(config.KeyRateLimitLoginBurst, strconv.Itoa(loginBurst)) _ = cfg.SetString(config.KeyRateLimitApiPerMinute, strconv.Itoa(apiPerMin)) _ = cfg.SetString(config.KeyRateLimitApiBurst, strconv.Itoa(apiBurst)) // shitty ah fix actualBool := 0 if useNTFY { actualBool = 1 } _ = cfg.SetString(config.KeyUseNtfy, strconv.Itoa(actualBool)) _ = cfg.SetString(config.KeyNtfyUrl, ntfyUrl) _ = cfg.SetString(config.KeyNtfyTopic, ntfyTopic) c.Redirect(http.StatusFound, "/config?saved=1") } func (h *Handler) renderConfigError(c *gin.Context, msg string) { maxBytes := h.configService.GetInt64Default(config.KeyUploadMaxFileSizeBytes, config.DefaultUploadMaxFileSizeBytes) data := ConfigPageData{ Error: msg, UploadMaxFileSizeMB: maxBytes / (1024 * 1024), UploadMultiMaxFiles: h.configService.GetIntDefault(config.KeyUploadMultiMaxFiles, config.DefaultUploadMultiMaxFiles), UploadMaxHours: h.configService.GetIntDefault(config.KeyUploadMaxHours, config.DefaultUploadMaxHours), RateLimitLoginPerMinute: h.configService.GetIntDefault(config.KeyRateLimitLoginPerMinute, config.DefaultRateLimitLoginPerMinute), RateLimitLoginBurst: h.configService.GetIntDefault(config.KeyRateLimitLoginBurst, config.DefaultRateLimitLoginBurst), RateLimitApiPerMinute: h.configService.GetIntDefault(config.KeyRateLimitApiPerMinute, config.DefaultRateLimitApiPerMinute), RateLimitApiBurst: h.configService.GetIntDefault(config.KeyRateLimitApiBurst, config.DefaultRateLimitApiBurst), } c.HTML(http.StatusBadRequest, "config.html", data) }