package settings import ( "RenewCMS/api/pkg/apperr" validator "RenewCMS/api/pkg/validator" "RenewCMS/internal/domain/audit" "RenewCMS/internal/infrastructure/useCases" "encoding/json" "errors" "io" "net/http" "time" ) type Handler struct { GetSettingsUseCase *useCases.GetSettingsUseCase GetActivityRetentionUseCase *useCases.GetActivityRetentionUseCase UpdateActivityRetentionUseCase *useCases.UpdateActivityRetentionUseCase } type UpdateSettingsRequest struct { ActivityRetentionDays *int `json:"activity_retention_days" validate:"omitempty,oneof=0 30 90 365"` ActivityRetentionMaxGB *int `json:"activity_retention_max_gb" validate:"omitempty,oneof=0 1 5 10 25 50"` } type settingsResponse struct { Version string `json:"version"` ActivityRetentionDays int `json:"activity_retention_days"` ActivityRetentionMaxGB int `json:"activity_retention_max_gb"` ActivityLogSizeBytes int64 `json:"activity_log_size_bytes"` ActivityLastCleanupAt *time.Time `json:"activity_last_cleanup_at"` ActivityLastCleanupRemoved int `json:"activity_last_cleanup_removed"` ActivityNextCleanupAt *time.Time `json:"activity_next_cleanup_at"` } func NewHandler(getSettings *useCases.GetSettingsUseCase, getRetention *useCases.GetActivityRetentionUseCase, updateRetention *useCases.UpdateActivityRetentionUseCase) *Handler { return &Handler{GetSettingsUseCase: getSettings, GetActivityRetentionUseCase: getRetention, UpdateActivityRetentionUseCase: updateRetention} } func (h *Handler) GetSettings(w http.ResponseWriter, _ *http.Request) { retention, err := h.GetActivityRetentionUseCase.GetActivityRetention() if err != nil { http.Error(w, "Internal server error", http.StatusInternalServerError) return } h.write(w, retention) } func (h *Handler) UpdateSettings(w http.ResponseWriter, r *http.Request) { body, err := io.ReadAll(r.Body) if err != nil || hasNullValue(body) { http.Error(w, apperr.BodyErrorMessage, http.StatusBadRequest) return } var request UpdateSettingsRequest if err := json.Unmarshal(body, &request); err != nil { http.Error(w, apperr.BodyErrorMessage, http.StatusBadRequest) return } if err := validator.Validate.Struct(request); err != nil { http.Error(w, apperr.BodyErrorMessage, http.StatusBadRequest) return } retention, err := h.UpdateActivityRetentionUseCase.UpdateActivityRetention(r.Context(), useCases.UpdateActivityRetentionCommand{ Days: request.ActivityRetentionDays, MaxGB: request.ActivityRetentionMaxGB, }) if err != nil { if errors.Is(err, audit.ErrInvalidRetention) { http.Error(w, apperr.BodyErrorMessage, http.StatusBadRequest) return } http.Error(w, "Internal server error", http.StatusInternalServerError) return } h.write(w, retention) } func hasNullValue(body []byte) bool { var fields map[string]json.RawMessage if err := json.Unmarshal(body, &fields); err != nil { return false } for _, value := range fields { if string(value) == "null" { return true } } return false } func (h *Handler) write(w http.ResponseWriter, retention audit.Settings) { body, err := json.Marshal(settingsResponse{ Version: h.GetSettingsUseCase.GetSettings().Version, ActivityRetentionDays: retention.RetentionDays, ActivityRetentionMaxGB: retention.RetentionMaxGB, ActivityLogSizeBytes: retention.LogSizeBytes, ActivityLastCleanupAt: retention.LastCleanupAt, ActivityLastCleanupRemoved: retention.LastCleanupRemoved, ActivityNextCleanupAt: retention.NextCleanupAt, }) if err != nil { http.Error(w, "Internal server error", http.StatusInternalServerError) return } _, _ = w.Write(body) }