- quizzes_create_question, update_question, delete_question and
reorder_questions on /api/v1/quizzes/{slug}/questions
- update_question replaces answers by id (kept, created, deleted), so
answer ids stored in past attempts stay valid; foreign ids are refused
- classes.CheckQuestion holds the importer's per-question rules, shared
with the manage routes; attempt scores are never touched (D-16)
80 lines
3.4 KiB
Go
80 lines
3.4 KiB
Go
package quizzes
|
|
|
|
import (
|
|
"git.golem15.com/golem15/sm-quizzes-plugin/controllers/api"
|
|
"git.golem15.com/golem15/summercms/modules/pact"
|
|
"git.golem15.com/golem15/summercms/modules/surf"
|
|
)
|
|
|
|
var _ pact.HasRoutes = (*Plugin)(nil)
|
|
|
|
// ManagePrefix is the path prefix of the management API.
|
|
const ManagePrefix = "/api/v1/quizzes"
|
|
|
|
// permQuizzes is the permission that lets a user manage quizzes, in the
|
|
// admin and through the management API.
|
|
const permQuizzes = "golem15.quizzes.access_quizzes"
|
|
|
|
// bucketManage limits the owner manage routes (the MCP quiz tools). It runs
|
|
// after user.api_token, so it keys by user.
|
|
const bucketManage = "golem15.quizzes.manage"
|
|
|
|
// Routes mounts the quiz API under /_quizzes/api/v1 behind golem15.user's
|
|
// jwt.auth middleware, and the management API under /api/v1/quizzes behind
|
|
// its application-wide API token: the import route, and the owner manage
|
|
// routes the MCP quiz tools bind, which also need the access_quizzes
|
|
// permission and run under the per-user golem15.quizzes.manage throttle.
|
|
func (p *Plugin) Routes(r pact.Router) error {
|
|
h := api.New(p.app)
|
|
r.Group("/_quizzes/api/v1", surf.Use("jwt.auth"), func(g pact.Router) {
|
|
g.Get("/quizzes", h.ListQuizzes)
|
|
g.Get("/quizzes/{slug}", h.ShowQuiz)
|
|
g.Where("slug", `[a-z0-9-]+`)
|
|
g.Post("/quizzes/{slug}/attempts", h.StartAttempt)
|
|
g.Where("slug", `[a-z0-9-]+`)
|
|
g.Get("/attempts", h.ListAttempts)
|
|
g.Get("/attempts/{id}", h.ShowAttempt)
|
|
g.Where("id", `[0-9]+`)
|
|
g.Post("/attempts/{id}/answers", h.Answer)
|
|
g.Where("id", `[0-9]+`)
|
|
g.Post("/attempts/{id}/finish", h.Finish)
|
|
g.Where("id", `[0-9]+`)
|
|
})
|
|
// quizzes:import over HTTP. The token's scope limits delegation;
|
|
// user.permission checks the owner's current group permissions.
|
|
r.Group(ManagePrefix, surf.Use("throttle:60,1", "user.api_token"), func(g pact.Router) {
|
|
g.Post("/import", h.ImportQuiz, "user.scope:write", "user.permission:"+permQuizzes)
|
|
})
|
|
// The owner manage routes (MCP). user.api_token runs first so the
|
|
// bucket keys by user; every route needs the permission, so a student
|
|
// whose groups lack it never lists or reaches these tools.
|
|
perm := "user.permission:" + permQuizzes
|
|
r.Group(ManagePrefix, surf.Use("user.api_token", "throttle:"+bucketManage), func(g pact.Router) {
|
|
g.Get("", h.ManageListQuizzes, "user.scope:read", perm)
|
|
g.Get("/{slug}", h.ManageShowQuiz, "user.scope:read", perm)
|
|
g.Where("slug", `[a-z0-9-]+`)
|
|
g.Post("", h.ManageCreateQuiz, "user.scope:write", perm)
|
|
g.Patch("/{slug}", h.ManageUpdateQuiz, "user.scope:write", perm)
|
|
g.Where("slug", `[a-z0-9-]+`)
|
|
g.Delete("/{slug}", h.ManageDeleteQuiz, "user.scope:write", perm)
|
|
g.Where("slug", `[a-z0-9-]+`)
|
|
g.Post("/{slug}/publish", h.ManagePublish, "user.scope:write", perm)
|
|
g.Where("slug", `[a-z0-9-]+`)
|
|
g.Post("/{slug}/unpublish", h.ManageUnpublish, "user.scope:write", perm)
|
|
g.Where("slug", `[a-z0-9-]+`)
|
|
g.Get("/{slug}/stats", h.ManageQuizStats, "user.scope:read", perm)
|
|
g.Where("slug", `[a-z0-9-]+`)
|
|
g.Post("/{slug}/questions", h.ManageCreateQuestion, "user.scope:write", perm)
|
|
g.Where("slug", `[a-z0-9-]+`)
|
|
g.Patch("/{slug}/questions/{id}", h.ManageUpdateQuestion, "user.scope:write", perm)
|
|
g.Where("slug", `[a-z0-9-]+`)
|
|
g.Where("id", "[0-9]+")
|
|
g.Delete("/{slug}/questions/{id}", h.ManageDeleteQuestion, "user.scope:write", perm)
|
|
g.Where("slug", `[a-z0-9-]+`)
|
|
g.Where("id", "[0-9]+")
|
|
g.Put("/{slug}/questions/order", h.ManageReorderQuestions, "user.scope:write", perm)
|
|
g.Where("slug", `[a-z0-9-]+`)
|
|
})
|
|
return nil
|
|
}
|