package handler import ( "context" "encoding/json" "net/http" "strings" "time" "vctp/internal/tasks" ) // SnapshotAggregateForce forces regeneration of a daily or monthly summary table. // @Summary Force snapshot aggregation // @Description Forces regeneration of a daily or monthly summary table for a specified date or month. // @Tags snapshots // @Produce json // @Param type query string true "Aggregation type: daily or monthly" // @Param date query string true "Daily date (YYYY-MM-DD) or monthly date (YYYY-MM)" // @Success 200 {object} map[string]string "Aggregation complete" // @Failure 400 {object} map[string]string "Invalid request" // @Failure 500 {object} map[string]string "Server error" // @Router /api/snapshots/aggregate [post] func (h *Handler) SnapshotAggregateForce(w http.ResponseWriter, r *http.Request) { snapshotType := strings.ToLower(strings.TrimSpace(r.URL.Query().Get("type"))) dateValue := strings.TrimSpace(r.URL.Query().Get("date")) startedAt := time.Now() loc := time.Now().Location() if snapshotType == "" || dateValue == "" { h.Logger.Warn("Snapshot aggregation request missing parameters", "type", snapshotType, "date", dateValue, ) writeJSONError(w, http.StatusBadRequest, "type and date are required") return } ctx := context.Background() ct := &tasks.CronTask{ Logger: h.Logger, Database: h.Database, Settings: h.Settings, } switch snapshotType { case "daily": parsed, err := time.ParseInLocation("2006-01-02", dateValue, loc) if err != nil { h.Logger.Warn("Snapshot aggregation invalid daily date format", "date", dateValue) writeJSONError(w, http.StatusBadRequest, "date must be YYYY-MM-DD") return } h.Logger.Info("Starting daily snapshot aggregation", "date", parsed.Format("2006-01-02"), "force", true) if err := ct.AggregateDailySummary(ctx, parsed, true); err != nil { h.Logger.Error("Daily snapshot aggregation failed", "date", parsed.Format("2006-01-02"), "error", err) writeJSONError(w, http.StatusInternalServerError, err.Error()) return } case "monthly": parsed, err := time.ParseInLocation("2006-01", dateValue, loc) if err != nil { h.Logger.Warn("Snapshot aggregation invalid monthly date format", "date", dateValue) writeJSONError(w, http.StatusBadRequest, "date must be YYYY-MM") return } h.Logger.Info("Starting monthly snapshot aggregation", "date", parsed.Format("2006-01"), "force", true) if err := ct.AggregateMonthlySummary(ctx, parsed, true); err != nil { h.Logger.Error("Monthly snapshot aggregation failed", "date", parsed.Format("2006-01"), "error", err) writeJSONError(w, http.StatusInternalServerError, err.Error()) return } default: h.Logger.Warn("Snapshot aggregation invalid type", "type", snapshotType) writeJSONError(w, http.StatusBadRequest, "type must be daily or monthly") return } h.Logger.Info("Snapshot aggregation completed", "type", snapshotType, "date", dateValue, "duration", time.Since(startedAt), ) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(map[string]string{ "status": "OK", }) } func writeJSONError(w http.ResponseWriter, status int, message string) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) json.NewEncoder(w).Encode(map[string]string{ "status": "ERROR", "message": message, }) }