All checks were successful
continuous-integration/drone/push Build is passing
120 lines
4.3 KiB
Go
120 lines
4.3 KiB
Go
package handler
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
"vctp/internal/settings"
|
|
"vctp/internal/tasks"
|
|
"vctp/server/models"
|
|
)
|
|
|
|
// 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)"
|
|
// @Param granularity query string false "Monthly aggregation granularity: hourly or daily"
|
|
// @Success 200 {object} models.StatusResponse "Aggregation complete"
|
|
// @Failure 400 {object} models.ErrorResponse "Invalid request"
|
|
// @Failure 500 {object} models.ErrorResponse "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"))
|
|
granularity := strings.ToLower(strings.TrimSpace(r.URL.Query().Get("granularity")))
|
|
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
|
|
}
|
|
|
|
if granularity != "" && snapshotType != "monthly" {
|
|
h.Logger.Debug("Snapshot aggregation ignoring granularity for non-monthly request",
|
|
"type", snapshotType,
|
|
"granularity", granularity,
|
|
)
|
|
granularity = ""
|
|
}
|
|
if snapshotType == "monthly" && granularity != "" && granularity != "hourly" && granularity != "daily" {
|
|
h.Logger.Warn("Snapshot aggregation invalid granularity", "granularity", granularity)
|
|
writeJSONError(w, http.StatusBadRequest, "granularity must be hourly or daily")
|
|
return
|
|
}
|
|
|
|
ctx := context.Background()
|
|
settingsCopy := *h.Settings.Values
|
|
if granularity != "" {
|
|
settingsCopy.Settings.MonthlyAggregationGranularity = granularity
|
|
}
|
|
ct := &tasks.CronTask{
|
|
Logger: h.Logger,
|
|
Database: h.Database,
|
|
Settings: &settings.Settings{Logger: h.Logger, SettingsPath: h.Settings.SettingsPath, Values: &settingsCopy},
|
|
}
|
|
|
|
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, "granularity", granularity)
|
|
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,
|
|
"granularity", granularity,
|
|
"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(models.ErrorResponse{
|
|
Status: "ERROR",
|
|
Message: message,
|
|
})
|
|
}
|