package api // The owner manage routes under /api/v1/quizzes, behind user.api_token, the // golem15.quizzes.manage throttle, a token scope and the access_quizzes // permission. The MCP quiz tools bind them. Answers use the owner shapes of // manage_json.go, which carry the solutions. import ( "errors" "net/http" "strings" "git.golem15.com/golem15/sm-quizzes-plugin/classes" "git.golem15.com/golem15/summercms/modules/wire" "gorm.io/gorm" ) // manageDB returns the application database, or answers 503 as the student // routes do without one. func (h *Handlers) manageDB(w http.ResponseWriter) (*gorm.DB, bool) { var gdb *gorm.DB if h.App != nil { gdb, _ = h.App.Lookup[*gorm.DB]() } if gdb == nil { wire.WriteJSON(w, http.StatusServiceUnavailable, map[string]any{"error": "Database unavailable"}) return nil, false } return gdb, true } // writeManaged answers {data: data}, or maps err: ErrNotFound is a 404, // ErrQuizExists a 409, anything else an opaque 500. func writeManaged(w http.ResponseWriter, status int, data any, err error) { switch { case err == nil: wire.WriteJSON(w, status, map[string]any{"data": data}) case errors.Is(err, classes.ErrNotFound): wire.WriteJSON(w, http.StatusNotFound, map[string]any{"error": true, "message": "Quiz not found."}) case errors.Is(err, classes.ErrQuizExists): wire.WriteJSON(w, http.StatusConflict, map[string]any{"error": true, "message": "A quiz with this slug already exists."}) default: wire.WriteOpaque500(w) } } // ManageListQuizzes answers GET /api/v1/quizzes?context=: every quiz, // published or not, with its question and completed-attempt counts. func (h *Handlers) ManageListQuizzes(w http.ResponseWriter, r *http.Request) { db, ok := h.manageDB(w) if !ok { return } rows, err := classes.ListManagedQuizzes(r.Context(), db, strings.TrimSpace(r.URL.Query().Get("context"))) if err != nil { writeManaged(w, 0, nil, err) return } out := make([]managedQuizJSON, 0, len(rows)) for _, row := range rows { out = append(out, managedQuizRow(row)) } writeManaged(w, http.StatusOK, out, nil) } // ManageShowQuiz answers GET /api/v1/quizzes/{slug}: the quiz with every // question and answer, solutions included. func (h *Handlers) ManageShowQuiz(w http.ResponseWriter, r *http.Request) { db, ok := h.manageDB(w) if !ok { return } h.writeManagedQuiz(w, r, db, http.StatusOK, r.PathValue("slug")) } // writeManagedQuiz answers the quiz with its questions and solutions. func (h *Handlers) writeManagedQuiz(w http.ResponseWriter, r *http.Request, db *gorm.DB, status int, slug string) { quiz, err := classes.ManagedQuiz(r.Context(), db, slug) if err != nil { writeManaged(w, 0, nil, err) return } attempts, err := classes.CompletedAttemptCount(r.Context(), db, quiz.ID) if err != nil { writeManaged(w, 0, nil, err) return } writeManaged(w, status, managedQuizDetail(*quiz, attempts), nil) }