package httpapi import ( "net/http" "github.com/metamorphosis-dev/kafka/internal/contracts" "github.com/metamorphosis-dev/kafka/internal/search" "github.com/metamorphosis-dev/kafka/internal/views" ) type Handler struct { searchSvc *search.Service } func NewHandler(searchSvc *search.Service) *Handler { return &Handler{searchSvc: searchSvc} } func (h *Handler) Healthz(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain; charset=utf-8") w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte("OK")) } // Index renders the homepage with the search box. func (h *Handler) Index(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/" { http.NotFound(w, r) return } if err := views.RenderIndex(w); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } } // OpenSearch serves the OpenSearch description XML. func (h *Handler) OpenSearch(baseURL string) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { xml, err := views.OpenSearchXML(baseURL) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/opensearchdescription+xml; charset=utf-8") w.Write(xml) } } func (h *Handler) Search(w http.ResponseWriter, r *http.Request) { // For HTML format with no query, redirect to homepage. if r.FormValue("q") == "" && (r.FormValue("format") == "" || r.FormValue("format") == "html") { http.Redirect(w, r, "/", http.StatusFound) return } req, err := search.ParseSearchRequest(r) if err != nil { // For HTML, render error on the results page. if req.Format == contracts.FormatHTML || r.FormValue("format") == "html" { pd := views.PageData{Query: r.FormValue("q")} if views.IsHTMXRequest(r) { views.RenderSearchFragment(w, pd) } else { views.RenderSearch(w, pd) } return } http.Error(w, err.Error(), http.StatusBadRequest) return } resp, err := h.searchSvc.Search(r.Context(), req) if err != nil { if req.Format == contracts.FormatHTML { pd := views.PageData{Query: req.Query} if views.IsHTMXRequest(r) { views.RenderSearchFragment(w, pd) } else { views.RenderSearch(w, pd) } return } http.Error(w, err.Error(), http.StatusInternalServerError) return } if req.Format == contracts.FormatHTML { pd := views.FromResponse(resp, req.Query, req.Pageno) if err := views.RenderSearchAuto(w, r, pd); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } return } if err := search.WriteSearchResponse(w, req.Format, resp); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } }