package web import ( "html/template" "net/http" ) // ConvoyFetcher defines the interface for fetching convoy data. type ConvoyFetcher interface { FetchConvoys() ([]ConvoyRow, error) FetchMergeQueue() ([]MergeQueueRow, error) FetchPolecats() ([]PolecatRow, error) } // ConvoyHandler handles HTTP requests for the convoy dashboard. type ConvoyHandler struct { fetcher ConvoyFetcher template *template.Template } // NewConvoyHandler creates a new convoy handler with the given fetcher. func NewConvoyHandler(fetcher ConvoyFetcher) (*ConvoyHandler, error) { tmpl, err := LoadTemplates() if err != nil { return nil, err } return &ConvoyHandler{ fetcher: fetcher, template: tmpl, }, nil } // ServeHTTP handles GET / requests and renders the convoy dashboard. func (h *ConvoyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { convoys, err := h.fetcher.FetchConvoys() if err != nil { http.Error(w, "Failed to fetch convoys", http.StatusInternalServerError) return } mergeQueue, err := h.fetcher.FetchMergeQueue() if err != nil { // Non-fatal: show convoys even if merge queue fails mergeQueue = nil } polecats, err := h.fetcher.FetchPolecats() if err != nil { // Non-fatal: show convoys even if polecats fail polecats = nil } data := ConvoyData{ Convoys: convoys, MergeQueue: mergeQueue, Polecats: polecats, } w.Header().Set("Content-Type", "text/html; charset=utf-8") if err := h.template.ExecuteTemplate(w, "convoy.html", data); err != nil { http.Error(w, "Failed to render template", http.StatusInternalServerError) return } }