package httpserver import ( "net/http" "github.com/krow/krow-backend/go-api/internal/authctx" "github.com/krow/krow-backend/go-api/internal/domain" "github.com/krow/krow-backend/go-api/internal/owliver" ) // routeOwliver registers the Owliver panel's suggestion endpoint. // // GET, and a query string rather than a body, because the request is a read // with no side effect and the panel issues one per keystroke: a GET is what // makes it retryable, cancellable and cacheable by anything in front of it. // // It is deliberately NOT on the publicPaths allowlist in auth.go. Which // readings exist depends on the caller's role, so an anonymous suggestion has // no meaning — and the allowlist's failure mode is a route that refuses // everyone, which is the direction this should fall in. func (s *Server) routeOwliver(mux *http.ServeMux) int { mux.HandleFunc("GET /api/v1/owliver/suggestions", s.handleOwliverSuggestions) return 1 } // suggestionsBody is the payload inside the standard data envelope. // // An object rather than a bare array, so the response has somewhere to grow — a // future `truncated` or `context` field would otherwise be a breaking change to // a client already reading `data` as a list. type suggestionsBody struct { Suggestions []owliver.Suggestion `json:"suggestions"` } // handleOwliverSuggestions answers what the caller could usefully ask here. // // There is no s.authorize call and no policy lookup in this handler, and that // is the design rather than an omission: this endpoint exposes no resource, so // there is no operation to gate. Authorization happens per suggestion, inside // the catalogue, against the same domain.Policy table every other endpoint // consults — a reading the caller could not perform is never ranked, so it // cannot be returned. Authentication is upstream, in the middleware. func (s *Server) handleOwliverSuggestions(w http.ResponseWriter, r *http.Request) { ident, err := authctx.MustFrom(r.Context()) if err != nil { // Unreachable: the middleware refuses an unauthenticated request before // the router sees it. A missing identity here is a wiring bug. writeError(w, s.log, domain.Internal(err)) return } params, err := s.suggestions.ParseParams(r.URL.Query()) if err != nil { writeError(w, s.log, err) return } writeJSON(w, http.StatusOK, envelope{ Data: suggestionsBody{Suggestions: s.suggestions.Suggest(ident, params)}, }) }