package server import ( "errors" "mime" "net/http" "net/url" "strconv" "strings" "cmbuyer/admin/internal/taskdetail" "cmbuyer/admin/internal/transport/webui" "github.com/gin-gonic/gin" ) const detailViewHeader = "X-CMBuyer-View" const detailVaryHeader = "X-CMBuyer-View, Accept, Sec-Fetch-Site" func taskDetailPage(options Options) gin.HandlerFunc { return func(context *gin.Context) { context.Header("Vary", detailVaryHeader) if !options.Sessions.IsAuthenticated(context.Request) { context.Redirect(http.StatusSeeOther, "/login?return_to="+url.QueryEscape(context.Request.URL.RequestURI())) return } view := context.GetHeader(detailViewHeader) if view != "" && view != "drawer" { context.Status(http.StatusBadRequest) return } if view == "drawer" { if context.GetHeader("Sec-Fetch-Site") != "same-origin" { context.Status(http.StatusForbidden) return } if !acceptsHTML(context.GetHeader("Accept")) { context.Status(http.StatusNotAcceptable) return } } detail, err := options.TaskDetails.Get(context.Request.Context(), context.Param("id")) if errors.Is(err, taskdetail.ErrNotFound) { context.Status(http.StatusNotFound) return } if err != nil { context.Status(http.StatusInternalServerError) return } context.Header("Content-Type", "text/html; charset=utf-8") context.Status(http.StatusOK) data := webui.TaskDetailData{Detail: detail} if view == "drawer" { if err := webui.RenderTaskDetailFragment(context.Writer, data); err != nil { _ = context.Error(err) } return } if err := webui.RenderTaskDetailPage(context.Writer, data); err != nil { _ = context.Error(err) } } } func acceptsHTML(header string) bool { for _, value := range strings.Split(header, ",") { mediaType, parameters, err := mime.ParseMediaType(strings.TrimSpace(value)) if err != nil || !strings.EqualFold(mediaType, "text/html") { continue } quality := 1.0 if rawQuality, exists := parameters["q"]; exists { quality, err = strconv.ParseFloat(rawQuality, 64) if err != nil || quality < 0 || quality > 1 { continue } } if quality > 0 { return true } } return false }