package handler import ( "context" "encoding/json" "fmt" "net/http" "net/url" "vctp/components/views" "vctp/internal/report" "github.com/a-h/templ" ) // SnapshotHourlyList renders the hourly snapshot list page. // @Summary List hourly snapshots // @Description Lists hourly inventory snapshot tables. // @Tags snapshots // @Produce text/html // @Success 200 {string} string "HTML page" // @Failure 500 {string} string "Server error" // @Router /snapshots/hourly [get] func (h *Handler) SnapshotHourlyList(w http.ResponseWriter, r *http.Request) { h.renderSnapshotList(w, r, "hourly", "Hourly Inventory Snapshots", views.SnapshotHourlyList) } // SnapshotDailyList renders the daily snapshot list page. // @Summary List daily snapshots // @Description Lists daily summary snapshot tables. // @Tags snapshots // @Produce text/html // @Success 200 {string} string "HTML page" // @Failure 500 {string} string "Server error" // @Router /snapshots/daily [get] func (h *Handler) SnapshotDailyList(w http.ResponseWriter, r *http.Request) { h.renderSnapshotList(w, r, "daily", "Daily Inventory Snapshots", views.SnapshotDailyList) } // SnapshotMonthlyList renders the monthly snapshot list page. // @Summary List monthly snapshots // @Description Lists monthly summary snapshot tables. // @Tags snapshots // @Produce text/html // @Success 200 {string} string "HTML page" // @Failure 500 {string} string "Server error" // @Router /snapshots/monthly [get] func (h *Handler) SnapshotMonthlyList(w http.ResponseWriter, r *http.Request) { h.renderSnapshotList(w, r, "monthly", "Monthly Inventory Snapshots", views.SnapshotMonthlyList) } // SnapshotReportDownload streams a snapshot table as XLSX. // @Summary Download snapshot report // @Description Downloads a snapshot table as an XLSX file. // @Tags snapshots // @Produce application/vnd.openxmlformats-officedocument.spreadsheetml.sheet // @Param table query string true "Snapshot table name" // @Success 200 {file} file "Snapshot XLSX report" // @Failure 400 {object} map[string]string "Invalid request" // @Failure 500 {object} map[string]string "Server error" // @Router /api/report/snapshot [get] func (h *Handler) SnapshotReportDownload(w http.ResponseWriter, r *http.Request) { ctx := context.Background() tableName := r.URL.Query().Get("table") if tableName == "" { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusBadRequest) json.NewEncoder(w).Encode(map[string]string{ "status": "ERROR", "message": "Missing table parameter", }) return } reportData, err := report.CreateTableReport(h.Logger, h.Database, ctx, tableName) if err != nil { h.Logger.Error("Failed to create snapshot report", "error", err, "table", tableName) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusInternalServerError) json.NewEncoder(w).Encode(map[string]string{ "status": "ERROR", "message": fmt.Sprintf("Unable to create snapshot report: '%s'", err), }) return } filename := fmt.Sprintf("%s.xlsx", tableName) w.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename)) w.Header().Set("File-Name", filename) w.Write(reportData) } func (h *Handler) renderSnapshotList(w http.ResponseWriter, r *http.Request, snapshotType string, title string, renderer func([]views.SnapshotEntry) templ.Component) { ctx := context.Background() if err := report.EnsureSnapshotRegistry(ctx, h.Database); err != nil { h.Logger.Error("Failed to ensure snapshot registry", "error", err) w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, "Unable to list snapshot tables: %s\n", err) return } records, err := report.ListSnapshots(ctx, h.Database, snapshotType) if err != nil { h.Logger.Error("Failed to list snapshots", "error", err, "snapshot_type", snapshotType) w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, "Unable to list snapshot tables: %s\n", err) return } entries := make([]views.SnapshotEntry, 0, len(records)) for _, record := range records { label := report.FormatSnapshotLabel(snapshotType, record.SnapshotTime, record.TableName) entries = append(entries, views.SnapshotEntry{ Label: label, Link: "/api/report/snapshot?table=" + url.QueryEscape(record.TableName), Count: record.SnapshotCount, }) } if err := renderer(entries).Render(r.Context(), w); err != nil { h.Logger.Error("Failed to render snapshot list", "error", err, "title", title) w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, "Failed to render snapshot list") } }