package http import ( stdhttp "net/http" "gitea.dcglab.co.uk/steve/restic-manager/internal/store" ) // hostView is the JSON projection of a Host row. Same shape as the // store row, but with explicit time-strings so wire format is stable // across DB driver changes. type hostView struct { ID string `json:"id"` Name string `json:"name"` OS string `json:"os"` Arch string `json:"arch"` AgentVersion string `json:"agent_version,omitempty"` ResticVersion string `json:"restic_version,omitempty"` ProtocolVersion int `json:"protocol_version"` EnrolledAt string `json:"enrolled_at"` LastSeenAt *string `json:"last_seen_at,omitempty"` Status string `json:"status"` Tags []string `json:"tags"` CurrentJobID *string `json:"current_job_id,omitempty"` LastBackupAt *string `json:"last_backup_at,omitempty"` LastBackupStatus *string `json:"last_backup_status,omitempty"` RepoSizeBytes int64 `json:"repo_size_bytes"` SnapshotCount int `json:"snapshot_count"` OpenAlertCount int `json:"open_alert_count"` } // handleListHosts returns the full fleet as JSON. Authenticated; the // shape mirrors what the dashboard renders, so external API consumers // see the same projection. func (s *Server) handleListHosts(w stdhttp.ResponseWriter, r *stdhttp.Request) { if !s.authedUser(r) { writeJSONError(w, stdhttp.StatusUnauthorized, "unauthorized", "") return } hosts, err := s.deps.Store.ListHosts(r.Context()) if err != nil { writeJSONError(w, stdhttp.StatusInternalServerError, "internal", "") return } out := make([]hostView, 0, len(hosts)) for _, h := range hosts { out = append(out, hostToView(h)) } writeJSON(w, stdhttp.StatusOK, map[string]any{ "count": len(out), "hosts": out, }) } // handleFleetSummary returns the dashboard tile aggregate. func (s *Server) handleFleetSummary(w stdhttp.ResponseWriter, r *stdhttp.Request) { if !s.authedUser(r) { writeJSONError(w, stdhttp.StatusUnauthorized, "unauthorized", "") return } fs, err := s.deps.Store.FleetSummary(r.Context()) if err != nil { writeJSONError(w, stdhttp.StatusInternalServerError, "internal", "") return } writeJSON(w, stdhttp.StatusOK, fs) } // hostToView converts a store.Host to its JSON shape. Times become // RFC 3339 strings; nullable times are passed through as nil. func hostToView(h store.Host) hostView { v := hostView{ ID: h.ID, Name: h.Name, OS: h.OS, Arch: h.Arch, AgentVersion: h.AgentVersion, ResticVersion: h.ResticVersion, ProtocolVersion: h.ProtocolVersion, EnrolledAt: h.EnrolledAt.Format("2006-01-02T15:04:05.000000000Z"), Status: h.Status, Tags: h.Tags, CurrentJobID: h.CurrentJobID, LastBackupStatus: h.LastBackupStatus, RepoSizeBytes: h.RepoSizeBytes, SnapshotCount: h.SnapshotCount, OpenAlertCount: h.OpenAlertCount, } if v.Tags == nil { v.Tags = []string{} } if h.LastSeenAt != nil { s := h.LastSeenAt.Format("2006-01-02T15:04:05.000000000Z") v.LastSeenAt = &s } if h.LastBackupAt != nil { s := h.LastBackupAt.Format("2006-01-02T15:04:05.000000000Z") v.LastBackupAt = &s } return v }