feat: use generic Response type and rewrite swagger documentation. close #1
This commit is contained in:
parent
9485dbbce4
commit
eb6f5d0aca
2
Makefile
2
Makefile
@ -31,7 +31,7 @@ dep:
|
|||||||
|
|
||||||
swagger:
|
swagger:
|
||||||
go install github.com/swaggo/swag/cmd/swag@latest
|
go install github.com/swaggo/swag/cmd/swag@latest
|
||||||
$(GOBIN)/swag init -g internal/web/router/api.go -o internal/web/router/docs
|
$(GOBIN)/swag init -g internal/web/router/api.go -d .,./internal/e,./internal/model --pdl 1 -o internal/web/router/docs
|
||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
go fmt ./...
|
go fmt ./...
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
var _ Handler = (*handler)(nil)
|
var _ Handler = (*handler)(nil)
|
||||||
|
|
||||||
type Handler interface {
|
type Handler interface {
|
||||||
randomString(c *gin.Context)
|
RandomString(c *gin.Context)
|
||||||
}
|
}
|
||||||
|
|
||||||
type handler struct {
|
type handler struct {
|
||||||
@ -21,5 +21,5 @@ func RouteRegister(rg *gin.RouterGroup, i *do.Injector) {
|
|||||||
app := &handler{}
|
app := &handler{}
|
||||||
app.log = do.MustInvoke[log.Service](i).GetLogger("api.debug")
|
app.log = do.MustInvoke[log.Service](i).GetLogger("api.debug")
|
||||||
|
|
||||||
rg.GET("/random", app.randomString)
|
rg.GET("/random", app.RandomString)
|
||||||
}
|
}
|
||||||
|
@ -7,14 +7,14 @@ import (
|
|||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
// randomString
|
// RandomString
|
||||||
// @Summary random string
|
// @Summary generate random string
|
||||||
// @Description generate random string with length = 32
|
// @Description Generate random string with length = 32.
|
||||||
// @Tags debug
|
// @Tags debug
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Response 200 {object} e.Response "random string"
|
// @Response 200 {object} e.Response[string] "random string"
|
||||||
// @Router /debug/random [get]
|
// @Router /debug/random [get]
|
||||||
func (h *handler) randomString(c *gin.Context) {
|
func (h *handler) RandomString(c *gin.Context) {
|
||||||
str := utils.RandomString(32)
|
str := utils.RandomString(32)
|
||||||
h.log.Info("random string", zap.String("str", str))
|
h.log.Info("random string", zap.String("str", str))
|
||||||
e.Pong(c, e.Success, str)
|
e.Pong(c, e.Success, str)
|
||||||
|
@ -13,55 +13,61 @@ type createVersionRequest struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateVersion
|
// CreateVersion
|
||||||
// @Summary create a problem version
|
// @Summary [admin] create a problem version
|
||||||
// @Description create a problem version
|
// @Description Create a problem version associated with `pid`.
|
||||||
// @Tags problem
|
// @Tags problem,admin
|
||||||
// @Accept application/x-www-form-urlencoded
|
// @Accept application/x-www-form-urlencoded
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param pid formData int true "problem id"
|
// @Param pid formData int true "problem id"
|
||||||
// @Param storage_key formData string true "storage key"
|
// @Param storage_key formData string true "storage key, zip file containing problem data"
|
||||||
// @Response 200 {object} e.Response ""
|
// @Response 200 {object} e.Response[any] "nothing"
|
||||||
// @Security Authentication
|
// @Security Authentication
|
||||||
// @Router /v1/problem/create_version [post]
|
// @Router /v1/problem/create_version [post]
|
||||||
func (h *handler) CreateVersion(c *gin.Context) {
|
func (h *handler) CreateVersion(c *gin.Context) {
|
||||||
claim, exist := c.Get("claim")
|
claim, exist := c.Get("claim")
|
||||||
if !exist {
|
if !exist {
|
||||||
e.Pong(c, e.UserUnauthenticated, nil)
|
e.Pong[any](c, e.UserUnauthenticated, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// uid := claim.(*model.Claim).UID
|
|
||||||
|
|
||||||
role := claim.(*model.Claim).Role
|
|
||||||
req := new(createVersionRequest)
|
req := new(createVersionRequest)
|
||||||
|
|
||||||
if err := c.ShouldBind(req); err != nil {
|
if err := c.ShouldBind(req); err != nil {
|
||||||
e.Pong(c, e.InvalidParameter, err.Error())
|
e.Pong(c, e.InvalidParameter, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// guest can not submit
|
// only admin can create problem version
|
||||||
|
role := claim.(*model.Claim).Role
|
||||||
if role < model.RoleAdmin {
|
if role < model.RoleAdmin {
|
||||||
e.Pong(c, e.UserUnauthorized, nil)
|
e.Pong[any](c, e.UserUnauthorized, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: check pid exist
|
// make sure problem exists
|
||||||
|
_, status := h.problemService.Query(req.ProblemID, false, false)
|
||||||
|
if status != e.Success {
|
||||||
|
e.Pong[any](c, status, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// create problem version
|
||||||
createVersionData := &problem.CreateVersionData{
|
createVersionData := &problem.CreateVersionData{
|
||||||
ProblemID: req.ProblemID,
|
ProblemID: req.ProblemID,
|
||||||
StorageKey: req.StorageKey,
|
StorageKey: req.StorageKey,
|
||||||
}
|
}
|
||||||
pv, status := h.problemService.CreateVersion(createVersionData)
|
pv, status := h.problemService.CreateVersion(createVersionData)
|
||||||
if status != e.Success {
|
if status != e.Success {
|
||||||
e.Pong(c, status, nil)
|
e.Pong[any](c, status, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// enqueue task: runner build problem
|
||||||
payload := &model.ProblemBuildPayload{
|
payload := &model.ProblemBuildPayload{
|
||||||
ProblemVersionID: pv.ID,
|
ProblemVersionID: pv.ID,
|
||||||
StorageKey: pv.StorageKey,
|
StorageKey: pv.StorageKey,
|
||||||
}
|
}
|
||||||
_, status = h.taskService.ProblemBuild(payload)
|
_, status = h.taskService.ProblemBuild(payload)
|
||||||
e.Pong(c, status, nil)
|
e.Pong[any](c, status, nil)
|
||||||
|
|
||||||
|
// TODO: if failed, delete problem version
|
||||||
}
|
}
|
@ -10,18 +10,22 @@ type detailsRequest struct {
|
|||||||
Pid uint `form:"pid"`
|
Pid uint `form:"pid"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type problemDetailsResponse struct {
|
||||||
|
Problem *model.Problem `json:"problem"`
|
||||||
|
Context interface{} `json:"context"`
|
||||||
|
}
|
||||||
|
|
||||||
// Details
|
// Details
|
||||||
// @Summary get details of a problem
|
// @Summary get details of a problem
|
||||||
// @Description get details of a problem
|
// @Description Get details of a problem.
|
||||||
// @Tags problem
|
// @Tags problem
|
||||||
// @Accept application/x-www-form-urlencoded
|
// @Accept application/x-www-form-urlencoded
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param pid formData int true "problem id"
|
// @Param pid formData int true "problem id"
|
||||||
// @Response 200 {object} e.Response "problem details"
|
// @Response 200 {object} e.Response[problemDetailsResponse] "problem details"
|
||||||
// @Router /v1/problem/details [post]
|
// @Router /v1/problem/details [post]
|
||||||
func (h *handler) Details(c *gin.Context) {
|
func (h *handler) Details(c *gin.Context) {
|
||||||
req := new(detailsRequest)
|
req := new(detailsRequest)
|
||||||
|
|
||||||
if err := c.ShouldBind(req); err != nil {
|
if err := c.ShouldBind(req); err != nil {
|
||||||
e.Pong(c, e.InvalidParameter, err.Error())
|
e.Pong(c, e.InvalidParameter, err.Error())
|
||||||
return
|
return
|
||||||
@ -32,17 +36,18 @@ func (h *handler) Details(c *gin.Context) {
|
|||||||
|
|
||||||
p, status := h.problemService.Query(req.Pid, true, shouldEnable)
|
p, status := h.problemService.Query(req.Pid, true, shouldEnable)
|
||||||
if status != e.Success {
|
if status != e.Success {
|
||||||
e.Pong(c, status, nil)
|
e.Pong[any](c, status, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pv, status := h.problemService.QueryLatestVersion(req.Pid)
|
pv, status := h.problemService.QueryLatestVersion(req.Pid)
|
||||||
if status != e.Success {
|
if status != e.Success {
|
||||||
e.Pong(c, status, nil)
|
e.Pong[any](c, status, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
e.Pong(c, e.Success, gin.H{
|
|
||||||
"problem": p,
|
e.Pong(c, e.Success, problemDetailsResponse{
|
||||||
"context": pv.Context.Get(),
|
Problem: p,
|
||||||
|
Context: pv.Context.Get(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package problem
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"git.0x7f.app/WOJ/woj-server/internal/e"
|
"git.0x7f.app/WOJ/woj-server/internal/e"
|
||||||
|
_ "git.0x7f.app/WOJ/woj-server/internal/model" // swag requires this
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -10,17 +11,16 @@ type searchRequest struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Search
|
// Search
|
||||||
// @Summary get detail of a problem
|
// @Summary search for problems
|
||||||
// @Description get detail of a problem
|
// @Description Search for problems based on keywords. If the keyword is empty, return all problems.
|
||||||
// @Tags problem
|
// @Tags problem
|
||||||
// @Accept application/x-www-form-urlencoded
|
// @Accept application/x-www-form-urlencoded
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param search formData string false "word search"
|
// @Param search formData string false "keyword"
|
||||||
// @Response 200 {object} e.Response "problemset"
|
// @Response 200 {object} e.Response[[]model.Problem] "problems found"
|
||||||
// @Router /v1/problem/search [post]
|
// @Router /v1/problem/search [post]
|
||||||
func (h *handler) Search(c *gin.Context) {
|
func (h *handler) Search(c *gin.Context) {
|
||||||
req := new(searchRequest)
|
req := new(searchRequest)
|
||||||
|
|
||||||
if err := c.ShouldBind(req); err != nil {
|
if err := c.ShouldBind(req); err != nil {
|
||||||
e.Pong(c, e.InvalidParameter, err.Error())
|
e.Pong(c, e.InvalidParameter, err.Error())
|
||||||
return
|
return
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"git.0x7f.app/WOJ/woj-server/internal/e"
|
"git.0x7f.app/WOJ/woj-server/internal/e"
|
||||||
"git.0x7f.app/WOJ/woj-server/internal/model"
|
"git.0x7f.app/WOJ/woj-server/internal/model"
|
||||||
"git.0x7f.app/WOJ/woj-server/internal/service/problem"
|
"git.0x7f.app/WOJ/woj-server/internal/service/problem"
|
||||||
|
"git.0x7f.app/WOJ/woj-server/pkg/utils"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -15,29 +16,31 @@ type updateRequest struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update
|
// Update
|
||||||
// @Summary create or update a problem
|
// @Summary [admin] create or update a problem
|
||||||
// @Description create or update a problem
|
// @Description Create or update a problem.
|
||||||
// @Tags problem
|
// @Tags problem,admin
|
||||||
// @Accept application/x-www-form-urlencoded
|
// @Accept application/x-www-form-urlencoded
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param pid formData int false "problem id, 0 for create"
|
// @Param pid formData int false "problem id, 0 for create"
|
||||||
// @Param title formData string true "title"
|
// @Param title formData string true "title"
|
||||||
// @Param statement formData string true "statement"
|
// @Param statement formData string true "statement"
|
||||||
// @Param is_enabled formData bool false "is enabled"
|
// @Param is_enabled formData bool false "is enabled"
|
||||||
// @Response 200 {object} e.Response "problem info without provider information"
|
// @Response 200 {object} e.Response[model.Problem] "problem info without provider information"
|
||||||
// @Security Authentication
|
// @Security Authentication
|
||||||
// @Router /v1/problem/update [post]
|
// @Router /v1/problem/update [post]
|
||||||
func (h *handler) Update(c *gin.Context) {
|
func (h *handler) Update(c *gin.Context) {
|
||||||
claim, exist := c.Get("claim")
|
claim, exist := c.Get("claim")
|
||||||
if !exist {
|
if !exist {
|
||||||
e.Pong(c, e.UserUnauthenticated, nil)
|
e.Pong[any](c, e.UserUnauthenticated, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
uid := claim.(*model.Claim).UID
|
uid := claim.(*model.Claim).UID
|
||||||
role := claim.(*model.Claim).Role
|
role := claim.(*model.Claim).Role
|
||||||
|
|
||||||
|
// only admin can modify problem
|
||||||
if role < model.RoleAdmin {
|
if role < model.RoleAdmin {
|
||||||
e.Pong(c, e.UserUnauthorized, nil)
|
e.Pong[any](c, e.UserUnauthorized, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,6 +51,7 @@ func (h *handler) Update(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if req.Pid == 0 {
|
if req.Pid == 0 {
|
||||||
|
// create problem
|
||||||
createData := &problem.CreateData{
|
createData := &problem.CreateData{
|
||||||
Title: req.Title,
|
Title: req.Title,
|
||||||
Statement: req.Statement,
|
Statement: req.Statement,
|
||||||
@ -58,18 +62,24 @@ func (h *handler) Update(c *gin.Context) {
|
|||||||
e.Pong(c, status, p)
|
e.Pong(c, status, p)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
|
// update problem
|
||||||
|
|
||||||
|
// check if problem exists
|
||||||
p, status := h.problemService.Query(req.Pid, true, false)
|
p, status := h.problemService.Query(req.Pid, true, false)
|
||||||
if status != e.Success {
|
if status != e.Success {
|
||||||
e.Pong(c, status, nil)
|
e.Pong[any](c, status, nil)
|
||||||
return
|
|
||||||
}
|
|
||||||
if p.ProviderID != uid {
|
|
||||||
e.Pong(c, e.UserUnauthorized, nil)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
p.Title = req.Title
|
// check if user is the provider of the problem
|
||||||
p.Statement = req.Statement
|
if p.ProviderID != uid {
|
||||||
|
e.Pong[any](c, e.UserUnauthorized, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// update problem
|
||||||
|
p.Title = utils.If(req.Title != "", req.Title, p.Title)
|
||||||
|
p.Statement = utils.If(req.Statement != "", req.Statement, p.Statement)
|
||||||
p.IsEnabled = req.IsEnabled
|
p.IsEnabled = req.IsEnabled
|
||||||
|
|
||||||
p, status = h.problemService.Update(p)
|
p, status = h.problemService.Update(p)
|
||||||
|
@ -8,37 +8,40 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type uploadResponse struct {
|
||||||
|
Key string `json:"key"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
// Upload
|
// Upload
|
||||||
// @Summary get upload url
|
// @Summary [admin] get upload url
|
||||||
// @Description get upload url
|
// @Description Retrieve a pre-signed upload URL from the object storage
|
||||||
// @Tags problem
|
// @Tags problem,admin
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Response 200 {object} e.Response "upload url and key"
|
// @Response 200 {object} e.Response[uploadResponse] "upload url and key"
|
||||||
// @Security Authentication
|
// @Security Authentication
|
||||||
// @Router /v1/problem/upload [post]
|
// @Router /v1/problem/upload [post]
|
||||||
func (h *handler) Upload(c *gin.Context) {
|
func (h *handler) Upload(c *gin.Context) {
|
||||||
claim, exist := c.Get("claim")
|
claim, exist := c.Get("claim")
|
||||||
if !exist {
|
if !exist {
|
||||||
e.Pong(c, e.UserUnauthenticated, nil)
|
e.Pong[any](c, e.UserUnauthenticated, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// only admin can upload
|
||||||
role := claim.(*model.Claim).Role
|
role := claim.(*model.Claim).Role
|
||||||
if role < model.RoleAdmin {
|
if role < model.RoleAdmin {
|
||||||
e.Pong(c, e.UserUnauthorized, nil)
|
e.Pong[any](c, e.UserUnauthorized, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generate random key
|
||||||
key := utils.RandomString(16)
|
key := utils.RandomString(16)
|
||||||
url, status := h.storageService.Upload(key, time.Second*60*60)
|
url, status := h.storageService.Upload(key, time.Second*60*60)
|
||||||
|
|
||||||
if status != e.Success {
|
if status != e.Success {
|
||||||
e.Pong(c, status, nil)
|
e.Pong[any](c, status, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
e.Pong(c, e.Success, gin.H{
|
e.Pong(c, e.Success, uploadResponse{Key: key, URL: url})
|
||||||
"key": key,
|
|
||||||
"url": url,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package status
|
|||||||
import (
|
import (
|
||||||
"git.0x7f.app/WOJ/woj-server/internal/misc/log"
|
"git.0x7f.app/WOJ/woj-server/internal/misc/log"
|
||||||
"git.0x7f.app/WOJ/woj-server/internal/service/status"
|
"git.0x7f.app/WOJ/woj-server/internal/service/status"
|
||||||
|
"git.0x7f.app/WOJ/woj-server/internal/service/submission"
|
||||||
"git.0x7f.app/WOJ/woj-server/internal/web/jwt"
|
"git.0x7f.app/WOJ/woj-server/internal/web/jwt"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/samber/do"
|
"github.com/samber/do"
|
||||||
@ -13,22 +14,26 @@ var _ Handler = (*handler)(nil)
|
|||||||
|
|
||||||
type Handler interface {
|
type Handler interface {
|
||||||
Query(c *gin.Context)
|
Query(c *gin.Context)
|
||||||
|
QueryBySubmissionID(c *gin.Context)
|
||||||
QueryByProblemVersion(c *gin.Context)
|
QueryByProblemVersion(c *gin.Context)
|
||||||
}
|
}
|
||||||
|
|
||||||
type handler struct {
|
type handler struct {
|
||||||
log *zap.Logger
|
log *zap.Logger
|
||||||
statusService status.Service
|
statusService status.Service
|
||||||
jwtService jwt.Service
|
submissionService submission.Service
|
||||||
|
jwtService jwt.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
func RouteRegister(rg *gin.RouterGroup, i *do.Injector) {
|
func RouteRegister(rg *gin.RouterGroup, i *do.Injector) {
|
||||||
app := &handler{
|
app := &handler{
|
||||||
log: do.MustInvoke[log.Service](i).GetLogger("api.status"),
|
log: do.MustInvoke[log.Service](i).GetLogger("api.status"),
|
||||||
statusService: do.MustInvoke[status.Service](i),
|
submissionService: do.MustInvoke[submission.Service](i),
|
||||||
jwtService: do.MustInvoke[jwt.Service](i),
|
statusService: do.MustInvoke[status.Service](i),
|
||||||
|
jwtService: do.MustInvoke[jwt.Service](i),
|
||||||
}
|
}
|
||||||
|
|
||||||
rg.POST("/query", app.Query)
|
rg.POST("/query", app.jwtService.Handler(true), app.Query)
|
||||||
rg.POST("/query/problem_version", app.jwtService.Handler(true), app.QueryByProblemVersion)
|
rg.POST("/query/submission", app.jwtService.Handler(true), app.QueryBySubmissionID)
|
||||||
|
rg.POST("/query/version", app.jwtService.Handler(true), app.QueryByProblemVersion)
|
||||||
}
|
}
|
||||||
|
@ -2,33 +2,74 @@ package status
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"git.0x7f.app/WOJ/woj-server/internal/e"
|
"git.0x7f.app/WOJ/woj-server/internal/e"
|
||||||
|
"git.0x7f.app/WOJ/woj-server/internal/model"
|
||||||
|
"git.0x7f.app/WOJ/woj-server/pkg/utils"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
type queryRequest struct {
|
type queryRequest struct {
|
||||||
SubmissionID uint `form:"sid" binding:"required"`
|
Pid uint `form:"pid"`
|
||||||
|
Uid uint `form:"uid"`
|
||||||
|
Offset int `form:"offset"`
|
||||||
|
Limit int `form:"limit" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type queryResponse struct {
|
||||||
|
Submission model.Submission `json:"submission"`
|
||||||
|
Point int32 `json:"point"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query
|
// Query
|
||||||
// @Summary query submissions by via submission id
|
// @Summary query status via problem id or user id
|
||||||
// @Description query submissions by via submission id
|
// @Description Batch query judgement status based on either the question or user.
|
||||||
// @Tags status
|
// @Tags status
|
||||||
// @Accept application/x-www-form-urlencoded
|
// @Accept application/x-www-form-urlencoded
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param sid formData uint true "submission id"
|
// @Param pid formData uint false "problem id"
|
||||||
// @Response 200 {object} e.Response "model.status"
|
// @Param uid formData uint false "user id"
|
||||||
|
// @Param offset formData int false "start position"
|
||||||
|
// @Param limit formData int true "limit number of records"
|
||||||
|
// @Response 200 {object} e.Response[[]queryResponse] "queryResponse"
|
||||||
// @Router /v1/status/query [post]
|
// @Router /v1/status/query [post]
|
||||||
func (h *handler) Query(c *gin.Context) {
|
func (h *handler) Query(c *gin.Context) {
|
||||||
// TODO: add permission check
|
claim, exist := c.Get("claim")
|
||||||
|
if !exist {
|
||||||
|
e.Pong[any](c, e.UserUnauthenticated, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
req := new(queryRequest)
|
req := new(queryRequest)
|
||||||
|
|
||||||
if err := c.ShouldBind(req); err != nil {
|
if err := c.ShouldBind(req); err != nil {
|
||||||
e.Pong(c, e.InvalidParameter, err.Error())
|
e.Pong(c, e.InvalidParameter, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
status, eStatus := h.statusService.Query(req.SubmissionID, true)
|
if req.Pid == 0 && req.Uid == 0 {
|
||||||
|
e.Pong[any](c, e.InvalidParameter, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
e.Pong(c, eStatus, status)
|
submissions, status := h.submissionService.Query(req.Pid, req.Uid, req.Offset, req.Limit)
|
||||||
|
|
||||||
|
uid := claim.(*model.Claim).UID
|
||||||
|
role := claim.(*model.Claim).Role
|
||||||
|
var response []*queryResponse
|
||||||
|
|
||||||
|
for _, submission := range submissions {
|
||||||
|
cur, _ := h.statusService.Query(submission.ID, false)
|
||||||
|
point := utils.If(cur == nil, -1, cur.Point)
|
||||||
|
resp := &queryResponse{
|
||||||
|
Submission: *submission,
|
||||||
|
Point: point,
|
||||||
|
}
|
||||||
|
|
||||||
|
if role < model.RoleAdmin || uid != submission.UserID {
|
||||||
|
// strip out code
|
||||||
|
resp.Submission.Code = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
response = append(response, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Pong(c, status, response)
|
||||||
}
|
}
|
||||||
|
50
internal/api/status/query_one.go
Normal file
50
internal/api/status/query_one.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package status
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.0x7f.app/WOJ/woj-server/internal/e"
|
||||||
|
"git.0x7f.app/WOJ/woj-server/internal/model"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type queryOneRequest struct {
|
||||||
|
SubmissionID uint `form:"sid" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryBySubmissionID
|
||||||
|
// @Summary query status via submission id
|
||||||
|
// @Description Query the detailed results of the judgement based on the submission ID.
|
||||||
|
// @Tags status
|
||||||
|
// @Accept application/x-www-form-urlencoded
|
||||||
|
// @Produce json
|
||||||
|
// @Param sid formData uint true "submission id"
|
||||||
|
// @Response 200 {object} e.Response[model.Status] "submission status"
|
||||||
|
// @Router /v1/status/query/submission [post]
|
||||||
|
func (h *handler) QueryBySubmissionID(c *gin.Context) {
|
||||||
|
claim, exist := c.Get("claim")
|
||||||
|
if !exist {
|
||||||
|
e.Pong[any](c, e.UserUnauthenticated, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := new(queryOneRequest)
|
||||||
|
if err := c.ShouldBind(req); err != nil {
|
||||||
|
e.Pong(c, e.InvalidParameter, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// query status
|
||||||
|
submitStatus, status := h.statusService.Query(req.SubmissionID, true)
|
||||||
|
|
||||||
|
// check permission
|
||||||
|
role := claim.(*model.Claim).Role
|
||||||
|
if role >= model.RoleAdmin || submitStatus.Submission.UserID == claim.(*model.Claim).UID {
|
||||||
|
// full status
|
||||||
|
e.Pong(c, status, submitStatus)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
// strip out code
|
||||||
|
submitStatus.Submission.Code = ""
|
||||||
|
e.Pong(c, status, submitStatus)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
@ -13,39 +13,37 @@ type queryByVersionRequest struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// QueryByProblemVersion
|
// QueryByProblemVersion
|
||||||
// @Summary query submissions by problem version (admin only)
|
// @Summary [admin] query status by problem version
|
||||||
// @Description query submissions by problem version (admin only)
|
// @Description Retrieve all judgement results corresponding to the problem version.
|
||||||
// @Tags status
|
// @Tags status,admin
|
||||||
// @Accept application/x-www-form-urlencoded
|
// @Accept application/x-www-form-urlencoded
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param pvid formData uint true "problem version id"
|
// @Param pvid formData uint true "problem version"
|
||||||
// @Param offset formData int false "start position"
|
// @Param offset formData int false "start position"
|
||||||
// @Param limit formData int true "limit number of records"
|
// @Param limit formData int true "max number of results"
|
||||||
// @Response 200 {object} e.Response "[]*model.status"
|
// @Response 200 {object} e.Response[[]model.Status] "submission status array"
|
||||||
// @Security Authentication
|
// @Security Authentication
|
||||||
// @Router /v1/status/query/problem_version [post]
|
// @Router /v1/status/query/version [post]
|
||||||
func (h *handler) QueryByProblemVersion(c *gin.Context) {
|
func (h *handler) QueryByProblemVersion(c *gin.Context) {
|
||||||
claim, exist := c.Get("claim")
|
claim, exist := c.Get("claim")
|
||||||
if !exist {
|
if !exist {
|
||||||
e.Pong(c, e.UserUnauthenticated, nil)
|
e.Pong[any](c, e.UserUnauthenticated, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
role := claim.(*model.Claim).Role
|
|
||||||
|
|
||||||
req := new(queryByVersionRequest)
|
req := new(queryByVersionRequest)
|
||||||
|
|
||||||
if err := c.ShouldBind(req); err != nil {
|
if err := c.ShouldBind(req); err != nil {
|
||||||
e.Pong(c, e.InvalidParameter, err.Error())
|
e.Pong(c, e.InvalidParameter, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check permission
|
||||||
|
role := claim.(*model.Claim).Role
|
||||||
if role < model.RoleAdmin {
|
if role < model.RoleAdmin {
|
||||||
e.Pong(c, e.UserUnauthorized, nil)
|
e.Pong[any](c, e.UserUnauthorized, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
statuses, eStatus := h.statusService.QueryByVersion(req.ProblemVersionID, req.Offset, req.Limit)
|
submitStatus, status := h.statusService.QueryByVersion(req.ProblemVersionID, req.Offset, req.Limit)
|
||||||
|
e.Pong(c, status, submitStatus)
|
||||||
e.Pong(c, eStatus, statuses)
|
|
||||||
}
|
}
|
@ -14,21 +14,21 @@ type createRequest struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create
|
// Create
|
||||||
// @Summary create a submission
|
// @Summary submit for judgement
|
||||||
// @Description create a submission
|
// @Description Submit the code for judgement.
|
||||||
// @Tags submission
|
// @Tags submission
|
||||||
// @Accept application/x-www-form-urlencoded
|
// @Accept application/x-www-form-urlencoded
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param pid formData int true "problem id"
|
// @Param pid formData int true "problem id"
|
||||||
// @Param language formData string true "language"
|
// @Param language formData string true "language"
|
||||||
// @Param code formData string true "code"
|
// @Param code formData string true "code"
|
||||||
// @Response 200 {object} e.Response ""
|
// @Response 200 {object} e.Response[uint] "submission id"
|
||||||
// @Security Authentication
|
// @Security Authentication
|
||||||
// @Router /v1/submission/create [post]
|
// @Router /v1/submission/create [post]
|
||||||
func (h *handler) Create(c *gin.Context) {
|
func (h *handler) Create(c *gin.Context) {
|
||||||
claim, exist := c.Get("claim")
|
claim, exist := c.Get("claim")
|
||||||
if !exist {
|
if !exist {
|
||||||
e.Pong(c, e.UserUnauthenticated, nil)
|
e.Pong[any](c, e.UserUnauthenticated, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ func (h *handler) Create(c *gin.Context) {
|
|||||||
|
|
||||||
// guest can not submit
|
// guest can not submit
|
||||||
if role < model.RoleGeneral {
|
if role < model.RoleGeneral {
|
||||||
e.Pong(c, e.UserUnauthorized, nil)
|
e.Pong[any](c, e.UserUnauthorized, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,30 +47,33 @@ func (h *handler) Create(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create submission
|
||||||
createData := &submission.CreateData{
|
createData := &submission.CreateData{
|
||||||
ProblemID: req.Pid,
|
ProblemID: req.Pid,
|
||||||
UserID: uid,
|
UserID: uid,
|
||||||
Language: req.Language,
|
Language: req.Language,
|
||||||
Code: req.Code,
|
Code: req.Code,
|
||||||
}
|
}
|
||||||
s, status := h.submissionService.Create(createData)
|
res, status := h.submissionService.Create(createData)
|
||||||
if status != e.Success {
|
if status != e.Success {
|
||||||
e.Pong(c, status, nil)
|
e.Pong[any](c, status, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// query latest version
|
||||||
pv, status := h.problemService.QueryLatestVersion(req.Pid)
|
pv, status := h.problemService.QueryLatestVersion(req.Pid)
|
||||||
if status != e.Success {
|
if status != e.Success {
|
||||||
e.Pong(c, status, nil)
|
e.Pong[any](c, status, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// submit judge
|
||||||
payload := &model.SubmitJudgePayload{
|
payload := &model.SubmitJudgePayload{
|
||||||
ProblemVersionID: pv.ID,
|
ProblemVersionID: pv.ID,
|
||||||
StorageKey: pv.StorageKey,
|
StorageKey: pv.StorageKey,
|
||||||
Submission: *s,
|
Submission: *res,
|
||||||
}
|
}
|
||||||
_, status = h.taskService.SubmitJudge(payload)
|
_, status = h.taskService.SubmitJudge(payload)
|
||||||
|
|
||||||
e.Pong(c, status, nil)
|
e.Pong[any](c, status, res.ID)
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@ var _ Handler = (*handler)(nil)
|
|||||||
|
|
||||||
type Handler interface {
|
type Handler interface {
|
||||||
Create(c *gin.Context)
|
Create(c *gin.Context)
|
||||||
Query(c *gin.Context)
|
|
||||||
Rejudge(c *gin.Context)
|
Rejudge(c *gin.Context)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,6 +39,5 @@ func RouteRegister(rg *gin.RouterGroup, i *do.Injector) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
rg.POST("/create", app.jwtService.Handler(true), app.Create)
|
rg.POST("/create", app.jwtService.Handler(true), app.Create)
|
||||||
rg.POST("/query", app.Query)
|
|
||||||
rg.POST("/rejudge", app.jwtService.Handler(true), app.Rejudge)
|
rg.POST("/rejudge", app.jwtService.Handler(true), app.Rejudge)
|
||||||
}
|
}
|
||||||
|
@ -1,72 +0,0 @@
|
|||||||
package submission
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.0x7f.app/WOJ/woj-server/internal/e"
|
|
||||||
"git.0x7f.app/WOJ/woj-server/internal/model"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
)
|
|
||||||
|
|
||||||
type queryRequest struct {
|
|
||||||
Pid uint `form:"pid"`
|
|
||||||
Uid uint `form:"uid"`
|
|
||||||
Offset int `form:"offset"`
|
|
||||||
Limit int `form:"limit" binding:"required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type queryResponse struct {
|
|
||||||
Submission model.Submission `json:"submission"`
|
|
||||||
Point int32 `json:"point"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Query
|
|
||||||
// @Summary Query submissions
|
|
||||||
// @Description Query submissions
|
|
||||||
// @Tags submission
|
|
||||||
// @Accept application/x-www-form-urlencoded
|
|
||||||
// @Produce json
|
|
||||||
// @Param pid formData uint false "problem id"
|
|
||||||
// @Param uid formData uint false "user id"
|
|
||||||
// @Param offset formData int false "start position"
|
|
||||||
// @Param limit formData int true "limit number of records"
|
|
||||||
// @Response 200 {object} e.Response "queryResponse"
|
|
||||||
// @Router /v1/submission/query [post]
|
|
||||||
func (h *handler) Query(c *gin.Context) {
|
|
||||||
req := new(queryRequest)
|
|
||||||
|
|
||||||
if err := c.ShouldBind(req); err != nil {
|
|
||||||
e.Pong(c, e.InvalidParameter, err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if req.Pid == 0 && req.Uid == 0 {
|
|
||||||
e.Pong(c, e.InvalidParameter, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
submissions, status := h.submissionService.Query(req.Pid, req.Uid, req.Offset, req.Limit)
|
|
||||||
|
|
||||||
var response []*queryResponse
|
|
||||||
|
|
||||||
for _, submission := range submissions {
|
|
||||||
currentStatus, _ := h.statusService.Query(submission.ID, false)
|
|
||||||
var currentPoint int32
|
|
||||||
|
|
||||||
if currentStatus == nil {
|
|
||||||
currentPoint = -1
|
|
||||||
} else {
|
|
||||||
currentPoint = currentStatus.Point
|
|
||||||
}
|
|
||||||
|
|
||||||
newResponse := &queryResponse{
|
|
||||||
Submission: *submission,
|
|
||||||
Point: currentPoint,
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: only show code when user is admin or the code is submitted by the user
|
|
||||||
newResponse.Submission.Code = ""
|
|
||||||
|
|
||||||
response = append(response, newResponse)
|
|
||||||
}
|
|
||||||
|
|
||||||
e.Pong(c, status, response)
|
|
||||||
}
|
|
@ -11,53 +11,55 @@ type rejudgeRequest struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Rejudge
|
// Rejudge
|
||||||
// @Summary rejudge a submission
|
// @Summary [admin] rejudge a specific submission
|
||||||
// @Description rejudge a submission
|
// @Description rejudge a specific submission
|
||||||
// @Tags submission
|
// @Tags submission,admin
|
||||||
// @Accept application/x-www-form-urlencoded
|
// @Accept application/x-www-form-urlencoded
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param sid formData int true "submission id"
|
// @Param sid formData int true "submission id"
|
||||||
// @Response 200 {object} e.Response ""
|
// @Response 200 {object} e.Response[any] "nothing"
|
||||||
// @Security Authentication
|
// @Security Authentication
|
||||||
// @Router /v1/submission/rejudge [post]
|
// @Router /v1/submission/rejudge [post]
|
||||||
func (h *handler) Rejudge(c *gin.Context) {
|
func (h *handler) Rejudge(c *gin.Context) {
|
||||||
claim, exist := c.Get("claim")
|
claim, exist := c.Get("claim")
|
||||||
if !exist {
|
if !exist {
|
||||||
e.Pong(c, e.UserUnauthenticated, nil)
|
e.Pong[any](c, e.UserUnauthenticated, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
role := claim.(*model.Claim).Role
|
|
||||||
req := new(rejudgeRequest)
|
req := new(rejudgeRequest)
|
||||||
|
|
||||||
if err := c.ShouldBind(req); err != nil {
|
if err := c.ShouldBind(req); err != nil {
|
||||||
e.Pong(c, e.InvalidParameter, err.Error())
|
e.Pong(c, e.InvalidParameter, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// only admin can rejudge
|
// only admin can rejudge
|
||||||
|
role := claim.(*model.Claim).Role
|
||||||
if role < model.RoleAdmin {
|
if role < model.RoleAdmin {
|
||||||
e.Pong(c, e.UserUnauthorized, nil)
|
e.Pong[any](c, e.UserUnauthorized, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// query submission
|
||||||
s, status := h.submissionService.QueryBySid(req.Sid, false)
|
s, status := h.submissionService.QueryBySid(req.Sid, false)
|
||||||
if status != e.Success {
|
if status != e.Success {
|
||||||
e.Pong(c, status, nil)
|
e.Pong[any](c, status, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// query latest problem version
|
||||||
pv, status := h.problemService.QueryLatestVersion(s.ProblemID)
|
pv, status := h.problemService.QueryLatestVersion(s.ProblemID)
|
||||||
if status != e.Success {
|
if status != e.Success {
|
||||||
e.Pong(c, status, nil)
|
e.Pong[any](c, status, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// submit judge
|
||||||
_, status = h.taskService.SubmitJudge(&model.SubmitJudgePayload{
|
_, status = h.taskService.SubmitJudge(&model.SubmitJudgePayload{
|
||||||
ProblemVersionID: pv.ID,
|
ProblemVersionID: pv.ID,
|
||||||
StorageKey: pv.StorageKey,
|
StorageKey: pv.StorageKey,
|
||||||
Submission: *s,
|
Submission: *s,
|
||||||
})
|
})
|
||||||
|
|
||||||
e.Pong(c, status, nil)
|
e.Pong[any](c, status, nil)
|
||||||
}
|
}
|
||||||
|
@ -22,16 +22,16 @@ type createRequest struct {
|
|||||||
// @Param username formData string true "username"
|
// @Param username formData string true "username"
|
||||||
// @Param nickname formData string true "nickname"
|
// @Param nickname formData string true "nickname"
|
||||||
// @Param password formData string true "password"
|
// @Param password formData string true "password"
|
||||||
// @Response 200 {object} e.Response "jwt token"
|
// @Response 200 {object} e.Response[string] "jwt token"
|
||||||
// @Router /v1/user/create [post]
|
// @Router /v1/user/create [post]
|
||||||
func (h *handler) Create(c *gin.Context) {
|
func (h *handler) Create(c *gin.Context) {
|
||||||
req := new(createRequest)
|
req := new(createRequest)
|
||||||
|
|
||||||
if err := c.ShouldBind(req); err != nil {
|
if err := c.ShouldBind(req); err != nil {
|
||||||
e.Pong(c, e.InvalidParameter, err.Error())
|
e.Pong(c, e.InvalidParameter, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create user
|
||||||
createData := &user.CreateData{
|
createData := &user.CreateData{
|
||||||
UserName: req.UserName,
|
UserName: req.UserName,
|
||||||
Password: req.Password,
|
Password: req.Password,
|
||||||
@ -39,16 +39,18 @@ func (h *handler) Create(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
u, status := h.userService.Create(createData)
|
u, status := h.userService.Create(createData)
|
||||||
if status != e.Success {
|
if status != e.Success {
|
||||||
e.Pong(c, status, nil)
|
e.Pong[any](c, status, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update version in cache
|
||||||
version, status := h.userService.IncrVersion(u.ID)
|
version, status := h.userService.IncrVersion(u.ID)
|
||||||
if status != e.Success {
|
if status != e.Success {
|
||||||
e.Pong(c, status, nil)
|
e.Pong[any](c, status, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sign jwt token
|
||||||
claim := &model.Claim{
|
claim := &model.Claim{
|
||||||
UID: u.ID,
|
UID: u.ID,
|
||||||
Role: u.Role,
|
Role: u.Role,
|
||||||
|
@ -12,6 +12,11 @@ type loginRequest struct {
|
|||||||
Password string `form:"password" binding:"required"`
|
Password string `form:"password" binding:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type loginResponse struct {
|
||||||
|
Token string `json:"token"`
|
||||||
|
NickName string `json:"nickname"`
|
||||||
|
}
|
||||||
|
|
||||||
// Login
|
// Login
|
||||||
// @Summary login
|
// @Summary login
|
||||||
// @Description login and return token
|
// @Description login and return token
|
||||||
@ -20,13 +25,12 @@ type loginRequest struct {
|
|||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param username formData string true "username"
|
// @Param username formData string true "username"
|
||||||
// @Param password formData string true "password"
|
// @Param password formData string true "password"
|
||||||
// @Response 200 {object} e.Response "jwt token and user nickname"
|
// @Response 200 {object} e.Response[loginResponse] "jwt token and user's nickname"
|
||||||
// @Router /v1/user/login [post]
|
// @Router /v1/user/login [post]
|
||||||
func (h *handler) Login(c *gin.Context) {
|
func (h *handler) Login(c *gin.Context) {
|
||||||
req := new(loginRequest)
|
req := new(loginRequest)
|
||||||
|
|
||||||
if err := c.ShouldBind(req); err != nil {
|
if err := c.ShouldBind(req); err != nil {
|
||||||
e.Pong(c, e.InvalidParameter, nil)
|
e.Pong(c, e.InvalidParameter, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,14 +41,14 @@ func (h *handler) Login(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
u, status := h.userService.Login(loginData)
|
u, status := h.userService.Login(loginData)
|
||||||
if status != e.Success {
|
if status != e.Success {
|
||||||
e.Pong(c, status, nil)
|
e.Pong[any](c, status, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// sign and return token
|
// sign and return token
|
||||||
version, status := h.userService.IncrVersion(u.ID)
|
version, status := h.userService.IncrVersion(u.ID)
|
||||||
if status != e.Success {
|
if status != e.Success {
|
||||||
e.Pong(c, status, nil)
|
e.Pong[any](c, status, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
claim := &model.Claim{
|
claim := &model.Claim{
|
||||||
@ -53,8 +57,5 @@ func (h *handler) Login(c *gin.Context) {
|
|||||||
Version: version,
|
Version: version,
|
||||||
}
|
}
|
||||||
token, status := h.jwtService.SignClaim(claim)
|
token, status := h.jwtService.SignClaim(claim)
|
||||||
e.Pong(c, status, gin.H{
|
e.Pong(c, status, loginResponse{Token: token, NickName: u.NickName})
|
||||||
"token": token,
|
|
||||||
"nickname": u.NickName,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
@ -12,16 +12,16 @@ import (
|
|||||||
// @Tags user
|
// @Tags user
|
||||||
// @Accept application/x-www-form-urlencoded
|
// @Accept application/x-www-form-urlencoded
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Response 200 {object} e.Response "nil"
|
// @Response 200 {object} e.Response[any] "nothing"
|
||||||
// @Security Authentication
|
// @Security Authentication
|
||||||
// @Router /v1/user/logout [post]
|
// @Router /v1/user/logout [post]
|
||||||
func (h *handler) Logout(c *gin.Context) {
|
func (h *handler) Logout(c *gin.Context) {
|
||||||
claim, exist := c.Get("claim")
|
claim, exist := c.Get("claim")
|
||||||
if !exist {
|
if !exist {
|
||||||
e.Pong(c, e.UserUnauthenticated, nil)
|
e.Pong[any](c, e.UserUnauthenticated, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, status := h.userService.IncrVersion(claim.(*model.Claim).UID)
|
_, status := h.userService.IncrVersion(claim.(*model.Claim).UID)
|
||||||
e.Pong(c, status, nil)
|
e.Pong[any](c, status, nil)
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package user
|
|||||||
import (
|
import (
|
||||||
"git.0x7f.app/WOJ/woj-server/internal/e"
|
"git.0x7f.app/WOJ/woj-server/internal/e"
|
||||||
"git.0x7f.app/WOJ/woj-server/internal/model"
|
"git.0x7f.app/WOJ/woj-server/internal/model"
|
||||||
|
"git.0x7f.app/WOJ/woj-server/pkg/utils"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -17,15 +18,13 @@ type profileRequest struct {
|
|||||||
// @Accept application/x-www-form-urlencoded
|
// @Accept application/x-www-form-urlencoded
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param uid formData int false "user id"
|
// @Param uid formData int false "user id"
|
||||||
// @Response 200 {object} e.Response "user info"
|
// @Response 200 {object} e.Response[model.User] "user info"
|
||||||
// @Security Authentication
|
// @Security Authentication
|
||||||
// @Router /v1/user/profile [post]
|
// @Router /v1/user/profile [post]
|
||||||
func (h *handler) Profile(c *gin.Context) {
|
func (h *handler) Profile(c *gin.Context) {
|
||||||
// TODO: create a new struct for profile (user info & solve info)
|
|
||||||
|
|
||||||
claim, exist := c.Get("claim")
|
claim, exist := c.Get("claim")
|
||||||
if !exist {
|
if !exist {
|
||||||
e.Pong(c, e.UserUnauthenticated, nil)
|
e.Pong[any](c, e.UserUnauthenticated, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,22 +32,21 @@ func (h *handler) Profile(c *gin.Context) {
|
|||||||
role := claim.(*model.Claim).Role
|
role := claim.(*model.Claim).Role
|
||||||
|
|
||||||
req := new(profileRequest)
|
req := new(profileRequest)
|
||||||
|
|
||||||
if err := c.ShouldBind(req); err != nil {
|
if err := c.ShouldBind(req); err != nil {
|
||||||
e.Pong(c, e.InvalidParameter, nil)
|
e.Pong(c, e.InvalidParameter, err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.UID == 0 {
|
user, status := h.userService.Profile(utils.If(req.UID == 0, uid, req.UID))
|
||||||
req.UID = uid
|
if status != e.Success {
|
||||||
} else if req.UID != uid && role < model.RoleGeneral {
|
e.Pong[any](c, status, nil)
|
||||||
e.Pong(c, e.UserUnauthorized, nil)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user, status := h.userService.Profile(req.UID)
|
if role < model.RoleAdmin && user.ID != uid {
|
||||||
|
e.Pong[any](c, e.UserUnauthorized, nil)
|
||||||
// TODO: >= admin can see is_enable
|
return
|
||||||
|
}
|
||||||
|
|
||||||
e.Pong(c, status, user)
|
e.Pong(c, status, user)
|
||||||
}
|
}
|
||||||
|
@ -6,30 +6,21 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Response struct {
|
type Response[T any] struct {
|
||||||
Code int `json:"code"`
|
Code int `json:"code"`
|
||||||
Msg string `json:"msg"`
|
Msg string `json:"msg"`
|
||||||
Body interface{} `json:"body"`
|
Body T `json:"body"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func Wrap(status Status, body interface{}) interface{} {
|
func wrap[T any](status Status, body T) Response[interface{}] {
|
||||||
return Response{
|
return Response[interface{}]{
|
||||||
Code: int(status),
|
Code: int(status),
|
||||||
Msg: status.String(),
|
Msg: status.String(),
|
||||||
Body: utils.If(status == Success, body, nil),
|
Body: utils.If[interface{}](status == Success, body, nil),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Pong(c *gin.Context, status Status, body interface{}) {
|
func Pong[T any](c *gin.Context, status Status, body T) {
|
||||||
c.Set("err", status)
|
c.Set("err", status)
|
||||||
c.JSON(http.StatusOK, Wrap(status, body))
|
c.JSON(http.StatusOK, wrap(status, body))
|
||||||
}
|
|
||||||
|
|
||||||
type Endpoint func(*gin.Context) (Status, interface{})
|
|
||||||
|
|
||||||
func PongWrapper(handler Endpoint) func(*gin.Context) {
|
|
||||||
return func(c *gin.Context) {
|
|
||||||
status, body := handler(c)
|
|
||||||
Pong(c, status, body)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ func (s *service) Handler(forced bool) gin.HandlerFunc {
|
|||||||
c.Set("claim", claim)
|
c.Set("claim", claim)
|
||||||
}
|
}
|
||||||
if forced && status != e.Success {
|
if forced && status != e.Success {
|
||||||
e.Pong(c, status, nil)
|
e.Pong[any](c, status, nil)
|
||||||
c.Abort()
|
c.Abort()
|
||||||
} else {
|
} else {
|
||||||
c.Next()
|
c.Next()
|
||||||
|
@ -11,8 +11,8 @@ import (
|
|||||||
"github.com/samber/do"
|
"github.com/samber/do"
|
||||||
)
|
)
|
||||||
|
|
||||||
// @title OJ Server API Documentation
|
// @title WOJ Server API Documentation
|
||||||
// @version 1.0
|
// @version 1.1.0
|
||||||
// @BasePath /api
|
// @BasePath /api
|
||||||
// @securityDefinitions.apikey Authentication
|
// @securityDefinitions.apikey Authentication
|
||||||
// @in header
|
// @in header
|
||||||
|
Loading…
Reference in New Issue
Block a user