From ef1577a212d1b5c6f908a59b943a512d33d312fe Mon Sep 17 00:00:00 2001 From: nsfisis Date: Thu, 8 Aug 2024 20:00:06 +0900 Subject: feat(backend/worker): enable `revive` in `golangci-lint` --- backend/admin/handler.go | 265 +++++++++++++++++++++++++++++++++++++++++++++ backend/admin/handlers.go | 268 ---------------------------------------------- backend/admin/renderer.go | 2 +- 3 files changed, 266 insertions(+), 269 deletions(-) create mode 100644 backend/admin/handler.go delete mode 100644 backend/admin/handlers.go (limited to 'backend/admin') diff --git a/backend/admin/handler.go b/backend/admin/handler.go new file mode 100644 index 0000000..111476b --- /dev/null +++ b/backend/admin/handler.go @@ -0,0 +1,265 @@ +package admin + +import ( + "errors" + "net/http" + "strconv" + "time" + + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgtype" + "github.com/labstack/echo/v4" + + "github.com/nsfisis/iosdc-japan-2024-albatross/backend/auth" + "github.com/nsfisis/iosdc-japan-2024-albatross/backend/db" +) + +var jst = time.FixedZone("Asia/Tokyo", 9*60*60) + +type Handler struct { + q *db.Queries + hubs GameHubsInterface +} + +type GameHubsInterface interface { + StartGame(gameID int) error +} + +func NewHandler(q *db.Queries, hubs GameHubsInterface) *Handler { + return &Handler{ + q: q, + hubs: hubs, + } +} + +func newAdminMiddleware() echo.MiddlewareFunc { + return func(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + jwt, err := c.Cookie("albatross_token") + if err != nil { + return c.Redirect(http.StatusSeeOther, "/login") + } + claims, err := auth.ParseJWT(jwt.Value) + if err != nil { + return c.Redirect(http.StatusSeeOther, "/login") + } + if !claims.IsAdmin { + return echo.NewHTTPError(http.StatusForbidden) + } + return next(c) + } + } +} + +func (h *Handler) RegisterHandlers(g *echo.Group) { + g.Use(newAssetsMiddleware()) + g.Use(newAdminMiddleware()) + + g.GET("/dashboard", h.getDashboard) + g.GET("/users", h.getUsers) + g.GET("/users/:userID", h.getUserEdit) + g.GET("/games", h.getGames) + g.GET("/games/:gameID", h.getGameEdit) + g.POST("/games/:gameID", h.postGameEdit) +} + +func (h *Handler) getDashboard(c echo.Context) error { + return c.Render(http.StatusOK, "dashboard", echo.Map{ + "Title": "Dashboard", + }) +} + +func (h *Handler) getUsers(c echo.Context) error { + rows, err := h.q.ListUsers(c.Request().Context()) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + users := make([]echo.Map, len(rows)) + for i, u := range rows { + users[i] = echo.Map{ + "UserID": u.UserID, + "Username": u.Username, + "DisplayName": u.DisplayName, + "IconPath": u.IconPath, + "IsAdmin": u.IsAdmin, + } + } + + return c.Render(http.StatusOK, "users", echo.Map{ + "Title": "Users", + "Users": users, + }) +} + +func (h *Handler) getUserEdit(c echo.Context) error { + userID, err := strconv.Atoi(c.Param("userID")) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, "Invalid user id") + } + row, err := h.q.GetUserByID(c.Request().Context(), int32(userID)) + if err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return echo.NewHTTPError(http.StatusNotFound) + } + return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + + return c.Render(http.StatusOK, "user_edit", echo.Map{ + "Title": "User Edit", + "User": echo.Map{ + "UserID": row.UserID, + "Username": row.Username, + "DisplayName": row.DisplayName, + "IconPath": row.IconPath, + "IsAdmin": row.IsAdmin, + }, + }) +} + +func (h *Handler) getGames(c echo.Context) error { + rows, err := h.q.ListGames(c.Request().Context()) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + games := make([]echo.Map, len(rows)) + for i, g := range rows { + var startedAt string + if !g.StartedAt.Valid { + startedAt = g.StartedAt.Time.In(jst).Format("2006-01-02T15:04") + } + games[i] = echo.Map{ + "GameID": g.GameID, + "GameType": g.GameType, + "State": g.State, + "DisplayName": g.DisplayName, + "DurationSeconds": g.DurationSeconds, + "StartedAt": startedAt, + "ProblemID": g.ProblemID, + } + } + + return c.Render(http.StatusOK, "games", echo.Map{ + "Title": "Games", + "Games": games, + }) +} + +func (h *Handler) getGameEdit(c echo.Context) error { + gameID, err := strconv.Atoi(c.Param("gameID")) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, "Invalid game id") + } + row, err := h.q.GetGameByID(c.Request().Context(), int32(gameID)) + if err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return echo.NewHTTPError(http.StatusNotFound) + } + return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + + var startedAt string + if !row.StartedAt.Valid { + startedAt = row.StartedAt.Time.In(jst).Format("2006-01-02T15:04") + } + + return c.Render(http.StatusOK, "game_edit", echo.Map{ + "Title": "Game Edit", + "Game": echo.Map{ + "GameID": row.GameID, + "GameType": row.GameType, + "State": row.State, + "DisplayName": row.DisplayName, + "DurationSeconds": row.DurationSeconds, + "StartedAt": startedAt, + "ProblemID": row.ProblemID, + }, + }) +} + +func (h *Handler) postGameEdit(c echo.Context) error { + gameID, err := strconv.Atoi(c.Param("gameID")) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, "Invalid game id") + } + row, err := h.q.GetGameByID(c.Request().Context(), int32(gameID)) + if err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return echo.NewHTTPError(http.StatusNotFound) + } + return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + + gameType := c.FormValue("game_type") + state := c.FormValue("state") + displayName := c.FormValue("display_name") + durationSeconds, err := strconv.Atoi(c.FormValue("duration_seconds")) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, "Invalid duration_seconds") + } + var problemID *int + { + problemIDRaw := c.FormValue("problem_id") + if problemIDRaw != "" { + problemIDInt, err := strconv.Atoi(problemIDRaw) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, "Invalid problem_id") + } + problemID = &problemIDInt + } + } + var startedAt *time.Time + { + startedAtRaw := c.FormValue("started_at") + if startedAtRaw != "" { + startedAtTime, err := time.Parse("2006-01-02T15:04", startedAtRaw) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, "Invalid started_at") + } + startedAt = &startedAtTime + } + } + + var changedStartedAt pgtype.Timestamp + if startedAt == nil { + changedStartedAt = pgtype.Timestamp{ + Valid: false, + } + } else { + changedStartedAt = pgtype.Timestamp{ + Time: *startedAt, + Valid: true, + } + } + var changedProblemID *int32 + if problemID == nil { + changedProblemID = nil + } else { + changedProblemID = new(int32) + *changedProblemID = int32(*problemID) + } + + { + // TODO: + if state != row.State && state == "prepare" { + err := h.hubs.StartGame(int(gameID)) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + } + } + + err = h.q.UpdateGame(c.Request().Context(), db.UpdateGameParams{ + GameID: int32(gameID), + GameType: gameType, + State: state, + DisplayName: displayName, + DurationSeconds: int32(durationSeconds), + StartedAt: changedStartedAt, + ProblemID: changedProblemID, + }) + if err != nil { + return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) + } + + return c.NoContent(http.StatusNoContent) +} diff --git a/backend/admin/handlers.go b/backend/admin/handlers.go deleted file mode 100644 index 2a678d3..0000000 --- a/backend/admin/handlers.go +++ /dev/null @@ -1,268 +0,0 @@ -package admin - -import ( - "errors" - "net/http" - "strconv" - "time" - - "github.com/jackc/pgx/v5" - "github.com/jackc/pgx/v5/pgtype" - "github.com/labstack/echo/v4" - - "github.com/nsfisis/iosdc-japan-2024-albatross/backend/auth" - "github.com/nsfisis/iosdc-japan-2024-albatross/backend/db" -) - -var jst = time.FixedZone("Asia/Tokyo", 9*60*60) - -type AdminHandler struct { - q *db.Queries - hubs GameHubsInterface -} - -type GameHubsInterface interface { - StartGame(gameID int) error -} - -func NewAdminHandler(q *db.Queries, hubs GameHubsInterface) *AdminHandler { - return &AdminHandler{ - q: q, - hubs: hubs, - } -} - -func newAdminMiddleware() echo.MiddlewareFunc { - return func(next echo.HandlerFunc) echo.HandlerFunc { - return func(c echo.Context) error { - jwt, err := c.Cookie("albatross_token") - if err != nil { - return c.Redirect(http.StatusSeeOther, "/login") - } - claims, err := auth.ParseJWT(jwt.Value) - if err != nil { - return c.Redirect(http.StatusSeeOther, "/login") - } - if !claims.IsAdmin { - return echo.NewHTTPError(http.StatusForbidden) - } - return next(c) - } - } -} - -func (h *AdminHandler) RegisterHandlers(g *echo.Group) { - g.Use(newAssetsMiddleware()) - g.Use(newAdminMiddleware()) - - g.GET("/dashboard", h.getDashboard) - g.GET("/users", h.getUsers) - g.GET("/users/:userID", h.getUserEdit) - g.GET("/games", h.getGames) - g.GET("/games/:gameID", h.getGameEdit) - g.POST("/games/:gameID", h.postGameEdit) -} - -func (h *AdminHandler) getDashboard(c echo.Context) error { - return c.Render(http.StatusOK, "dashboard", echo.Map{ - "Title": "Dashboard", - }) -} - -func (h *AdminHandler) getUsers(c echo.Context) error { - rows, err := h.q.ListUsers(c.Request().Context()) - if err != nil { - return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) - } - users := make([]echo.Map, len(rows)) - for i, u := range rows { - users[i] = echo.Map{ - "UserID": u.UserID, - "Username": u.Username, - "DisplayName": u.DisplayName, - "IconPath": u.IconPath, - "IsAdmin": u.IsAdmin, - } - } - - return c.Render(http.StatusOK, "users", echo.Map{ - "Title": "Users", - "Users": users, - }) -} - -func (h *AdminHandler) getUserEdit(c echo.Context) error { - userID, err := strconv.Atoi(c.Param("userID")) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, "Invalid user id") - } - row, err := h.q.GetUserByID(c.Request().Context(), int32(userID)) - if err != nil { - if errors.Is(err, pgx.ErrNoRows) { - return echo.NewHTTPError(http.StatusNotFound) - } else { - return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) - } - } - - return c.Render(http.StatusOK, "user_edit", echo.Map{ - "Title": "User Edit", - "User": echo.Map{ - "UserID": row.UserID, - "Username": row.Username, - "DisplayName": row.DisplayName, - "IconPath": row.IconPath, - "IsAdmin": row.IsAdmin, - }, - }) -} - -func (h *AdminHandler) getGames(c echo.Context) error { - rows, err := h.q.ListGames(c.Request().Context()) - if err != nil { - return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) - } - games := make([]echo.Map, len(rows)) - for i, g := range rows { - var startedAt string - if !g.StartedAt.Valid { - startedAt = g.StartedAt.Time.In(jst).Format("2006-01-02T15:04") - } - games[i] = echo.Map{ - "GameID": g.GameID, - "GameType": g.GameType, - "State": g.State, - "DisplayName": g.DisplayName, - "DurationSeconds": g.DurationSeconds, - "StartedAt": startedAt, - "ProblemID": g.ProblemID, - } - } - - return c.Render(http.StatusOK, "games", echo.Map{ - "Title": "Games", - "Games": games, - }) -} - -func (h *AdminHandler) getGameEdit(c echo.Context) error { - gameID, err := strconv.Atoi(c.Param("gameID")) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, "Invalid game id") - } - row, err := h.q.GetGameByID(c.Request().Context(), int32(gameID)) - if err != nil { - if errors.Is(err, pgx.ErrNoRows) { - return echo.NewHTTPError(http.StatusNotFound) - } else { - return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) - } - } - - var startedAt string - if !row.StartedAt.Valid { - startedAt = row.StartedAt.Time.In(jst).Format("2006-01-02T15:04") - } - - return c.Render(http.StatusOK, "game_edit", echo.Map{ - "Title": "Game Edit", - "Game": echo.Map{ - "GameID": row.GameID, - "GameType": row.GameType, - "State": row.State, - "DisplayName": row.DisplayName, - "DurationSeconds": row.DurationSeconds, - "StartedAt": startedAt, - "ProblemID": row.ProblemID, - }, - }) -} - -func (h *AdminHandler) postGameEdit(c echo.Context) error { - gameID, err := strconv.Atoi(c.Param("gameID")) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, "Invalid game id") - } - row, err := h.q.GetGameByID(c.Request().Context(), int32(gameID)) - if err != nil { - if errors.Is(err, pgx.ErrNoRows) { - return echo.NewHTTPError(http.StatusNotFound) - } else { - return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) - } - } - - gameType := c.FormValue("game_type") - state := c.FormValue("state") - displayName := c.FormValue("display_name") - durationSeconds, err := strconv.Atoi(c.FormValue("duration_seconds")) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, "Invalid duration_seconds") - } - var problemID *int - { - problemIDRaw := c.FormValue("problem_id") - if problemIDRaw != "" { - problemIDInt, err := strconv.Atoi(problemIDRaw) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, "Invalid problem_id") - } - problemID = &problemIDInt - } - } - var startedAt *time.Time - { - startedAtRaw := c.FormValue("started_at") - if startedAtRaw != "" { - startedAtTime, err := time.Parse("2006-01-02T15:04", startedAtRaw) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, "Invalid started_at") - } - startedAt = &startedAtTime - } - } - - var changedStartedAt pgtype.Timestamp - if startedAt == nil { - changedStartedAt = pgtype.Timestamp{ - Valid: false, - } - } else { - changedStartedAt = pgtype.Timestamp{ - Time: *startedAt, - Valid: true, - } - } - var changedProblemID *int32 - if problemID == nil { - changedProblemID = nil - } else { - changedProblemID = new(int32) - *changedProblemID = int32(*problemID) - } - - { - // TODO: - if state != row.State && state == "prepare" { - err := h.hubs.StartGame(int(gameID)) - if err != nil { - return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) - } - } - } - - err = h.q.UpdateGame(c.Request().Context(), db.UpdateGameParams{ - GameID: int32(gameID), - GameType: gameType, - State: state, - DisplayName: displayName, - DurationSeconds: int32(durationSeconds), - StartedAt: changedStartedAt, - ProblemID: changedProblemID, - }) - if err != nil { - return echo.NewHTTPError(http.StatusInternalServerError, err.Error()) - } - - return c.NoContent(http.StatusNoContent) -} diff --git a/backend/admin/renderer.go b/backend/admin/renderer.go index 468677f..d38c701 100644 --- a/backend/admin/renderer.go +++ b/backend/admin/renderer.go @@ -27,7 +27,7 @@ func NewRenderer() *Renderer { } } -func (r *Renderer) Render(w io.Writer, name string, data interface{}, c echo.Context) error { +func (r *Renderer) Render(w io.Writer, name string, data interface{}, _ echo.Context) error { tmpl, ok := r.templates[name] if !ok { t, err := template.ParseFS(templatesFS, "templates/base.html", "templates/"+name+".html") -- cgit v1.2.3-70-g09d2