package stacks import ( "database/sql" "encoding/json" "io" "net/http" "os" "screenmark/screenmark/middleware" "screenmark/screenmark/models" "github.com/charmbracelet/log" "github.com/go-chi/chi/v5" ) func writeJsonOrError[K any](logger *log.Logger, object K, w http.ResponseWriter) { jsonObject, err := json.Marshal(object) if err != nil { logger.Warn("could not marshal json object", "err", err) w.WriteHeader(http.StatusBadRequest) return } w.Write(jsonObject) w.WriteHeader(http.StatusOK) } type StackHandler struct { logger *log.Logger stackModel models.ListModel } func withValidatedPost[K any]( fn func(request K, w http.ResponseWriter, r *http.Request), ) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { request := new(K) body, err := io.ReadAll(r.Body) if err != nil { w.WriteHeader(http.StatusBadRequest) return } err = json.Unmarshal(body, request) if err != nil { w.WriteHeader(http.StatusBadRequest) return } fn(*request, w, r) } } func (h *StackHandler) getAllStacks(w http.ResponseWriter, r *http.Request) { ctx := r.Context() userID, err := middleware.GetUserID(ctx, h.logger, w) if err != nil { return } lists, err := h.stackModel.List(ctx, userID) if err != nil { h.logger.Warn("could not get stacks", "err", err) w.WriteHeader(http.StatusBadRequest) return } writeJsonOrError(h.logger, lists, w) } func (h *StackHandler) getStackItems(w http.ResponseWriter, r *http.Request) { ctx := r.Context() _, err := middleware.GetUserID(ctx, h.logger, w) if err != nil { return } listID, err := middleware.GetPathParamID(h.logger, "listID", w, r) if err != nil { return } // TODO: must check for permission here. lists, err := h.stackModel.ListItems(ctx, listID) if err != nil { h.logger.Warn("could not get list items", "err", err) w.WriteHeader(http.StatusBadRequest) return } writeJsonOrError(h.logger, lists, w) } type EditStack struct { Hello string `json:"hello"` } func (h *StackHandler) editStack(req EditStack, w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotImplemented) } type CreateStackBody struct { Title string `json:"title"` // We want a regular string because AI will take care of creating these for us. Fields string `json:"fields"` } func (h *StackHandler) createStack(body CreateStackBody, w http.ResponseWriter, r *http.Request) { ctx := r.Context() userID, err := middleware.GetUserID(ctx, h.logger, w) if err != nil { return } err = h.stackModel.SaveProcessing(ctx, userID, body.Title, body.Fields) if err != nil { h.logger.Warn("could not save processing", "err", err) w.WriteHeader(http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) } func (h *StackHandler) CreateRoutes(r chi.Router) { h.logger.Info("Mounting stack router") r.Group(func(r chi.Router) { r.Use(middleware.ProtectedRoute) r.Use(middleware.SetJson) r.Get("/", h.getAllStacks) r.Get("/{listID}", h.getStackItems) r.Post("/", withValidatedPost(h.createStack)) r.Patch("/{listID}", withValidatedPost(h.editStack)) }) } func CreateStackHandler(db *sql.DB) StackHandler { stackModel := models.NewListModel(db) logger := log.New(os.Stdout).WithPrefix("Stacks") return StackHandler{ logger, stackModel, } }