| package app |
|
|
| import ( |
| "errors" |
| "fmt" |
| "log" |
| "net/http" |
| "strconv" |
|
|
| "ccLoad/internal/model" |
|
|
| "github.com/gin-gonic/gin" |
| ) |
|
|
| |
| const ( |
| LogRetentionDaysMin = 1 |
| LogRetentionDaysMax = 365 |
| LogRetentionDaysDisabled = -1 |
| ) |
|
|
| |
| |
| func (s *Server) AdminListSettings(c *gin.Context) { |
| settings, err := s.configService.ListAllSettings(c.Request.Context()) |
| if err != nil { |
| log.Printf("[ERROR] AdminListSettings 失败: %v", err) |
| RespondError(c, http.StatusInternalServerError, err) |
| return |
| } |
|
|
| if settings == nil { |
| settings = make([]*model.SystemSetting, 0) |
| } |
| RespondJSON(c, http.StatusOK, settings) |
| } |
|
|
| |
| |
| func (s *Server) AdminGetSetting(c *gin.Context) { |
| key := c.Param("key") |
| if key == "" { |
| RespondErrorMsg(c, http.StatusBadRequest, "missing setting key") |
| return |
| } |
|
|
| |
| setting, err := s.configService.GetSettingFresh(c.Request.Context(), key) |
| if errors.Is(err, model.ErrSettingNotFound) { |
| RespondErrorMsg(c, http.StatusNotFound, fmt.Sprintf("setting not found: %s", key)) |
| return |
| } |
| if err != nil { |
| log.Printf("[ERROR] AdminGetSetting key=%s 失败: %v", key, err) |
| RespondError(c, http.StatusInternalServerError, err) |
| return |
| } |
|
|
| RespondJSON(c, http.StatusOK, setting) |
| } |
|
|
| |
| |
| func (s *Server) AdminUpdateSetting(c *gin.Context) { |
| key := c.Param("key") |
| if key == "" { |
| RespondErrorMsg(c, http.StatusBadRequest, "missing setting key") |
| return |
| } |
|
|
| var req SettingUpdateRequest |
| if err := c.ShouldBindJSON(&req); err != nil { |
| RespondErrorMsg(c, http.StatusBadRequest, fmt.Sprintf("invalid request: %v", err)) |
| return |
| } |
|
|
| |
| setting := s.configService.GetSetting(key) |
| if setting == nil { |
| RespondErrorMsg(c, http.StatusNotFound, fmt.Sprintf("setting not found: %s", key)) |
| return |
| } |
|
|
| if err := validateSettingValue(key, setting.ValueType, req.Value); err != nil { |
| RespondErrorMsg(c, http.StatusBadRequest, fmt.Sprintf("invalid value for type %s: %v", setting.ValueType, err)) |
| return |
| } |
|
|
| |
| if err := s.configService.UpdateSetting(c.Request.Context(), key, req.Value); err != nil { |
| log.Printf("[ERROR] AdminUpdateSetting key=%s 失败: %v", key, err) |
| RespondError(c, http.StatusInternalServerError, err) |
| return |
| } |
|
|
| |
|
|
| |
| RespondJSON(c, http.StatusOK, gin.H{ |
| "message": "配置已保存,程序将在2秒后重启", |
| "key": key, |
| "value": req.Value, |
| }) |
|
|
| |
| go triggerRestart() |
| } |
|
|
| |
| |
| func (s *Server) AdminResetSetting(c *gin.Context) { |
| key := c.Param("key") |
| if key == "" { |
| RespondErrorMsg(c, http.StatusBadRequest, "missing setting key") |
| return |
| } |
|
|
| |
| setting := s.configService.GetSetting(key) |
| if setting == nil { |
| RespondErrorMsg(c, http.StatusNotFound, fmt.Sprintf("setting not found: %s", key)) |
| return |
| } |
|
|
| |
| if err := s.configService.UpdateSetting(c.Request.Context(), key, setting.DefaultValue); err != nil { |
| log.Printf("[ERROR] AdminResetSetting key=%s 失败: %v", key, err) |
| RespondError(c, http.StatusInternalServerError, err) |
| return |
| } |
|
|
| |
|
|
| RespondJSON(c, http.StatusOK, gin.H{ |
| "message": "配置已重置为默认值,程序将在2秒后重启", |
| "key": key, |
| "value": setting.DefaultValue, |
| }) |
|
|
| |
| go triggerRestart() |
| } |
|
|
| |
| |
| func (s *Server) AdminBatchUpdateSettings(c *gin.Context) { |
| var req map[string]string |
| if err := c.ShouldBindJSON(&req); err != nil { |
| RespondErrorMsg(c, http.StatusBadRequest, fmt.Sprintf("invalid request: %v", err)) |
| return |
| } |
|
|
| if len(req) == 0 { |
| RespondErrorMsg(c, http.StatusBadRequest, "no settings to update") |
| return |
| } |
|
|
| |
| for key, value := range req { |
| setting := s.configService.GetSetting(key) |
| if setting == nil { |
| RespondErrorMsg(c, http.StatusBadRequest, fmt.Sprintf("unknown setting: %s", key)) |
| return |
| } |
|
|
| if err := validateSettingValue(key, setting.ValueType, value); err != nil { |
| RespondErrorMsg(c, http.StatusBadRequest, fmt.Sprintf("invalid value for %s: %v", key, err)) |
| return |
| } |
| } |
|
|
| |
| if err := s.configService.BatchUpdateSettings(c.Request.Context(), req); err != nil { |
| log.Printf("[ERROR] AdminBatchUpdateSettings 失败: %v", err) |
| RespondError(c, http.StatusInternalServerError, err) |
| return |
| } |
|
|
| log.Printf("[INFO] 已批量更新 %d 项配置(需重启)", len(req)) |
|
|
| RespondJSON(c, http.StatusOK, gin.H{ |
| "message": fmt.Sprintf("已保存 %d 项配置,程序将在2秒后重启", len(req)), |
| }) |
|
|
| |
| go triggerRestart() |
| } |
|
|
| |
| func validateSettingValue(key, valueType, value string) error { |
| switch valueType { |
| case "int": |
| intVal, err := strconv.Atoi(value) |
| if err != nil { |
| return fmt.Errorf("not a valid integer") |
| } |
| |
| switch key { |
| case "max_key_retries": |
| if intVal < 1 { |
| return fmt.Errorf("max_key_retries must be >= 1") |
| } |
| case "channel_check_interval_hours": |
| if intVal < 0 { |
| return fmt.Errorf("channel_check_interval_hours must be >= 0") |
| } |
| case "log_retention_days": |
| if intVal != LogRetentionDaysDisabled && (intVal < LogRetentionDaysMin || intVal > LogRetentionDaysMax) { |
| return fmt.Errorf("log_retention_days must be %d (永久) or %d-%d", LogRetentionDaysDisabled, LogRetentionDaysMin, LogRetentionDaysMax) |
| } |
| default: |
| if intVal < -1 { |
| return fmt.Errorf("value must be >= -1") |
| } |
| } |
|
|
| case "bool": |
| if value != "true" && value != "false" && value != "1" && value != "0" { |
| return fmt.Errorf("must be true/false or 1/0") |
| } |
|
|
| case "duration": |
| intVal, err := strconv.Atoi(value) |
| if err != nil { |
| return fmt.Errorf("duration must be an integer (seconds)") |
| } |
| if intVal < 0 { |
| return fmt.Errorf("duration must be >= 0 (0 = disabled)") |
| } |
|
|
| case "string": |
| switch key { |
| case "log_channel_click_action": |
| if value != "edit" && value != "navigate" { |
| return fmt.Errorf("log_channel_click_action must be edit or navigate") |
| } |
| } |
|
|
| default: |
| return fmt.Errorf("unknown value type: %s", valueType) |
| } |
|
|
| return nil |
| } |
|
|
| |
| var RestartFunc func() |
|
|
| |
| |
| func triggerRestart() { |
| log.Print("[INFO] 配置变更触发重启...") |
|
|
| if RestartFunc == nil { |
| log.Printf("[ERROR] RestartFunc 为空,重启已跳过") |
| return |
| } |
| RestartFunc() |
| } |
|
|