package imagevariant import ( "RenewCMS/api/pkg/apperr" validator "RenewCMS/api/pkg/validator" "RenewCMS/internal/domain/image" "RenewCMS/internal/infrastructure/useCases" "encoding/json" "errors" "math" "net/http" validatorlib "github.com/go-playground/validator/v10" ) type cacheUnusedResponse struct { Files int `json:"files"` Bytes int64 `json:"bytes"` Days int `json:"days"` } type cacheVariantResponse struct { Name string `json:"name"` Enabled bool `json:"enabled"` Files int `json:"files"` Bytes int64 `json:"bytes"` } type cacheImageResponse struct { ID uint32 `json:"id"` Filename string `json:"filename"` URL string `json:"url"` Files int `json:"files"` Bytes int64 `json:"bytes"` } type cacheResponse struct { GeneratedFiles int `json:"generated_files"` DiskUsed int64 `json:"disk_used"` VariantsDefined int `json:"variants_defined"` VariantsEnabled int `json:"variants_enabled"` VariantsDisabled int `json:"variants_disabled"` RejectedRequests int `json:"rejected_requests"` RejectedWindowDays int `json:"rejected_window_days"` Unused cacheUnusedResponse `json:"unused"` ByVariant []cacheVariantResponse `json:"by_variant"` TopImages []cacheImageResponse `json:"top_images"` } type cachePurgeRequest struct { Scope string `json:"scope" validate:"required,oneof=all unused variant image"` Variant string `json:"variant" validate:"required_if=Scope variant"` ImageID int64 `json:"image_id" validate:"required_if=Scope image,omitempty,gt=0,lte=4294967295"` } type cachePurgeResponse struct { Scope string `json:"scope"` PurgedFiles int `json:"purged_files"` PurgedBytes int64 `json:"purged_bytes"` FailedFiles int `json:"failed_files"` } func (r *cachePurgeRequest) ignoreUnusedTargets() { if r.Scope != string(image.CacheScopeVariant) { r.Variant = "" } if r.Scope != string(image.CacheScopeImage) { r.ImageID = 0 } } func cacheResponseOf(stats useCases.VariantCacheStats) cacheResponse { response := cacheResponse{ GeneratedFiles: stats.GeneratedFiles, DiskUsed: stats.DiskUsed, VariantsDefined: stats.VariantsDefined, VariantsEnabled: stats.VariantsEnabled, VariantsDisabled: stats.VariantsDisabled, RejectedRequests: stats.RejectedRequests, RejectedWindowDays: stats.RejectedWindowDays, Unused: cacheUnusedResponse{Files: stats.Unused.Files, Bytes: stats.Unused.Bytes, Days: stats.UnusedDays}, ByVariant: make([]cacheVariantResponse, 0, len(stats.ByVariant)), TopImages: make([]cacheImageResponse, 0, len(stats.TopImages)), } for _, entry := range stats.ByVariant { response.ByVariant = append(response.ByVariant, cacheVariantResponse{Name: entry.Name, Enabled: entry.Enabled, Files: entry.Files, Bytes: entry.Bytes}) } for _, top := range stats.TopImages { response.TopImages = append(response.TopImages, cacheImageResponse{ID: top.ID, Filename: top.Filename, URL: top.URL, Files: top.Files, Bytes: top.Bytes}) } return response } func (h *Handler) CacheStats(w http.ResponseWriter, _ *http.Request) { stats, err := h.cacheStats.Get() if err != nil { h.writeError(w, "", err) return } writeJSON(w, http.StatusOK, cacheResponseOf(stats)) } func (h *Handler) PurgeCache(w http.ResponseWriter, r *http.Request) { var request cachePurgeRequest if err := json.NewDecoder(r.Body).Decode(&request); err != nil { http.Error(w, apperr.BodyErrorMessage, http.StatusBadRequest) return } request.ignoreUnusedTargets() if err := validator.Validate.Struct(request); err != nil { http.Error(w, cachePurgeValidationMessage(err), http.StatusUnprocessableEntity) return } result, err := h.purgeCache.Purge(r.Context(), useCases.PurgeVariantCacheCommand{ Scope: image.CachePurgeScope(request.Scope), Variant: request.Variant, ImageID: uint32(min(request.ImageID, math.MaxUint32)), }) if err != nil { h.writePurgeCacheError(w, err) return } writeJSON(w, http.StatusOK, cachePurgeResponse{Scope: string(result.Scope), PurgedFiles: result.Files, PurgedBytes: result.Bytes, FailedFiles: result.Failed}) } func cachePurgeValidationMessage(err error) string { var failures validatorlib.ValidationErrors if errors.As(err, &failures) && len(failures) > 0 && failures[0].StructField() != "Scope" { return apperr.CachePurgeTargetErrorMessage } return apperr.CachePurgeScopeErrorMessage } func (h *Handler) writePurgeCacheError(w http.ResponseWriter, err error) { if errors.Is(err, image.ErrImageNotFound) { http.Error(w, apperr.ImageNotFoundErrorMessage, http.StatusNotFound) return } h.writeError(w, "", err) }