package http import ( stdhttp "net/http" "time" "github.com/go-chi/chi/v5" ) // snapshotView is the public JSON shape for a snapshot. Matches the // wire-protocol Snapshot type plus a `refreshed_at` field so the UI // can show "list last refreshed Xm ago" — useful when the projection // is stale because the host has been offline for a while. type snapshotView struct { ID string `json:"id"` ShortID string `json:"short_id"` Time time.Time `json:"time"` Hostname string `json:"hostname"` Paths []string `json:"paths"` Tags []string `json:"tags,omitempty"` SizeBytes int64 `json:"size_bytes,omitempty"` FileCount int64 `json:"file_count,omitempty"` } type listSnapshotsResponse struct { HostID string `json:"host_id"` Count int `json:"count"` RefreshedAt *time.Time `json:"refreshed_at,omitempty"` Snapshots []snapshotView `json:"snapshots"` } // handleListHostSnapshots returns the cached snapshot projection for // one host. The agent refreshes this by sending `snapshots.report` // after each successful backup; this endpoint is a read-only view // onto whatever the server most recently received. func (s *Server) handleListHostSnapshots(w stdhttp.ResponseWriter, r *stdhttp.Request) { if _, ok := s.requireUser(r); !ok { writeJSONError(w, stdhttp.StatusUnauthorized, "unauthorized", "") return } hostID := chi.URLParam(r, "id") if hostID == "" { writeJSONError(w, stdhttp.StatusBadRequest, "missing_host_id", "") return } if _, err := s.deps.Store.GetHost(r.Context(), hostID); err != nil { writeJSONError(w, stdhttp.StatusNotFound, "host_not_found", "") return } snaps, err := s.deps.Store.ListSnapshotsByHost(r.Context(), hostID) if err != nil { writeJSONError(w, stdhttp.StatusInternalServerError, "internal", "") return } out := listSnapshotsResponse{ HostID: hostID, Count: len(snaps), Snapshots: make([]snapshotView, len(snaps)), } if len(snaps) > 0 { t := snaps[0].RefreshedAt out.RefreshedAt = &t } for i, sn := range snaps { out.Snapshots[i] = snapshotView{ ID: sn.ID, ShortID: sn.ShortID, Time: sn.Time, Hostname: sn.Hostname, Paths: sn.Paths, Tags: sn.Tags, SizeBytes: sn.SizeBytes, FileCount: sn.FileCount, } } writeJSON(w, stdhttp.StatusOK, out) }