aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2024-08-01 21:35:25 +0900
committernsfisis <nsfisis@gmail.com>2024-08-01 21:35:25 +0900
commitf6443042fbec1bd394439904f3c69e23709f7e6a (patch)
tree46872e86ed7e42962895e0bfe04064387487523f
parent10b9be2c2a46b204f83be7d152ca62bf69e8843e (diff)
parent94d5d89aa59b6d1e53dab280c26e3a8fcb22b7e4 (diff)
downloadiosdc-japan-2024-albatross-f6443042fbec1bd394439904f3c69e23709f7e6a.tar.gz
iosdc-japan-2024-albatross-f6443042fbec1bd394439904f3c69e23709f7e6a.tar.zst
iosdc-japan-2024-albatross-f6443042fbec1bd394439904f3c69e23709f7e6a.zip
Merge branch 'refactor/api'
-rw-r--r--Makefile18
-rw-r--r--backend/Makefile10
-rw-r--r--backend/api/generated.go504
-rw-r--r--backend/api/handler_wrapper.go156
-rw-r--r--backend/api/handlers.go246
-rw-r--r--backend/gen/api_handler_wrapper_gen.go168
-rw-r--r--backend/gen/gen.go5
-rw-r--r--backend/gen/oapi-codegen.yaml (renamed from backend/oapi-codegen.yaml)2
-rw-r--r--backend/gen/sqlc.yaml (renamed from backend/sqlc.yaml)6
-rw-r--r--backend/main.go4
-rw-r--r--frontend/Makefile3
-rw-r--r--frontend/app/.server/api/client.ts90
-rw-r--r--frontend/app/.server/api/schema.d.ts669
-rw-r--r--frontend/app/.server/auth.ts13
-rw-r--r--frontend/app/routes/admin.games.tsx15
-rw-r--r--frontend/app/routes/admin.games_.$gameId.tsx35
-rw-r--r--frontend/app/routes/admin.users.tsx15
-rw-r--r--frontend/app/routes/dashboard.tsx18
-rw-r--r--frontend/app/routes/golf.$gameId.play.tsx30
-rw-r--r--frontend/app/routes/golf.$gameId.watch.tsx30
-rw-r--r--frontend/package.json1
-rw-r--r--openapi.yaml277
22 files changed, 1184 insertions, 1131 deletions
diff --git a/Makefile b/Makefile
index 7adf808..2361ac5 100644
--- a/Makefile
+++ b/Makefile
@@ -52,17 +52,7 @@ initdb:
make psql-query < ./backend/schema.sql
make psql-query < ./backend/fixtures/dev.sql
-.PHONY: openapi
-openapi: oapi-codegen openapi-typescript
-
-.PHONY: oapi-codegen
-oapi-codegen:
- cd backend; make oapi-codegen
-
-.PHONY: openapi-typescript
-openapi-typescript:
- cd frontend; make openapi-typescript
-
-.PHONY: sqlc
-sqlc:
- cd backend; make sqlc
+.PHONY: gen
+gen:
+ cd backend; make gen
+ cd frontend; npm run openapi-typescript
diff --git a/backend/Makefile b/backend/Makefile
index 3b26e00..72d3314 100644
--- a/backend/Makefile
+++ b/backend/Makefile
@@ -10,10 +10,6 @@ check:
lint:
go vet ./...
-.PHONY: oapi-codegen
-oapi-codegen:
- go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen -config oapi-codegen.yaml ../openapi.yaml
-
-.PHONY: sqlc
-sqlc:
- go run github.com/sqlc-dev/sqlc/cmd/sqlc generate
+.PHONY: gen
+gen:
+ go generate ./...
diff --git a/backend/api/generated.go b/backend/api/generated.go
index a2fa329..a643833 100644
--- a/backend/api/generated.go
+++ b/backend/api/generated.go
@@ -43,17 +43,22 @@ const (
GameWatcherMessageS2CExecResultPayloadStatusSuccess GameWatcherMessageS2CExecResultPayloadStatus = "success"
)
-// Defines values for PutAdminGamesGameIdJSONBodyState.
+// Defines values for AdminPutGameJSONBodyState.
const (
- PutAdminGamesGameIdJSONBodyStateClosed PutAdminGamesGameIdJSONBodyState = "closed"
- PutAdminGamesGameIdJSONBodyStateFinished PutAdminGamesGameIdJSONBodyState = "finished"
- PutAdminGamesGameIdJSONBodyStateGaming PutAdminGamesGameIdJSONBodyState = "gaming"
- PutAdminGamesGameIdJSONBodyStatePrepare PutAdminGamesGameIdJSONBodyState = "prepare"
- PutAdminGamesGameIdJSONBodyStateStarting PutAdminGamesGameIdJSONBodyState = "starting"
- PutAdminGamesGameIdJSONBodyStateWaitingEntries PutAdminGamesGameIdJSONBodyState = "waiting_entries"
- PutAdminGamesGameIdJSONBodyStateWaitingStart PutAdminGamesGameIdJSONBodyState = "waiting_start"
+ AdminPutGameJSONBodyStateClosed AdminPutGameJSONBodyState = "closed"
+ AdminPutGameJSONBodyStateFinished AdminPutGameJSONBodyState = "finished"
+ AdminPutGameJSONBodyStateGaming AdminPutGameJSONBodyState = "gaming"
+ AdminPutGameJSONBodyStatePrepare AdminPutGameJSONBodyState = "prepare"
+ AdminPutGameJSONBodyStateStarting AdminPutGameJSONBodyState = "starting"
+ AdminPutGameJSONBodyStateWaitingEntries AdminPutGameJSONBodyState = "waiting_entries"
+ AdminPutGameJSONBodyStateWaitingStart AdminPutGameJSONBodyState = "waiting_start"
)
+// Error defines model for Error.
+type Error struct {
+ Message string `json:"message"`
+}
+
// Game defines model for Game.
type Game struct {
DisplayName string `json:"display_name"`
@@ -207,47 +212,64 @@ type User struct {
Username string `json:"username"`
}
-// GetAdminGamesParams defines parameters for GetAdminGames.
-type GetAdminGamesParams struct {
- Authorization string `json:"Authorization"`
+// HeaderAuthorization defines model for header_authorization.
+type HeaderAuthorization = string
+
+// PathGameId defines model for path_game_id.
+type PathGameId = int
+
+// BadRequest defines model for BadRequest.
+type BadRequest = Error
+
+// Forbidden defines model for Forbidden.
+type Forbidden = Error
+
+// NotFound defines model for NotFound.
+type NotFound = Error
+
+// Unauthorized defines model for Unauthorized.
+type Unauthorized = Error
+
+// AdminGetGamesParams defines parameters for AdminGetGames.
+type AdminGetGamesParams struct {
+ Authorization HeaderAuthorization `json:"Authorization"`
}
-// GetAdminGamesGameIdParams defines parameters for GetAdminGamesGameId.
-type GetAdminGamesGameIdParams struct {
- Authorization string `json:"Authorization"`
+// AdminGetGameParams defines parameters for AdminGetGame.
+type AdminGetGameParams struct {
+ Authorization HeaderAuthorization `json:"Authorization"`
}
-// PutAdminGamesGameIdJSONBody defines parameters for PutAdminGamesGameId.
-type PutAdminGamesGameIdJSONBody struct {
- DisplayName *string `json:"display_name,omitempty"`
- DurationSeconds *int `json:"duration_seconds,omitempty"`
- ProblemId nullable.Nullable[int] `json:"problem_id,omitempty"`
- StartedAt nullable.Nullable[int] `json:"started_at,omitempty"`
- State *PutAdminGamesGameIdJSONBodyState `json:"state,omitempty"`
+// AdminPutGameJSONBody defines parameters for AdminPutGame.
+type AdminPutGameJSONBody struct {
+ DisplayName *string `json:"display_name,omitempty"`
+ DurationSeconds *int `json:"duration_seconds,omitempty"`
+ ProblemId nullable.Nullable[int] `json:"problem_id,omitempty"`
+ StartedAt nullable.Nullable[int] `json:"started_at,omitempty"`
+ State *AdminPutGameJSONBodyState `json:"state,omitempty"`
}
-// PutAdminGamesGameIdParams defines parameters for PutAdminGamesGameId.
-type PutAdminGamesGameIdParams struct {
- Authorization string `json:"Authorization"`
+// AdminPutGameParams defines parameters for AdminPutGame.
+type AdminPutGameParams struct {
+ Authorization HeaderAuthorization `json:"Authorization"`
}
-// PutAdminGamesGameIdJSONBodyState defines parameters for PutAdminGamesGameId.
-type PutAdminGamesGameIdJSONBodyState string
+// AdminPutGameJSONBodyState defines parameters for AdminPutGame.
+type AdminPutGameJSONBodyState string
-// GetAdminUsersParams defines parameters for GetAdminUsers.
-type GetAdminUsersParams struct {
- Authorization string `json:"Authorization"`
+// AdminGetUsersParams defines parameters for AdminGetUsers.
+type AdminGetUsersParams struct {
+ Authorization HeaderAuthorization `json:"Authorization"`
}
// GetGamesParams defines parameters for GetGames.
type GetGamesParams struct {
- PlayerId *int `form:"player_id,omitempty" json:"player_id,omitempty"`
- Authorization string `json:"Authorization"`
+ Authorization HeaderAuthorization `json:"Authorization"`
}
-// GetGamesGameIdParams defines parameters for GetGamesGameId.
-type GetGamesGameIdParams struct {
- Authorization string `json:"Authorization"`
+// GetGameParams defines parameters for GetGame.
+type GetGameParams struct {
+ Authorization HeaderAuthorization `json:"Authorization"`
}
// PostLoginJSONBody defines parameters for PostLogin.
@@ -258,11 +280,11 @@ type PostLoginJSONBody struct {
// GetTokenParams defines parameters for GetToken.
type GetTokenParams struct {
- Authorization string `json:"Authorization"`
+ Authorization HeaderAuthorization `json:"Authorization"`
}
-// PutAdminGamesGameIdJSONRequestBody defines body for PutAdminGamesGameId for application/json ContentType.
-type PutAdminGamesGameIdJSONRequestBody PutAdminGamesGameIdJSONBody
+// AdminPutGameJSONRequestBody defines body for AdminPutGame for application/json ContentType.
+type AdminPutGameJSONRequestBody AdminPutGameJSONBody
// PostLoginJSONRequestBody defines body for PostLogin for application/json ContentType.
type PostLoginJSONRequestBody PostLoginJSONBody
@@ -633,22 +655,22 @@ func (t *GameWatcherMessageS2C) UnmarshalJSON(b []byte) error {
type ServerInterface interface {
// List games
// (GET /admin/games)
- GetAdminGames(ctx echo.Context, params GetAdminGamesParams) error
+ AdminGetGames(ctx echo.Context, params AdminGetGamesParams) error
// Get a game
// (GET /admin/games/{game_id})
- GetAdminGamesGameId(ctx echo.Context, gameId int, params GetAdminGamesGameIdParams) error
+ AdminGetGame(ctx echo.Context, gameId PathGameId, params AdminGetGameParams) error
// Update a game
// (PUT /admin/games/{game_id})
- PutAdminGamesGameId(ctx echo.Context, gameId int, params PutAdminGamesGameIdParams) error
+ AdminPutGame(ctx echo.Context, gameId PathGameId, params AdminPutGameParams) error
// List all users
// (GET /admin/users)
- GetAdminUsers(ctx echo.Context, params GetAdminUsersParams) error
+ AdminGetUsers(ctx echo.Context, params AdminGetUsersParams) error
// List games
// (GET /games)
GetGames(ctx echo.Context, params GetGamesParams) error
// Get a game
// (GET /games/{game_id})
- GetGamesGameId(ctx echo.Context, gameId int, params GetGamesGameIdParams) error
+ GetGame(ctx echo.Context, gameId PathGameId, params GetGameParams) error
// User login
// (POST /login)
PostLogin(ctx echo.Context) error
@@ -662,17 +684,17 @@ type ServerInterfaceWrapper struct {
Handler ServerInterface
}
-// GetAdminGames converts echo context to params.
-func (w *ServerInterfaceWrapper) GetAdminGames(ctx echo.Context) error {
+// AdminGetGames converts echo context to params.
+func (w *ServerInterfaceWrapper) AdminGetGames(ctx echo.Context) error {
var err error
// Parameter object where we will unmarshal all parameters from the context
- var params GetAdminGamesParams
+ var params AdminGetGamesParams
headers := ctx.Request().Header
// ------------- Required header parameter "Authorization" -------------
if valueList, found := headers[http.CanonicalHeaderKey("Authorization")]; found {
- var Authorization string
+ var Authorization HeaderAuthorization
n := len(valueList)
if n != 1 {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for Authorization, got %d", n))
@@ -689,15 +711,15 @@ func (w *ServerInterfaceWrapper) GetAdminGames(ctx echo.Context) error {
}
// Invoke the callback with all the unmarshaled arguments
- err = w.Handler.GetAdminGames(ctx, params)
+ err = w.Handler.AdminGetGames(ctx, params)
return err
}
-// GetAdminGamesGameId converts echo context to params.
-func (w *ServerInterfaceWrapper) GetAdminGamesGameId(ctx echo.Context) error {
+// AdminGetGame converts echo context to params.
+func (w *ServerInterfaceWrapper) AdminGetGame(ctx echo.Context) error {
var err error
// ------------- Path parameter "game_id" -------------
- var gameId int
+ var gameId PathGameId
err = runtime.BindStyledParameterWithOptions("simple", "game_id", ctx.Param("game_id"), &gameId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true})
if err != nil {
@@ -705,12 +727,12 @@ func (w *ServerInterfaceWrapper) GetAdminGamesGameId(ctx echo.Context) error {
}
// Parameter object where we will unmarshal all parameters from the context
- var params GetAdminGamesGameIdParams
+ var params AdminGetGameParams
headers := ctx.Request().Header
// ------------- Required header parameter "Authorization" -------------
if valueList, found := headers[http.CanonicalHeaderKey("Authorization")]; found {
- var Authorization string
+ var Authorization HeaderAuthorization
n := len(valueList)
if n != 1 {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for Authorization, got %d", n))
@@ -727,15 +749,15 @@ func (w *ServerInterfaceWrapper) GetAdminGamesGameId(ctx echo.Context) error {
}
// Invoke the callback with all the unmarshaled arguments
- err = w.Handler.GetAdminGamesGameId(ctx, gameId, params)
+ err = w.Handler.AdminGetGame(ctx, gameId, params)
return err
}
-// PutAdminGamesGameId converts echo context to params.
-func (w *ServerInterfaceWrapper) PutAdminGamesGameId(ctx echo.Context) error {
+// AdminPutGame converts echo context to params.
+func (w *ServerInterfaceWrapper) AdminPutGame(ctx echo.Context) error {
var err error
// ------------- Path parameter "game_id" -------------
- var gameId int
+ var gameId PathGameId
err = runtime.BindStyledParameterWithOptions("simple", "game_id", ctx.Param("game_id"), &gameId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true})
if err != nil {
@@ -743,12 +765,12 @@ func (w *ServerInterfaceWrapper) PutAdminGamesGameId(ctx echo.Context) error {
}
// Parameter object where we will unmarshal all parameters from the context
- var params PutAdminGamesGameIdParams
+ var params AdminPutGameParams
headers := ctx.Request().Header
// ------------- Required header parameter "Authorization" -------------
if valueList, found := headers[http.CanonicalHeaderKey("Authorization")]; found {
- var Authorization string
+ var Authorization HeaderAuthorization
n := len(valueList)
if n != 1 {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for Authorization, got %d", n))
@@ -765,21 +787,21 @@ func (w *ServerInterfaceWrapper) PutAdminGamesGameId(ctx echo.Context) error {
}
// Invoke the callback with all the unmarshaled arguments
- err = w.Handler.PutAdminGamesGameId(ctx, gameId, params)
+ err = w.Handler.AdminPutGame(ctx, gameId, params)
return err
}
-// GetAdminUsers converts echo context to params.
-func (w *ServerInterfaceWrapper) GetAdminUsers(ctx echo.Context) error {
+// AdminGetUsers converts echo context to params.
+func (w *ServerInterfaceWrapper) AdminGetUsers(ctx echo.Context) error {
var err error
// Parameter object where we will unmarshal all parameters from the context
- var params GetAdminUsersParams
+ var params AdminGetUsersParams
headers := ctx.Request().Header
// ------------- Required header parameter "Authorization" -------------
if valueList, found := headers[http.CanonicalHeaderKey("Authorization")]; found {
- var Authorization string
+ var Authorization HeaderAuthorization
n := len(valueList)
if n != 1 {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for Authorization, got %d", n))
@@ -796,7 +818,7 @@ func (w *ServerInterfaceWrapper) GetAdminUsers(ctx echo.Context) error {
}
// Invoke the callback with all the unmarshaled arguments
- err = w.Handler.GetAdminUsers(ctx, params)
+ err = w.Handler.AdminGetUsers(ctx, params)
return err
}
@@ -806,17 +828,11 @@ func (w *ServerInterfaceWrapper) GetGames(ctx echo.Context) error {
// Parameter object where we will unmarshal all parameters from the context
var params GetGamesParams
- // ------------- Optional query parameter "player_id" -------------
-
- err = runtime.BindQueryParameter("form", true, false, "player_id", ctx.QueryParams(), &params.PlayerId)
- if err != nil {
- return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter player_id: %s", err))
- }
headers := ctx.Request().Header
// ------------- Required header parameter "Authorization" -------------
if valueList, found := headers[http.CanonicalHeaderKey("Authorization")]; found {
- var Authorization string
+ var Authorization HeaderAuthorization
n := len(valueList)
if n != 1 {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for Authorization, got %d", n))
@@ -837,11 +853,11 @@ func (w *ServerInterfaceWrapper) GetGames(ctx echo.Context) error {
return err
}
-// GetGamesGameId converts echo context to params.
-func (w *ServerInterfaceWrapper) GetGamesGameId(ctx echo.Context) error {
+// GetGame converts echo context to params.
+func (w *ServerInterfaceWrapper) GetGame(ctx echo.Context) error {
var err error
// ------------- Path parameter "game_id" -------------
- var gameId int
+ var gameId PathGameId
err = runtime.BindStyledParameterWithOptions("simple", "game_id", ctx.Param("game_id"), &gameId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true})
if err != nil {
@@ -849,12 +865,12 @@ func (w *ServerInterfaceWrapper) GetGamesGameId(ctx echo.Context) error {
}
// Parameter object where we will unmarshal all parameters from the context
- var params GetGamesGameIdParams
+ var params GetGameParams
headers := ctx.Request().Header
// ------------- Required header parameter "Authorization" -------------
if valueList, found := headers[http.CanonicalHeaderKey("Authorization")]; found {
- var Authorization string
+ var Authorization HeaderAuthorization
n := len(valueList)
if n != 1 {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for Authorization, got %d", n))
@@ -871,7 +887,7 @@ func (w *ServerInterfaceWrapper) GetGamesGameId(ctx echo.Context) error {
}
// Invoke the callback with all the unmarshaled arguments
- err = w.Handler.GetGamesGameId(ctx, gameId, params)
+ err = w.Handler.GetGame(ctx, gameId, params)
return err
}
@@ -894,7 +910,7 @@ func (w *ServerInterfaceWrapper) GetToken(ctx echo.Context) error {
headers := ctx.Request().Header
// ------------- Required header parameter "Authorization" -------------
if valueList, found := headers[http.CanonicalHeaderKey("Authorization")]; found {
- var Authorization string
+ var Authorization HeaderAuthorization
n := len(valueList)
if n != 1 {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for Authorization, got %d", n))
@@ -943,164 +959,194 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
Handler: si,
}
- router.GET(baseURL+"/admin/games", wrapper.GetAdminGames)
- router.GET(baseURL+"/admin/games/:game_id", wrapper.GetAdminGamesGameId)
- router.PUT(baseURL+"/admin/games/:game_id", wrapper.PutAdminGamesGameId)
- router.GET(baseURL+"/admin/users", wrapper.GetAdminUsers)
+ router.GET(baseURL+"/admin/games", wrapper.AdminGetGames)
+ router.GET(baseURL+"/admin/games/:game_id", wrapper.AdminGetGame)
+ router.PUT(baseURL+"/admin/games/:game_id", wrapper.AdminPutGame)
+ router.GET(baseURL+"/admin/users", wrapper.AdminGetUsers)
router.GET(baseURL+"/games", wrapper.GetGames)
- router.GET(baseURL+"/games/:game_id", wrapper.GetGamesGameId)
+ router.GET(baseURL+"/games/:game_id", wrapper.GetGame)
router.POST(baseURL+"/login", wrapper.PostLogin)
router.GET(baseURL+"/token", wrapper.GetToken)
}
-type GetAdminGamesRequestObject struct {
- Params GetAdminGamesParams
+type BadRequestJSONResponse Error
+
+type ForbiddenJSONResponse Error
+
+type NotFoundJSONResponse Error
+
+type UnauthorizedJSONResponse Error
+
+type AdminGetGamesRequestObject struct {
+ Params AdminGetGamesParams
}
-type GetAdminGamesResponseObject interface {
- VisitGetAdminGamesResponse(w http.ResponseWriter) error
+type AdminGetGamesResponseObject interface {
+ VisitAdminGetGamesResponse(w http.ResponseWriter) error
}
-type GetAdminGames200JSONResponse struct {
+type AdminGetGames200JSONResponse struct {
Games []Game `json:"games"`
}
-func (response GetAdminGames200JSONResponse) VisitGetAdminGamesResponse(w http.ResponseWriter) error {
+func (response AdminGetGames200JSONResponse) VisitAdminGetGamesResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(200)
return json.NewEncoder(w).Encode(response)
}
-type GetAdminGames403JSONResponse struct {
- Message string `json:"message"`
+type AdminGetGames401JSONResponse struct{ UnauthorizedJSONResponse }
+
+func (response AdminGetGames401JSONResponse) VisitAdminGetGamesResponse(w http.ResponseWriter) error {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(401)
+
+ return json.NewEncoder(w).Encode(response)
}
-func (response GetAdminGames403JSONResponse) VisitGetAdminGamesResponse(w http.ResponseWriter) error {
+type AdminGetGames403JSONResponse struct{ ForbiddenJSONResponse }
+
+func (response AdminGetGames403JSONResponse) VisitAdminGetGamesResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(403)
return json.NewEncoder(w).Encode(response)
}
-type GetAdminGamesGameIdRequestObject struct {
- GameId int `json:"game_id"`
- Params GetAdminGamesGameIdParams
+type AdminGetGameRequestObject struct {
+ GameId PathGameId `json:"game_id"`
+ Params AdminGetGameParams
}
-type GetAdminGamesGameIdResponseObject interface {
- VisitGetAdminGamesGameIdResponse(w http.ResponseWriter) error
+type AdminGetGameResponseObject interface {
+ VisitAdminGetGameResponse(w http.ResponseWriter) error
}
-type GetAdminGamesGameId200JSONResponse struct {
+type AdminGetGame200JSONResponse struct {
Game Game `json:"game"`
}
-func (response GetAdminGamesGameId200JSONResponse) VisitGetAdminGamesGameIdResponse(w http.ResponseWriter) error {
+func (response AdminGetGame200JSONResponse) VisitAdminGetGameResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(200)
return json.NewEncoder(w).Encode(response)
}
-type GetAdminGamesGameId403JSONResponse struct {
- Message string `json:"message"`
+type AdminGetGame401JSONResponse struct{ UnauthorizedJSONResponse }
+
+func (response AdminGetGame401JSONResponse) VisitAdminGetGameResponse(w http.ResponseWriter) error {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(401)
+
+ return json.NewEncoder(w).Encode(response)
}
-func (response GetAdminGamesGameId403JSONResponse) VisitGetAdminGamesGameIdResponse(w http.ResponseWriter) error {
+type AdminGetGame403JSONResponse struct{ ForbiddenJSONResponse }
+
+func (response AdminGetGame403JSONResponse) VisitAdminGetGameResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(403)
return json.NewEncoder(w).Encode(response)
}
-type GetAdminGamesGameId404JSONResponse struct {
- Message string `json:"message"`
-}
+type AdminGetGame404JSONResponse struct{ NotFoundJSONResponse }
-func (response GetAdminGamesGameId404JSONResponse) VisitGetAdminGamesGameIdResponse(w http.ResponseWriter) error {
+func (response AdminGetGame404JSONResponse) VisitAdminGetGameResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(404)
return json.NewEncoder(w).Encode(response)
}
-type PutAdminGamesGameIdRequestObject struct {
- GameId int `json:"game_id"`
- Params PutAdminGamesGameIdParams
- Body *PutAdminGamesGameIdJSONRequestBody
+type AdminPutGameRequestObject struct {
+ GameId PathGameId `json:"game_id"`
+ Params AdminPutGameParams
+ Body *AdminPutGameJSONRequestBody
}
-type PutAdminGamesGameIdResponseObject interface {
- VisitPutAdminGamesGameIdResponse(w http.ResponseWriter) error
+type AdminPutGameResponseObject interface {
+ VisitAdminPutGameResponse(w http.ResponseWriter) error
}
-type PutAdminGamesGameId204Response struct {
+type AdminPutGame204Response struct {
}
-func (response PutAdminGamesGameId204Response) VisitPutAdminGamesGameIdResponse(w http.ResponseWriter) error {
+func (response AdminPutGame204Response) VisitAdminPutGameResponse(w http.ResponseWriter) error {
w.WriteHeader(204)
return nil
}
-type PutAdminGamesGameId400JSONResponse struct {
- Message string `json:"message"`
-}
+type AdminPutGame400JSONResponse struct{ BadRequestJSONResponse }
-func (response PutAdminGamesGameId400JSONResponse) VisitPutAdminGamesGameIdResponse(w http.ResponseWriter) error {
+func (response AdminPutGame400JSONResponse) VisitAdminPutGameResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(400)
return json.NewEncoder(w).Encode(response)
}
-type PutAdminGamesGameId403JSONResponse struct {
- Message string `json:"message"`
+type AdminPutGame401JSONResponse struct{ UnauthorizedJSONResponse }
+
+func (response AdminPutGame401JSONResponse) VisitAdminPutGameResponse(w http.ResponseWriter) error {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(401)
+
+ return json.NewEncoder(w).Encode(response)
}
-func (response PutAdminGamesGameId403JSONResponse) VisitPutAdminGamesGameIdResponse(w http.ResponseWriter) error {
+type AdminPutGame403JSONResponse struct{ ForbiddenJSONResponse }
+
+func (response AdminPutGame403JSONResponse) VisitAdminPutGameResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(403)
return json.NewEncoder(w).Encode(response)
}
-type PutAdminGamesGameId404JSONResponse struct {
- Message string `json:"message"`
-}
+type AdminPutGame404JSONResponse struct{ NotFoundJSONResponse }
-func (response PutAdminGamesGameId404JSONResponse) VisitPutAdminGamesGameIdResponse(w http.ResponseWriter) error {
+func (response AdminPutGame404JSONResponse) VisitAdminPutGameResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(404)
return json.NewEncoder(w).Encode(response)
}
-type GetAdminUsersRequestObject struct {
- Params GetAdminUsersParams
+type AdminGetUsersRequestObject struct {
+ Params AdminGetUsersParams
}
-type GetAdminUsersResponseObject interface {
- VisitGetAdminUsersResponse(w http.ResponseWriter) error
+type AdminGetUsersResponseObject interface {
+ VisitAdminGetUsersResponse(w http.ResponseWriter) error
}
-type GetAdminUsers200JSONResponse struct {
+type AdminGetUsers200JSONResponse struct {
Users []User `json:"users"`
}
-func (response GetAdminUsers200JSONResponse) VisitGetAdminUsersResponse(w http.ResponseWriter) error {
+func (response AdminGetUsers200JSONResponse) VisitAdminGetUsersResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(200)
return json.NewEncoder(w).Encode(response)
}
-type GetAdminUsers403JSONResponse struct {
- Message string `json:"message"`
+type AdminGetUsers401JSONResponse struct{ UnauthorizedJSONResponse }
+
+func (response AdminGetUsers401JSONResponse) VisitAdminGetUsersResponse(w http.ResponseWriter) error {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(401)
+
+ return json.NewEncoder(w).Encode(response)
}
-func (response GetAdminUsers403JSONResponse) VisitGetAdminUsersResponse(w http.ResponseWriter) error {
+type AdminGetUsers403JSONResponse struct{ ForbiddenJSONResponse }
+
+func (response AdminGetUsers403JSONResponse) VisitAdminGetUsersResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(403)
@@ -1126,10 +1172,17 @@ func (response GetGames200JSONResponse) VisitGetGamesResponse(w http.ResponseWri
return json.NewEncoder(w).Encode(response)
}
-type GetGames403JSONResponse struct {
- Message string `json:"message"`
+type GetGames401JSONResponse struct{ UnauthorizedJSONResponse }
+
+func (response GetGames401JSONResponse) VisitGetGamesResponse(w http.ResponseWriter) error {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(401)
+
+ return json.NewEncoder(w).Encode(response)
}
+type GetGames403JSONResponse struct{ ForbiddenJSONResponse }
+
func (response GetGames403JSONResponse) VisitGetGamesResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(403)
@@ -1137,42 +1190,47 @@ func (response GetGames403JSONResponse) VisitGetGamesResponse(w http.ResponseWri
return json.NewEncoder(w).Encode(response)
}
-type GetGamesGameIdRequestObject struct {
- GameId int `json:"game_id"`
- Params GetGamesGameIdParams
+type GetGameRequestObject struct {
+ GameId PathGameId `json:"game_id"`
+ Params GetGameParams
}
-type GetGamesGameIdResponseObject interface {
- VisitGetGamesGameIdResponse(w http.ResponseWriter) error
+type GetGameResponseObject interface {
+ VisitGetGameResponse(w http.ResponseWriter) error
}
-type GetGamesGameId200JSONResponse struct {
+type GetGame200JSONResponse struct {
Game Game `json:"game"`
}
-func (response GetGamesGameId200JSONResponse) VisitGetGamesGameIdResponse(w http.ResponseWriter) error {
+func (response GetGame200JSONResponse) VisitGetGameResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(200)
return json.NewEncoder(w).Encode(response)
}
-type GetGamesGameId403JSONResponse struct {
- Message string `json:"message"`
+type GetGame401JSONResponse struct{ UnauthorizedJSONResponse }
+
+func (response GetGame401JSONResponse) VisitGetGameResponse(w http.ResponseWriter) error {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(401)
+
+ return json.NewEncoder(w).Encode(response)
}
-func (response GetGamesGameId403JSONResponse) VisitGetGamesGameIdResponse(w http.ResponseWriter) error {
+type GetGame403JSONResponse struct{ ForbiddenJSONResponse }
+
+func (response GetGame403JSONResponse) VisitGetGameResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(403)
return json.NewEncoder(w).Encode(response)
}
-type GetGamesGameId404JSONResponse struct {
- Message string `json:"message"`
-}
+type GetGame404JSONResponse struct{ NotFoundJSONResponse }
-func (response GetGamesGameId404JSONResponse) VisitGetGamesGameIdResponse(w http.ResponseWriter) error {
+func (response GetGame404JSONResponse) VisitGetGameResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(404)
@@ -1198,9 +1256,7 @@ func (response PostLogin200JSONResponse) VisitPostLoginResponse(w http.ResponseW
return json.NewEncoder(w).Encode(response)
}
-type PostLogin401JSONResponse struct {
- Message string `json:"message"`
-}
+type PostLogin401JSONResponse struct{ UnauthorizedJSONResponse }
func (response PostLogin401JSONResponse) VisitPostLoginResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
@@ -1228,13 +1284,11 @@ func (response GetToken200JSONResponse) VisitGetTokenResponse(w http.ResponseWri
return json.NewEncoder(w).Encode(response)
}
-type GetToken403JSONResponse struct {
- Message string `json:"message"`
-}
+type GetToken401JSONResponse struct{ UnauthorizedJSONResponse }
-func (response GetToken403JSONResponse) VisitGetTokenResponse(w http.ResponseWriter) error {
+func (response GetToken401JSONResponse) VisitGetTokenResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(403)
+ w.WriteHeader(401)
return json.NewEncoder(w).Encode(response)
}
@@ -1243,22 +1297,22 @@ func (response GetToken403JSONResponse) VisitGetTokenResponse(w http.ResponseWri
type StrictServerInterface interface {
// List games
// (GET /admin/games)
- GetAdminGames(ctx context.Context, request GetAdminGamesRequestObject) (GetAdminGamesResponseObject, error)
+ AdminGetGames(ctx context.Context, request AdminGetGamesRequestObject) (AdminGetGamesResponseObject, error)
// Get a game
// (GET /admin/games/{game_id})
- GetAdminGamesGameId(ctx context.Context, request GetAdminGamesGameIdRequestObject) (GetAdminGamesGameIdResponseObject, error)
+ AdminGetGame(ctx context.Context, request AdminGetGameRequestObject) (AdminGetGameResponseObject, error)
// Update a game
// (PUT /admin/games/{game_id})
- PutAdminGamesGameId(ctx context.Context, request PutAdminGamesGameIdRequestObject) (PutAdminGamesGameIdResponseObject, error)
+ AdminPutGame(ctx context.Context, request AdminPutGameRequestObject) (AdminPutGameResponseObject, error)
// List all users
// (GET /admin/users)
- GetAdminUsers(ctx context.Context, request GetAdminUsersRequestObject) (GetAdminUsersResponseObject, error)
+ AdminGetUsers(ctx context.Context, request AdminGetUsersRequestObject) (AdminGetUsersResponseObject, error)
// List games
// (GET /games)
GetGames(ctx context.Context, request GetGamesRequestObject) (GetGamesResponseObject, error)
// Get a game
// (GET /games/{game_id})
- GetGamesGameId(ctx context.Context, request GetGamesGameIdRequestObject) (GetGamesGameIdResponseObject, error)
+ GetGame(ctx context.Context, request GetGameRequestObject) (GetGameResponseObject, error)
// User login
// (POST /login)
PostLogin(ctx context.Context, request PostLoginRequestObject) (PostLoginResponseObject, error)
@@ -1279,108 +1333,108 @@ type strictHandler struct {
middlewares []StrictMiddlewareFunc
}
-// GetAdminGames operation middleware
-func (sh *strictHandler) GetAdminGames(ctx echo.Context, params GetAdminGamesParams) error {
- var request GetAdminGamesRequestObject
+// AdminGetGames operation middleware
+func (sh *strictHandler) AdminGetGames(ctx echo.Context, params AdminGetGamesParams) error {
+ var request AdminGetGamesRequestObject
request.Params = params
handler := func(ctx echo.Context, request interface{}) (interface{}, error) {
- return sh.ssi.GetAdminGames(ctx.Request().Context(), request.(GetAdminGamesRequestObject))
+ return sh.ssi.AdminGetGames(ctx.Request().Context(), request.(AdminGetGamesRequestObject))
}
for _, middleware := range sh.middlewares {
- handler = middleware(handler, "GetAdminGames")
+ handler = middleware(handler, "AdminGetGames")
}
response, err := handler(ctx, request)
if err != nil {
return err
- } else if validResponse, ok := response.(GetAdminGamesResponseObject); ok {
- return validResponse.VisitGetAdminGamesResponse(ctx.Response())
+ } else if validResponse, ok := response.(AdminGetGamesResponseObject); ok {
+ return validResponse.VisitAdminGetGamesResponse(ctx.Response())
} else if response != nil {
return fmt.Errorf("unexpected response type: %T", response)
}
return nil
}
-// GetAdminGamesGameId operation middleware
-func (sh *strictHandler) GetAdminGamesGameId(ctx echo.Context, gameId int, params GetAdminGamesGameIdParams) error {
- var request GetAdminGamesGameIdRequestObject
+// AdminGetGame operation middleware
+func (sh *strictHandler) AdminGetGame(ctx echo.Context, gameId PathGameId, params AdminGetGameParams) error {
+ var request AdminGetGameRequestObject
request.GameId = gameId
request.Params = params
handler := func(ctx echo.Context, request interface{}) (interface{}, error) {
- return sh.ssi.GetAdminGamesGameId(ctx.Request().Context(), request.(GetAdminGamesGameIdRequestObject))
+ return sh.ssi.AdminGetGame(ctx.Request().Context(), request.(AdminGetGameRequestObject))
}
for _, middleware := range sh.middlewares {
- handler = middleware(handler, "GetAdminGamesGameId")
+ handler = middleware(handler, "AdminGetGame")
}
response, err := handler(ctx, request)
if err != nil {
return err
- } else if validResponse, ok := response.(GetAdminGamesGameIdResponseObject); ok {
- return validResponse.VisitGetAdminGamesGameIdResponse(ctx.Response())
+ } else if validResponse, ok := response.(AdminGetGameResponseObject); ok {
+ return validResponse.VisitAdminGetGameResponse(ctx.Response())
} else if response != nil {
return fmt.Errorf("unexpected response type: %T", response)
}
return nil
}
-// PutAdminGamesGameId operation middleware
-func (sh *strictHandler) PutAdminGamesGameId(ctx echo.Context, gameId int, params PutAdminGamesGameIdParams) error {
- var request PutAdminGamesGameIdRequestObject
+// AdminPutGame operation middleware
+func (sh *strictHandler) AdminPutGame(ctx echo.Context, gameId PathGameId, params AdminPutGameParams) error {
+ var request AdminPutGameRequestObject
request.GameId = gameId
request.Params = params
- var body PutAdminGamesGameIdJSONRequestBody
+ var body AdminPutGameJSONRequestBody
if err := ctx.Bind(&body); err != nil {
return err
}
request.Body = &body
handler := func(ctx echo.Context, request interface{}) (interface{}, error) {
- return sh.ssi.PutAdminGamesGameId(ctx.Request().Context(), request.(PutAdminGamesGameIdRequestObject))
+ return sh.ssi.AdminPutGame(ctx.Request().Context(), request.(AdminPutGameRequestObject))
}
for _, middleware := range sh.middlewares {
- handler = middleware(handler, "PutAdminGamesGameId")
+ handler = middleware(handler, "AdminPutGame")
}
response, err := handler(ctx, request)
if err != nil {
return err
- } else if validResponse, ok := response.(PutAdminGamesGameIdResponseObject); ok {
- return validResponse.VisitPutAdminGamesGameIdResponse(ctx.Response())
+ } else if validResponse, ok := response.(AdminPutGameResponseObject); ok {
+ return validResponse.VisitAdminPutGameResponse(ctx.Response())
} else if response != nil {
return fmt.Errorf("unexpected response type: %T", response)
}
return nil
}
-// GetAdminUsers operation middleware
-func (sh *strictHandler) GetAdminUsers(ctx echo.Context, params GetAdminUsersParams) error {
- var request GetAdminUsersRequestObject
+// AdminGetUsers operation middleware
+func (sh *strictHandler) AdminGetUsers(ctx echo.Context, params AdminGetUsersParams) error {
+ var request AdminGetUsersRequestObject
request.Params = params
handler := func(ctx echo.Context, request interface{}) (interface{}, error) {
- return sh.ssi.GetAdminUsers(ctx.Request().Context(), request.(GetAdminUsersRequestObject))
+ return sh.ssi.AdminGetUsers(ctx.Request().Context(), request.(AdminGetUsersRequestObject))
}
for _, middleware := range sh.middlewares {
- handler = middleware(handler, "GetAdminUsers")
+ handler = middleware(handler, "AdminGetUsers")
}
response, err := handler(ctx, request)
if err != nil {
return err
- } else if validResponse, ok := response.(GetAdminUsersResponseObject); ok {
- return validResponse.VisitGetAdminUsersResponse(ctx.Response())
+ } else if validResponse, ok := response.(AdminGetUsersResponseObject); ok {
+ return validResponse.VisitAdminGetUsersResponse(ctx.Response())
} else if response != nil {
return fmt.Errorf("unexpected response type: %T", response)
}
@@ -1412,26 +1466,26 @@ func (sh *strictHandler) GetGames(ctx echo.Context, params GetGamesParams) error
return nil
}
-// GetGamesGameId operation middleware
-func (sh *strictHandler) GetGamesGameId(ctx echo.Context, gameId int, params GetGamesGameIdParams) error {
- var request GetGamesGameIdRequestObject
+// GetGame operation middleware
+func (sh *strictHandler) GetGame(ctx echo.Context, gameId PathGameId, params GetGameParams) error {
+ var request GetGameRequestObject
request.GameId = gameId
request.Params = params
handler := func(ctx echo.Context, request interface{}) (interface{}, error) {
- return sh.ssi.GetGamesGameId(ctx.Request().Context(), request.(GetGamesGameIdRequestObject))
+ return sh.ssi.GetGame(ctx.Request().Context(), request.(GetGameRequestObject))
}
for _, middleware := range sh.middlewares {
- handler = middleware(handler, "GetGamesGameId")
+ handler = middleware(handler, "GetGame")
}
response, err := handler(ctx, request)
if err != nil {
return err
- } else if validResponse, ok := response.(GetGamesGameIdResponseObject); ok {
- return validResponse.VisitGetGamesGameIdResponse(ctx.Response())
+ } else if validResponse, ok := response.(GetGameResponseObject); ok {
+ return validResponse.VisitGetGameResponse(ctx.Response())
} else if response != nil {
return fmt.Errorf("unexpected response type: %T", response)
}
@@ -1495,32 +1549,32 @@ func (sh *strictHandler) GetToken(ctx echo.Context, params GetTokenParams) error
// Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{
- "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",
- "LMuy/wIAAP//vgYmOHMsAAA=",
+ "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",
}
// GetSwagger returns the content of the embedded swagger specification file
diff --git a/backend/api/handler_wrapper.go b/backend/api/handler_wrapper.go
new file mode 100644
index 0000000..939e37a
--- /dev/null
+++ b/backend/api/handler_wrapper.go
@@ -0,0 +1,156 @@
+// Code generated by go generate; DO NOT EDIT.
+
+package api
+
+import (
+ "context"
+ "errors"
+ "strings"
+
+ "github.com/nsfisis/iosdc-japan-2024-albatross/backend/auth"
+ "github.com/nsfisis/iosdc-japan-2024-albatross/backend/db"
+)
+
+var _ StrictServerInterface = (*ApiHandlerWrapper)(nil)
+
+type ApiHandlerWrapper struct {
+ innerHandler ApiHandler
+}
+
+func NewHandler(queries *db.Queries, hubs GameHubsInterface) *ApiHandlerWrapper {
+ return &ApiHandlerWrapper{
+ innerHandler: ApiHandler{
+ q: queries,
+ hubs: hubs,
+ },
+ }
+}
+
+func parseJWTClaimsFromAuthorizationHeader(authorization string) (*auth.JWTClaims, error) {
+ const prefix = "Bearer "
+ if !strings.HasPrefix(authorization, prefix) {
+ return nil, errors.New("invalid authorization header")
+ }
+ token := authorization[len(prefix):]
+ claims, err := auth.ParseJWT(token)
+ if err != nil {
+ return nil, err
+ }
+ return claims, nil
+}
+
+func (h *ApiHandlerWrapper) AdminGetGame(ctx context.Context, request AdminGetGameRequestObject) (AdminGetGameResponseObject, error) {
+ user, err := parseJWTClaimsFromAuthorizationHeader(request.Params.Authorization)
+ if err != nil {
+ return AdminGetGame401JSONResponse{
+ UnauthorizedJSONResponse: UnauthorizedJSONResponse{
+ Message: "Unauthorized",
+ },
+ }, nil
+ }
+ if !user.IsAdmin {
+ return AdminGetGame403JSONResponse{
+ ForbiddenJSONResponse: ForbiddenJSONResponse{
+ Message: "Forbidden",
+ },
+ }, nil
+ }
+ return h.innerHandler.AdminGetGame(ctx, request, user)
+}
+
+func (h *ApiHandlerWrapper) AdminGetGames(ctx context.Context, request AdminGetGamesRequestObject) (AdminGetGamesResponseObject, error) {
+ user, err := parseJWTClaimsFromAuthorizationHeader(request.Params.Authorization)
+ if err != nil {
+ return AdminGetGames401JSONResponse{
+ UnauthorizedJSONResponse: UnauthorizedJSONResponse{
+ Message: "Unauthorized",
+ },
+ }, nil
+ }
+ if !user.IsAdmin {
+ return AdminGetGames403JSONResponse{
+ ForbiddenJSONResponse: ForbiddenJSONResponse{
+ Message: "Forbidden",
+ },
+ }, nil
+ }
+ return h.innerHandler.AdminGetGames(ctx, request, user)
+}
+
+func (h *ApiHandlerWrapper) AdminGetUsers(ctx context.Context, request AdminGetUsersRequestObject) (AdminGetUsersResponseObject, error) {
+ user, err := parseJWTClaimsFromAuthorizationHeader(request.Params.Authorization)
+ if err != nil {
+ return AdminGetUsers401JSONResponse{
+ UnauthorizedJSONResponse: UnauthorizedJSONResponse{
+ Message: "Unauthorized",
+ },
+ }, nil
+ }
+ if !user.IsAdmin {
+ return AdminGetUsers403JSONResponse{
+ ForbiddenJSONResponse: ForbiddenJSONResponse{
+ Message: "Forbidden",
+ },
+ }, nil
+ }
+ return h.innerHandler.AdminGetUsers(ctx, request, user)
+}
+
+func (h *ApiHandlerWrapper) AdminPutGame(ctx context.Context, request AdminPutGameRequestObject) (AdminPutGameResponseObject, error) {
+ user, err := parseJWTClaimsFromAuthorizationHeader(request.Params.Authorization)
+ if err != nil {
+ return AdminPutGame401JSONResponse{
+ UnauthorizedJSONResponse: UnauthorizedJSONResponse{
+ Message: "Unauthorized",
+ },
+ }, nil
+ }
+ if !user.IsAdmin {
+ return AdminPutGame403JSONResponse{
+ ForbiddenJSONResponse: ForbiddenJSONResponse{
+ Message: "Forbidden",
+ },
+ }, nil
+ }
+ return h.innerHandler.AdminPutGame(ctx, request, user)
+}
+
+func (h *ApiHandlerWrapper) GetGame(ctx context.Context, request GetGameRequestObject) (GetGameResponseObject, error) {
+ user, err := parseJWTClaimsFromAuthorizationHeader(request.Params.Authorization)
+ if err != nil {
+ return GetGame401JSONResponse{
+ UnauthorizedJSONResponse: UnauthorizedJSONResponse{
+ Message: "Unauthorized",
+ },
+ }, nil
+ }
+ return h.innerHandler.GetGame(ctx, request, user)
+}
+
+func (h *ApiHandlerWrapper) GetGames(ctx context.Context, request GetGamesRequestObject) (GetGamesResponseObject, error) {
+ user, err := parseJWTClaimsFromAuthorizationHeader(request.Params.Authorization)
+ if err != nil {
+ return GetGames401JSONResponse{
+ UnauthorizedJSONResponse: UnauthorizedJSONResponse{
+ Message: "Unauthorized",
+ },
+ }, nil
+ }
+ return h.innerHandler.GetGames(ctx, request, user)
+}
+
+func (h *ApiHandlerWrapper) GetToken(ctx context.Context, request GetTokenRequestObject) (GetTokenResponseObject, error) {
+ user, err := parseJWTClaimsFromAuthorizationHeader(request.Params.Authorization)
+ if err != nil {
+ return GetToken401JSONResponse{
+ UnauthorizedJSONResponse: UnauthorizedJSONResponse{
+ Message: "Unauthorized",
+ },
+ }, nil
+ }
+ return h.innerHandler.GetToken(ctx, request, user)
+}
+
+func (h *ApiHandlerWrapper) PostLogin(ctx context.Context, request PostLoginRequestObject) (PostLoginResponseObject, error) {
+ return h.innerHandler.PostLogin(ctx, request)
+}
diff --git a/backend/api/handlers.go b/backend/api/handlers.go
index c96cd2a..35fc9f7 100644
--- a/backend/api/handlers.go
+++ b/backend/api/handlers.go
@@ -4,7 +4,6 @@ import (
"context"
"errors"
"net/http"
- "strings"
"time"
"github.com/jackc/pgx/v5"
@@ -15,8 +14,6 @@ import (
"github.com/nsfisis/iosdc-japan-2024-albatross/backend/db"
)
-var _ StrictServerInterface = (*ApiHandler)(nil)
-
type ApiHandler struct {
q *db.Queries
hubs GameHubsInterface
@@ -26,20 +23,7 @@ type GameHubsInterface interface {
StartGame(gameID int) error
}
-func NewHandler(queries *db.Queries, hubs GameHubsInterface) *ApiHandler {
- return &ApiHandler{
- q: queries,
- hubs: hubs,
- }
-}
-
-func (h *ApiHandler) GetAdminGames(ctx context.Context, request GetAdminGamesRequestObject) (GetAdminGamesResponseObject, error) {
- user := ctx.Value("user").(*auth.JWTClaims)
- if !user.IsAdmin {
- return GetAdminGames403JSONResponse{
- Message: "Forbidden",
- }, nil
- }
+func (h *ApiHandler) AdminGetGames(ctx context.Context, request AdminGetGamesRequestObject, user *auth.JWTClaims) (AdminGetGamesResponseObject, error) {
gameRows, err := h.q.ListGames(ctx)
if err != nil {
return nil, echo.NewHTTPError(http.StatusInternalServerError, err.Error())
@@ -71,24 +55,20 @@ func (h *ApiHandler) GetAdminGames(ctx context.Context, request GetAdminGamesReq
Problem: problem,
}
}
- return GetAdminGames200JSONResponse{
+ return AdminGetGames200JSONResponse{
Games: games,
}, nil
}
-func (h *ApiHandler) GetAdminGamesGameId(ctx context.Context, request GetAdminGamesGameIdRequestObject) (GetAdminGamesGameIdResponseObject, error) {
- user := ctx.Value("user").(*auth.JWTClaims)
- if !user.IsAdmin {
- return GetAdminGamesGameId403JSONResponse{
- Message: "Forbidden",
- }, nil
- }
- gameId := request.GameId
- row, err := h.q.GetGameById(ctx, int32(gameId))
+func (h *ApiHandler) AdminGetGame(ctx context.Context, request AdminGetGameRequestObject, user *auth.JWTClaims) (AdminGetGameResponseObject, error) {
+ gameID := request.GameId
+ row, err := h.q.GetGameById(ctx, int32(gameID))
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
- return GetAdminGamesGameId404JSONResponse{
- Message: "Game not found",
+ return AdminGetGame404JSONResponse{
+ NotFoundJSONResponse: NotFoundJSONResponse{
+ Message: "Game not found",
+ },
}, nil
} else {
return nil, echo.NewHTTPError(http.StatusInternalServerError, err.Error())
@@ -118,18 +98,12 @@ func (h *ApiHandler) GetAdminGamesGameId(ctx context.Context, request GetAdminGa
StartedAt: startedAt,
Problem: problem,
}
- return GetAdminGamesGameId200JSONResponse{
+ return AdminGetGame200JSONResponse{
Game: game,
}, nil
}
-func (h *ApiHandler) PutAdminGamesGameId(ctx context.Context, request PutAdminGamesGameIdRequestObject) (PutAdminGamesGameIdResponseObject, error) {
- user := ctx.Value("user").(*auth.JWTClaims)
- if !user.IsAdmin {
- return PutAdminGamesGameId403JSONResponse{
- Message: "Forbidden",
- }, nil
- }
+func (h *ApiHandler) AdminPutGame(ctx context.Context, request AdminPutGameRequestObject, user *auth.JWTClaims) (AdminPutGameResponseObject, error) {
gameID := request.GameId
displayName := request.Body.DisplayName
durationSeconds := request.Body.DurationSeconds
@@ -140,8 +114,10 @@ func (h *ApiHandler) PutAdminGamesGameId(ctx context.Context, request PutAdminGa
game, err := h.q.GetGameById(ctx, int32(gameID))
if err != nil {
if err == pgx.ErrNoRows {
- return PutAdminGamesGameId404JSONResponse{
- Message: "Game not found",
+ return AdminPutGame404JSONResponse{
+ NotFoundJSONResponse: NotFoundJSONResponse{
+ Message: "Game not found",
+ },
}, nil
} else {
return nil, echo.NewHTTPError(http.StatusInternalServerError, err.Error())
@@ -202,21 +178,17 @@ func (h *ApiHandler) PutAdminGamesGameId(ctx context.Context, request PutAdminGa
ProblemID: changedProblemID,
})
if err != nil {
- return PutAdminGamesGameId400JSONResponse{
- Message: err.Error(),
+ return AdminPutGame400JSONResponse{
+ BadRequestJSONResponse: BadRequestJSONResponse{
+ Message: err.Error(),
+ },
}, nil
}
- return PutAdminGamesGameId204Response{}, nil
+ return AdminPutGame204Response{}, nil
}
-func (h *ApiHandler) GetAdminUsers(ctx context.Context, request GetAdminUsersRequestObject) (GetAdminUsersResponseObject, error) {
- user := ctx.Value("user").(*auth.JWTClaims)
- if !user.IsAdmin {
- return GetAdminUsers403JSONResponse{
- Message: "Forbidden",
- }, nil
- }
+func (h *ApiHandler) AdminGetUsers(ctx context.Context, request AdminGetUsersRequestObject, user *auth.JWTClaims) (AdminGetUsersResponseObject, error) {
users, err := h.q.ListUsers(ctx)
if err != nil {
return nil, echo.NewHTTPError(http.StatusInternalServerError, err.Error())
@@ -231,7 +203,7 @@ func (h *ApiHandler) GetAdminUsers(ctx context.Context, request GetAdminUsersReq
IsAdmin: u.IsAdmin,
}
}
- return GetAdminUsers200JSONResponse{
+ return AdminGetUsers200JSONResponse{
Users: responseUsers,
}, nil
}
@@ -239,17 +211,21 @@ func (h *ApiHandler) GetAdminUsers(ctx context.Context, request GetAdminUsersReq
func (h *ApiHandler) PostLogin(ctx context.Context, request PostLoginRequestObject) (PostLoginResponseObject, error) {
username := request.Body.Username
password := request.Body.Password
- userId, err := auth.Login(ctx, h.q, username, password)
+ userID, err := auth.Login(ctx, h.q, username, password)
if err != nil {
return PostLogin401JSONResponse{
- Message: "Invalid username or password",
+ UnauthorizedJSONResponse: UnauthorizedJSONResponse{
+ Message: "Invalid username or password",
+ },
}, nil
}
- user, err := h.q.GetUserById(ctx, int32(userId))
+ user, err := h.q.GetUserById(ctx, int32(userID))
if err != nil {
return PostLogin401JSONResponse{
- Message: "Invalid username or password",
+ UnauthorizedJSONResponse: UnauthorizedJSONResponse{
+ Message: "Invalid username or password",
+ },
}, nil
}
@@ -263,8 +239,7 @@ func (h *ApiHandler) PostLogin(ctx context.Context, request PostLoginRequestObje
}, nil
}
-func (h *ApiHandler) GetToken(ctx context.Context, request GetTokenRequestObject) (GetTokenResponseObject, error) {
- user := ctx.Value("user").(*auth.JWTClaims)
+func (h *ApiHandler) GetToken(ctx context.Context, request GetTokenRequestObject, user *auth.JWTClaims) (GetTokenResponseObject, error) {
newToken, err := auth.NewShortLivedJWT(user)
if err != nil {
return nil, echo.NewHTTPError(http.StatusInternalServerError, err.Error())
@@ -274,98 +249,53 @@ func (h *ApiHandler) GetToken(ctx context.Context, request GetTokenRequestObject
}, nil
}
-func (h *ApiHandler) GetGames(ctx context.Context, request GetGamesRequestObject) (GetGamesResponseObject, error) {
- user := ctx.Value("user").(*auth.JWTClaims)
- playerId := request.Params.PlayerId
- if !user.IsAdmin {
- if playerId == nil || *playerId != user.UserID {
- return GetGames403JSONResponse{
- Message: "Forbidden",
- }, nil
- }
+func (h *ApiHandler) GetGames(ctx context.Context, request GetGamesRequestObject, user *auth.JWTClaims) (GetGamesResponseObject, error) {
+ gameRows, err := h.q.ListGamesForPlayer(ctx, int32(user.UserID))
+ if err != nil {
+ return nil, echo.NewHTTPError(http.StatusInternalServerError, err.Error())
}
- if playerId == nil {
- gameRows, err := h.q.ListGames(ctx)
- if err != nil {
- return nil, echo.NewHTTPError(http.StatusInternalServerError, err.Error())
+ games := make([]Game, len(gameRows))
+ for i, row := range gameRows {
+ var startedAt *int
+ if row.StartedAt.Valid {
+ startedAtTimestamp := int(row.StartedAt.Time.Unix())
+ startedAt = &startedAtTimestamp
}
- games := make([]Game, len(gameRows))
- for i, row := range gameRows {
- var startedAt *int
- if row.StartedAt.Valid {
- startedAtTimestamp := int(row.StartedAt.Time.Unix())
- startedAt = &startedAtTimestamp
- }
- var problem *Problem
- if row.ProblemID != nil {
- if row.Title == nil || row.Description == nil {
- panic("inconsistent data")
- }
- problem = &Problem{
- ProblemId: int(*row.ProblemID),
- Title: *row.Title,
- Description: *row.Description,
- }
+ var problem *Problem
+ if row.ProblemID != nil {
+ if row.Title == nil || row.Description == nil {
+ panic("inconsistent data")
}
- games[i] = Game{
- GameId: int(row.GameID),
- State: GameState(row.State),
- DisplayName: row.DisplayName,
- DurationSeconds: int(row.DurationSeconds),
- StartedAt: startedAt,
- Problem: problem,
+ problem = &Problem{
+ ProblemId: int(*row.ProblemID),
+ Title: *row.Title,
+ Description: *row.Description,
}
}
- return GetGames200JSONResponse{
- Games: games,
- }, nil
- } else {
- gameRows, err := h.q.ListGamesForPlayer(ctx, int32(*playerId))
- if err != nil {
- return nil, echo.NewHTTPError(http.StatusInternalServerError, err.Error())
- }
- games := make([]Game, len(gameRows))
- for i, row := range gameRows {
- var startedAt *int
- if row.StartedAt.Valid {
- startedAtTimestamp := int(row.StartedAt.Time.Unix())
- startedAt = &startedAtTimestamp
- }
- var problem *Problem
- if row.ProblemID != nil {
- if row.Title == nil || row.Description == nil {
- panic("inconsistent data")
- }
- problem = &Problem{
- ProblemId: int(*row.ProblemID),
- Title: *row.Title,
- Description: *row.Description,
- }
- }
- games[i] = Game{
- GameId: int(row.GameID),
- State: GameState(row.State),
- DisplayName: row.DisplayName,
- DurationSeconds: int(row.DurationSeconds),
- StartedAt: startedAt,
- Problem: problem,
- }
+ games[i] = Game{
+ GameId: int(row.GameID),
+ State: GameState(row.State),
+ DisplayName: row.DisplayName,
+ DurationSeconds: int(row.DurationSeconds),
+ StartedAt: startedAt,
+ Problem: problem,
}
- return GetGames200JSONResponse{
- Games: games,
- }, nil
}
+ return GetGames200JSONResponse{
+ Games: games,
+ }, nil
}
-func (h *ApiHandler) GetGamesGameId(ctx context.Context, request GetGamesGameIdRequestObject) (GetGamesGameIdResponseObject, error) {
- user := ctx.Value("user").(*auth.JWTClaims)
+func (h *ApiHandler) GetGame(ctx context.Context, request GetGameRequestObject, user *auth.JWTClaims) (GetGameResponseObject, error) {
// TODO: check user permission
- gameId := request.GameId
- row, err := h.q.GetGameById(ctx, int32(gameId))
+ gameID := request.GameId
+ row, err := h.q.GetGameById(ctx, int32(gameID))
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
- return GetGamesGameId404JSONResponse{
- Message: "Game not found",
+ return GetGame404JSONResponse{
+ NotFoundJSONResponse: NotFoundJSONResponse{
+ Message: "Game not found",
+ },
}, nil
} else {
return nil, echo.NewHTTPError(http.StatusInternalServerError, err.Error())
@@ -397,49 +327,7 @@ func (h *ApiHandler) GetGamesGameId(ctx context.Context, request GetGamesGameIdR
StartedAt: startedAt,
Problem: problem,
}
- return GetGamesGameId200JSONResponse{
+ return GetGame200JSONResponse{
Game: game,
}, nil
}
-
-func _assertUserResponseIsCompatibleWithJWTClaims() {
- var c auth.JWTClaims
- var u User
- u.UserId = c.UserID
- u.Username = c.Username
- u.DisplayName = c.DisplayName
- u.IconPath = c.IconPath
- u.IsAdmin = c.IsAdmin
- _ = u
-}
-
-func setupJWTFromAuthorizationHeader(c echo.Context) error {
- authorization := c.Request().Header.Get("Authorization")
- const prefix = "Bearer "
- if !strings.HasPrefix(authorization, prefix) {
- return echo.NewHTTPError(http.StatusUnauthorized)
- }
- token := authorization[len(prefix):]
- claims, err := auth.ParseJWT(token)
- if err != nil {
- return echo.NewHTTPError(http.StatusUnauthorized, err.Error())
- }
- c.SetRequest(c.Request().WithContext(context.WithValue(c.Request().Context(), "user", claims)))
- return nil
-}
-
-func NewJWTMiddleware() StrictMiddlewareFunc {
- return func(handler StrictHandlerFunc, operationID string) StrictHandlerFunc {
- if operationID == "PostLogin" {
- return handler
- }
-
- return func(c echo.Context, request interface{}) (interface{}, error) {
- err := setupJWTFromAuthorizationHeader(c)
- if err != nil {
- return nil, echo.NewHTTPError(http.StatusUnauthorized, err.Error())
- }
- return handler(c, request)
- }
- }
-}
diff --git a/backend/gen/api_handler_wrapper_gen.go b/backend/gen/api_handler_wrapper_gen.go
new file mode 100644
index 0000000..7fd34b2
--- /dev/null
+++ b/backend/gen/api_handler_wrapper_gen.go
@@ -0,0 +1,168 @@
+package main
+
+import (
+ "bytes"
+ "flag"
+ "go/ast"
+ "go/format"
+ "go/parser"
+ "go/token"
+ "os"
+ "slices"
+ "strings"
+ "text/template"
+)
+
+func main() {
+ inputFile := flag.String("i", "", "input file")
+ outputFile := flag.String("o", "", "output file")
+ flag.Parse()
+
+ if inputFile == nil || *inputFile == "" || outputFile == nil || *outputFile == "" {
+ flag.PrintDefaults()
+ os.Exit(1)
+ }
+
+ // Parse the input file
+ fileSet := token.NewFileSet()
+ parsedFile, err := parser.ParseFile(fileSet, *inputFile, nil, parser.SkipObjectResolution)
+ if err != nil {
+ panic(err)
+ }
+
+ // Find methods in StrictServerInterface
+ var methods []string
+ for _, decl := range parsedFile.Decls {
+ genDecl, ok := decl.(*ast.GenDecl)
+ if !ok {
+ continue
+ }
+ for _, spec := range genDecl.Specs {
+ typeSpec, ok := spec.(*ast.TypeSpec)
+ if !ok {
+ continue
+ }
+ if typeSpec.Name.Name != "StrictServerInterface" {
+ continue
+ }
+ interfaceType, ok := typeSpec.Type.(*ast.InterfaceType)
+ if !ok {
+ continue
+ }
+ for _, method := range interfaceType.Methods.List {
+ if len(method.Names) != 0 {
+ methods = append(methods, method.Names[0].Name)
+ }
+ }
+ }
+ }
+ if len(methods) == 0 {
+ panic("StrictServerInterface not found")
+ }
+ slices.Sort(methods)
+
+ type TemplateParameter struct {
+ Name string
+ RequiresLogin bool
+ RequiresAdminRole bool
+ }
+ templateParameters := make([]TemplateParameter, len(methods))
+ for i, method := range methods {
+ templateParameters[i] = TemplateParameter{
+ Name: method,
+ RequiresLogin: method != "PostLogin",
+ RequiresAdminRole: strings.Contains(method, "Admin"),
+ }
+ }
+
+ // Generate code.
+ tmpl, err := template.New("code").Parse(templateText)
+ if err != nil {
+ panic(err)
+ }
+
+ var buf bytes.Buffer
+ err = tmpl.Execute(&buf, templateParameters)
+ if err != nil {
+ panic(err)
+ }
+
+ formatted, err := format.Source(buf.Bytes())
+ if err != nil {
+ panic(err)
+ }
+
+ err = os.WriteFile(*outputFile, formatted, 0644)
+ if err != nil {
+ panic(err)
+ }
+}
+
+const templateText = `// Code generated by go generate; DO NOT EDIT.
+
+package api
+
+import (
+ "context"
+ "errors"
+ "strings"
+
+ "github.com/nsfisis/iosdc-japan-2024-albatross/backend/auth"
+ "github.com/nsfisis/iosdc-japan-2024-albatross/backend/db"
+)
+
+var _ StrictServerInterface = (*ApiHandlerWrapper)(nil)
+
+type ApiHandlerWrapper struct {
+ innerHandler ApiHandler
+}
+
+func NewHandler(queries *db.Queries, hubs GameHubsInterface) *ApiHandlerWrapper {
+ return &ApiHandlerWrapper{
+ innerHandler: ApiHandler{
+ q: queries,
+ hubs: hubs,
+ },
+ }
+}
+
+func parseJWTClaimsFromAuthorizationHeader(authorization string) (*auth.JWTClaims, error) {
+ const prefix = "Bearer "
+ if !strings.HasPrefix(authorization, prefix) {
+ return nil, errors.New("invalid authorization header")
+ }
+ token := authorization[len(prefix):]
+ claims, err := auth.ParseJWT(token)
+ if err != nil {
+ return nil, err
+ }
+ return claims, nil
+}
+
+{{ range . }}
+ func (h *ApiHandlerWrapper) {{ .Name }}(ctx context.Context, request {{ .Name }}RequestObject) ({{ .Name }}ResponseObject, error) {
+ {{ if .RequiresLogin -}}
+ user, err := parseJWTClaimsFromAuthorizationHeader(request.Params.Authorization)
+ if err != nil {
+ return {{ .Name }}401JSONResponse{
+ UnauthorizedJSONResponse: UnauthorizedJSONResponse{
+ Message: "Unauthorized",
+ },
+ }, nil
+ }
+ {{ if .RequiresAdminRole -}}
+ if !user.IsAdmin {
+ return {{ .Name }}403JSONResponse{
+ ForbiddenJSONResponse: ForbiddenJSONResponse{
+ Message: "Forbidden",
+ },
+ }, nil
+ }
+ {{ end -}}
+ return h.innerHandler.{{ .Name }}(ctx, request, user)
+ {{ else -}}
+ return h.innerHandler.{{ .Name }}(ctx, request)
+ {{ end -}}
+ }
+{{ end }}
+`
diff --git a/backend/gen/gen.go b/backend/gen/gen.go
new file mode 100644
index 0000000..6fb430f
--- /dev/null
+++ b/backend/gen/gen.go
@@ -0,0 +1,5 @@
+package main
+
+//go:generate go run github.com/sqlc-dev/sqlc/cmd/sqlc generate
+//go:generate go run github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen -config oapi-codegen.yaml ../../openapi.yaml
+//go:generate go run ./api_handler_wrapper_gen.go -i ../api/generated.go -o ../api/handler_wrapper.go
diff --git a/backend/oapi-codegen.yaml b/backend/gen/oapi-codegen.yaml
index 65cf079..d235096 100644
--- a/backend/oapi-codegen.yaml
+++ b/backend/gen/oapi-codegen.yaml
@@ -4,7 +4,7 @@ generate:
echo-server: true
strict-server: true
embedded-spec: true
-output: api/generated.go
+output: ../api/generated.go
output-options:
skip-prune: true
nullable-type: true
diff --git a/backend/sqlc.yaml b/backend/gen/sqlc.yaml
index 3b2d1d0..c56e44b 100644
--- a/backend/sqlc.yaml
+++ b/backend/gen/sqlc.yaml
@@ -1,11 +1,11 @@
version: "2"
sql:
- engine: "postgresql"
- queries: "query.sql"
- schema: "schema.sql"
+ queries: "../query.sql"
+ schema: "../schema.sql"
gen:
go:
package: "db"
- out: "db"
+ out: "../db"
sql_package: "pgx/v5"
emit_pointers_for_null_types: true
diff --git a/backend/main.go b/backend/main.go
index d636af7..0257113 100644
--- a/backend/main.go
+++ b/backend/main.go
@@ -75,9 +75,7 @@ func main() {
apiGroup := e.Group("/api")
apiGroup.Use(oapimiddleware.OapiRequestValidator(openApiSpec))
apiHandler := api.NewHandler(queries, gameHubs)
- api.RegisterHandlers(apiGroup, api.NewStrictHandler(apiHandler, []api.StrictMiddlewareFunc{
- api.NewJWTMiddleware(),
- }))
+ api.RegisterHandlers(apiGroup, api.NewStrictHandler(apiHandler, nil))
gameHubs.Run()
diff --git a/frontend/Makefile b/frontend/Makefile
deleted file mode 100644
index 17dd690..0000000
--- a/frontend/Makefile
+++ /dev/null
@@ -1,3 +0,0 @@
-.PHONY: openapi-typescript
-openapi-typescript:
- npx --no-install openapi-typescript --path-params-as-types --output ./app/.server/api/schema.d.ts ../openapi.yaml
diff --git a/frontend/app/.server/api/client.ts b/frontend/app/.server/api/client.ts
index 8e50b7e..8f53b5b 100644
--- a/frontend/app/.server/api/client.ts
+++ b/frontend/app/.server/api/client.ts
@@ -1,9 +1,95 @@
import createClient from "openapi-fetch";
-import type { paths } from "./schema";
+import type { paths, operations } from "./schema";
-export const apiClient = createClient<paths>({
+const apiClient = createClient<paths>({
baseUrl:
process.env.NODE_ENV === "development"
? "http://localhost:8002/api/"
: "http://api-server/api/",
});
+
+export async function apiPostLogin(username: string, password: string) {
+ const { data, error } = await apiClient.POST("/login", {
+ body: { username, password },
+ });
+ if (error) throw new Error(error.message);
+ return data;
+}
+
+export async function apiGetGames(token: string) {
+ const { data, error } = await apiClient.GET("/games", {
+ params: {
+ header: { Authorization: `Bearer ${token}` },
+ },
+ });
+ if (error) throw new Error(error.message);
+ return data;
+}
+
+export async function apiGetGame(token: string, gameId: number) {
+ const { data, error } = await apiClient.GET("/games/{game_id}", {
+ params: {
+ header: { Authorization: `Bearer ${token}` },
+ path: { game_id: gameId },
+ },
+ });
+ if (error) throw new Error(error.message);
+ return data;
+}
+
+export async function apiGetToken(token: string) {
+ const { data, error } = await apiClient.GET("/token", {
+ params: {
+ header: { Authorization: `Bearer ${token}` },
+ },
+ });
+ if (error) throw new Error(error.message);
+ return data;
+}
+
+export async function adminApiGetUsers(token: string) {
+ const { data, error } = await apiClient.GET("/admin/users", {
+ params: {
+ header: { Authorization: `Bearer ${token}` },
+ },
+ });
+ if (error) throw new Error(error.message);
+ return data;
+}
+
+export async function adminApiGetGames(token: string) {
+ const { data, error } = await apiClient.GET("/admin/games", {
+ params: {
+ header: { Authorization: `Bearer ${token}` },
+ },
+ });
+ if (error) throw new Error(error.message);
+ return data;
+}
+
+export async function adminApiGetGame(token: string, gameId: number) {
+ const { data, error } = await apiClient.GET("/admin/games/{game_id}", {
+ params: {
+ header: { Authorization: `Bearer ${token}` },
+ path: { game_id: gameId },
+ },
+ });
+ if (error) throw new Error(error.message);
+ return data;
+}
+
+export async function adminApiPutGame(
+ token: string,
+ gameId: number,
+ body: operations["adminPutGame"]["requestBody"]["content"]["application/json"],
+) {
+ const { data, error } = await apiClient.PUT("/admin/games/{game_id}", {
+ params: {
+ header: { Authorization: `Bearer ${token}` },
+ path: { game_id: gameId },
+ },
+ body,
+ });
+ if (error) throw new Error(error.message);
+ return data;
+}
diff --git a/frontend/app/.server/api/schema.d.ts b/frontend/app/.server/api/schema.d.ts
index 000c876..705380d 100644
--- a/frontend/app/.server/api/schema.d.ts
+++ b/frontend/app/.server/api/schema.d.ts
@@ -14,50 +14,7 @@ export interface paths {
get?: never;
put?: never;
/** User login */
- post: {
- parameters: {
- query?: never;
- header?: never;
- path?: never;
- cookie?: never;
- };
- requestBody: {
- content: {
- "application/json": {
- /** @example john */
- username: string;
- /** @example password123 */
- password: string;
- };
- };
- };
- responses: {
- /** @description Successfully authenticated */
- 200: {
- headers: {
- [name: string]: unknown;
- };
- content: {
- "application/json": {
- /** @example xxxxx.xxxxx.xxxxx */
- token: string;
- };
- };
- };
- /** @description Invalid username or password */
- 401: {
- headers: {
- [name: string]: unknown;
- };
- content: {
- "application/json": {
- /** @example Invalid credentials */
- message: string;
- };
- };
- };
- };
- };
+ post: operations["postLogin"];
delete?: never;
options?: never;
head?: never;
@@ -72,43 +29,7 @@ export interface paths {
cookie?: never;
};
/** Get a short-lived access token */
- get: {
- parameters: {
- query?: never;
- header: {
- Authorization: string;
- };
- path?: never;
- cookie?: never;
- };
- requestBody?: never;
- responses: {
- /** @description Successfully authenticated */
- 200: {
- headers: {
- [name: string]: unknown;
- };
- content: {
- "application/json": {
- /** @example xxxxx.xxxxx.xxxxx */
- token: string;
- };
- };
- };
- /** @description Forbidden */
- 403: {
- headers: {
- [name: string]: unknown;
- };
- content: {
- "application/json": {
- /** @example Forbidden operation */
- message: string;
- };
- };
- };
- };
- };
+ get: operations["getToken"];
put?: never;
post?: never;
delete?: never;
@@ -125,44 +46,7 @@ export interface paths {
cookie?: never;
};
/** List games */
- get: {
- parameters: {
- query?: {
- player_id?: number;
- };
- header: {
- Authorization: string;
- };
- path?: never;
- cookie?: never;
- };
- requestBody?: never;
- responses: {
- /** @description List of games */
- 200: {
- headers: {
- [name: string]: unknown;
- };
- content: {
- "application/json": {
- games: components["schemas"]["Game"][];
- };
- };
- };
- /** @description Forbidden */
- 403: {
- headers: {
- [name: string]: unknown;
- };
- content: {
- "application/json": {
- /** @example Forbidden operation */
- message: string;
- };
- };
- };
- };
- };
+ get: operations["getGames"];
put?: never;
post?: never;
delete?: never;
@@ -171,7 +55,7 @@ export interface paths {
patch?: never;
trace?: never;
};
- [path: `/games/${integer}`]: {
+ "/games/{game_id}": {
parameters: {
query?: never;
header?: never;
@@ -179,56 +63,7 @@ export interface paths {
cookie?: never;
};
/** Get a game */
- get: {
- parameters: {
- query?: never;
- header: {
- Authorization: string;
- };
- path: {
- game_id: number;
- };
- cookie?: never;
- };
- requestBody?: never;
- responses: {
- /** @description A game */
- 200: {
- headers: {
- [name: string]: unknown;
- };
- content: {
- "application/json": {
- game: components["schemas"]["Game"];
- };
- };
- };
- /** @description Forbidden */
- 403: {
- headers: {
- [name: string]: unknown;
- };
- content: {
- "application/json": {
- /** @example Forbidden operation */
- message: string;
- };
- };
- };
- /** @description Not found */
- 404: {
- headers: {
- [name: string]: unknown;
- };
- content: {
- "application/json": {
- /** @example Not found */
- message: string;
- };
- };
- };
- };
- };
+ get: operations["getGame"];
put?: never;
post?: never;
delete?: never;
@@ -245,42 +80,7 @@ export interface paths {
cookie?: never;
};
/** List all users */
- get: {
- parameters: {
- query?: never;
- header: {
- Authorization: string;
- };
- path?: never;
- cookie?: never;
- };
- requestBody?: never;
- responses: {
- /** @description List of users */
- 200: {
- headers: {
- [name: string]: unknown;
- };
- content: {
- "application/json": {
- users: components["schemas"]["User"][];
- };
- };
- };
- /** @description Forbidden */
- 403: {
- headers: {
- [name: string]: unknown;
- };
- content: {
- "application/json": {
- /** @example Forbidden operation */
- message: string;
- };
- };
- };
- };
- };
+ get: operations["adminGetUsers"];
put?: never;
post?: never;
delete?: never;
@@ -297,42 +97,7 @@ export interface paths {
cookie?: never;
};
/** List games */
- get: {
- parameters: {
- query?: never;
- header: {
- Authorization: string;
- };
- path?: never;
- cookie?: never;
- };
- requestBody?: never;
- responses: {
- /** @description List of games */
- 200: {
- headers: {
- [name: string]: unknown;
- };
- content: {
- "application/json": {
- games: components["schemas"]["Game"][];
- };
- };
- };
- /** @description Forbidden */
- 403: {
- headers: {
- [name: string]: unknown;
- };
- content: {
- "application/json": {
- /** @example Forbidden operation */
- message: string;
- };
- };
- };
- };
- };
+ get: operations["adminGetGames"];
put?: never;
post?: never;
delete?: never;
@@ -341,7 +106,7 @@ export interface paths {
patch?: never;
trace?: never;
};
- [path: `/admin/games/${integer}`]: {
+ "/admin/games/{game_id}": {
parameters: {
query?: never;
header?: never;
@@ -349,133 +114,9 @@ export interface paths {
cookie?: never;
};
/** Get a game */
- get: {
- parameters: {
- query?: never;
- header: {
- Authorization: string;
- };
- path: {
- game_id: number;
- };
- cookie?: never;
- };
- requestBody?: never;
- responses: {
- /** @description A game */
- 200: {
- headers: {
- [name: string]: unknown;
- };
- content: {
- "application/json": {
- game: components["schemas"]["Game"];
- };
- };
- };
- /** @description Forbidden */
- 403: {
- headers: {
- [name: string]: unknown;
- };
- content: {
- "application/json": {
- /** @example Forbidden operation */
- message: string;
- };
- };
- };
- /** @description Not found */
- 404: {
- headers: {
- [name: string]: unknown;
- };
- content: {
- "application/json": {
- /** @example Not found */
- message: string;
- };
- };
- };
- };
- };
+ get: operations["adminGetGame"];
/** Update a game */
- put: {
- parameters: {
- query?: never;
- header: {
- Authorization: string;
- };
- path: {
- game_id: number;
- };
- cookie?: never;
- };
- requestBody: {
- content: {
- "application/json": {
- /**
- * @example closed
- * @enum {string}
- */
- state?: "closed" | "waiting_entries" | "waiting_start" | "prepare" | "starting" | "gaming" | "finished";
- /** @example Game 1 */
- display_name?: string;
- /** @example 360 */
- duration_seconds?: number;
- /** @example 946684800 */
- started_at?: number | null;
- /** @example 1 */
- problem_id?: number | null;
- };
- };
- };
- responses: {
- /** @description Successfully updated */
- 204: {
- headers: {
- [name: string]: unknown;
- };
- content?: never;
- };
- /** @description Invalid request */
- 400: {
- headers: {
- [name: string]: unknown;
- };
- content: {
- "application/json": {
- /** @example Invalid request */
- message: string;
- };
- };
- };
- /** @description Forbidden */
- 403: {
- headers: {
- [name: string]: unknown;
- };
- content: {
- "application/json": {
- /** @example Forbidden operation */
- message: string;
- };
- };
- };
- /** @description Not found */
- 404: {
- headers: {
- [name: string]: unknown;
- };
- content: {
- "application/json": {
- /** @example Not found */
- message: string;
- };
- };
- };
- };
- };
+ put: operations["adminPutGame"];
post?: never;
delete?: never;
options?: never;
@@ -487,6 +128,10 @@ export interface paths {
export type webhooks = Record<string, never>;
export interface components {
schemas: {
+ Error: {
+ /** @example Invalid request */
+ message: string;
+ };
User: {
/** @example 123 */
user_id: number;
@@ -617,11 +262,291 @@ export interface components {
stderr: string;
};
};
- responses: never;
- parameters: never;
+ responses: {
+ /** @description Bad request */
+ BadRequest: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Error"];
+ };
+ };
+ /** @description Unauthorized */
+ Unauthorized: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Error"];
+ };
+ };
+ /** @description Forbidden */
+ Forbidden: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Error"];
+ };
+ };
+ /** @description Not found */
+ NotFound: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": components["schemas"]["Error"];
+ };
+ };
+ };
+ parameters: {
+ header_authorization: string;
+ path_game_id: number;
+ };
requestBodies: never;
headers: never;
pathItems: never;
}
export type $defs = Record<string, never>;
-export type operations = Record<string, never>;
+export interface operations {
+ postLogin: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": {
+ /** @example john */
+ username: string;
+ /** @example password123 */
+ password: string;
+ };
+ };
+ };
+ responses: {
+ /** @description Successfully authenticated */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ /** @example xxxxx.xxxxx.xxxxx */
+ token: string;
+ };
+ };
+ };
+ 401: components["responses"]["Unauthorized"];
+ };
+ };
+ getToken: {
+ parameters: {
+ query?: never;
+ header: {
+ Authorization: components["parameters"]["header_authorization"];
+ };
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Successfully authenticated */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ /** @example xxxxx.xxxxx.xxxxx */
+ token: string;
+ };
+ };
+ };
+ 401: components["responses"]["Unauthorized"];
+ };
+ };
+ getGames: {
+ parameters: {
+ query?: never;
+ header: {
+ Authorization: components["parameters"]["header_authorization"];
+ };
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description List of games */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ games: components["schemas"]["Game"][];
+ };
+ };
+ };
+ 401: components["responses"]["Unauthorized"];
+ 403: components["responses"]["Forbidden"];
+ };
+ };
+ getGame: {
+ parameters: {
+ query?: never;
+ header: {
+ Authorization: components["parameters"]["header_authorization"];
+ };
+ path: {
+ game_id: components["parameters"]["path_game_id"];
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description A game */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ game: components["schemas"]["Game"];
+ };
+ };
+ };
+ 401: components["responses"]["Unauthorized"];
+ 403: components["responses"]["Forbidden"];
+ 404: components["responses"]["NotFound"];
+ };
+ };
+ adminGetUsers: {
+ parameters: {
+ query?: never;
+ header: {
+ Authorization: components["parameters"]["header_authorization"];
+ };
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description List of users */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ users: components["schemas"]["User"][];
+ };
+ };
+ };
+ 401: components["responses"]["Unauthorized"];
+ 403: components["responses"]["Forbidden"];
+ };
+ };
+ adminGetGames: {
+ parameters: {
+ query?: never;
+ header: {
+ Authorization: components["parameters"]["header_authorization"];
+ };
+ path?: never;
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description List of games */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ games: components["schemas"]["Game"][];
+ };
+ };
+ };
+ 401: components["responses"]["Unauthorized"];
+ 403: components["responses"]["Forbidden"];
+ };
+ };
+ adminGetGame: {
+ parameters: {
+ query?: never;
+ header: {
+ Authorization: components["parameters"]["header_authorization"];
+ };
+ path: {
+ game_id: components["parameters"]["path_game_id"];
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description A game */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ game: components["schemas"]["Game"];
+ };
+ };
+ };
+ 401: components["responses"]["Unauthorized"];
+ 403: components["responses"]["Forbidden"];
+ 404: components["responses"]["NotFound"];
+ };
+ };
+ adminPutGame: {
+ parameters: {
+ query?: never;
+ header: {
+ Authorization: components["parameters"]["header_authorization"];
+ };
+ path: {
+ game_id: components["parameters"]["path_game_id"];
+ };
+ cookie?: never;
+ };
+ requestBody: {
+ content: {
+ "application/json": {
+ /**
+ * @example closed
+ * @enum {string}
+ */
+ state?: "closed" | "waiting_entries" | "waiting_start" | "prepare" | "starting" | "gaming" | "finished";
+ /** @example Game 1 */
+ display_name?: string;
+ /** @example 360 */
+ duration_seconds?: number;
+ /** @example 946684800 */
+ started_at?: number | null;
+ /** @example 1 */
+ problem_id?: number | null;
+ };
+ };
+ };
+ responses: {
+ /** @description Successfully updated */
+ 204: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content?: never;
+ };
+ 400: components["responses"]["BadRequest"];
+ 401: components["responses"]["Unauthorized"];
+ 403: components["responses"]["Forbidden"];
+ 404: components["responses"]["NotFound"];
+ };
+ };
+}
diff --git a/frontend/app/.server/auth.ts b/frontend/app/.server/auth.ts
index b80166b..b7e1820 100644
--- a/frontend/app/.server/auth.ts
+++ b/frontend/app/.server/auth.ts
@@ -3,22 +3,13 @@ import { FormStrategy } from "remix-auth-form";
import { jwtDecode } from "jwt-decode";
import type { Session } from "@remix-run/server-runtime";
import { sessionStorage } from "./session";
-import { apiClient } from "./api/client";
+import { apiPostLogin } from "./api/client";
import { components } from "./api/schema";
export const authenticator = new Authenticator<string>(sessionStorage);
async function login(username: string, password: string): Promise<string> {
- const { data, error } = await apiClient.POST("/login", {
- body: {
- username,
- password,
- },
- });
- if (error) {
- throw new Error(error.message);
- }
- return data.token;
+ return (await apiPostLogin(username, password)).token;
}
authenticator.use(
diff --git a/frontend/app/routes/admin.games.tsx b/frontend/app/routes/admin.games.tsx
index 085a97e..00a56d6 100644
--- a/frontend/app/routes/admin.games.tsx
+++ b/frontend/app/routes/admin.games.tsx
@@ -1,7 +1,7 @@
import type { LoaderFunctionArgs, MetaFunction } from "@remix-run/node";
import { useLoaderData, Link } from "@remix-run/react";
import { isAuthenticated } from "../.server/auth";
-import { apiClient } from "../.server/api/client";
+import { adminApiGetGames } from "../.server/api/client";
export const meta: MetaFunction = () => {
return [{ title: "[Admin] Games | iOSDC Japan 2024 Albatross.swift" }];
@@ -14,17 +14,8 @@ export async function loader({ request }: LoaderFunctionArgs) {
if (!user.is_admin) {
throw new Error("Unauthorized");
}
- const { data, error } = await apiClient.GET("/admin/games", {
- params: {
- header: {
- Authorization: `Bearer ${token}`,
- },
- },
- });
- if (error) {
- throw new Error(error.message);
- }
- return { games: data.games };
+ const { games } = await adminApiGetGames(token);
+ return { games };
}
export default function AdminGames() {
diff --git a/frontend/app/routes/admin.games_.$gameId.tsx b/frontend/app/routes/admin.games_.$gameId.tsx
index ff8f136..1ad8a2a 100644
--- a/frontend/app/routes/admin.games_.$gameId.tsx
+++ b/frontend/app/routes/admin.games_.$gameId.tsx
@@ -5,7 +5,7 @@ import type {
} from "@remix-run/node";
import { useLoaderData, Form } from "@remix-run/react";
import { isAuthenticated } from "../.server/auth";
-import { apiClient } from "../.server/api/client";
+import { adminApiPutGame, adminApiGetGame } from "../.server/api/client";
export const meta: MetaFunction<typeof loader> = ({ data }) => {
return [
@@ -25,20 +25,8 @@ export async function loader({ request, params }: LoaderFunctionArgs) {
throw new Error("Unauthorized");
}
const { gameId } = params;
- const { data, error } = await apiClient.GET("/admin/games/{game_id}", {
- params: {
- path: {
- game_id: Number(gameId),
- },
- header: {
- Authorization: `Bearer ${token}`,
- },
- },
- });
- if (error) {
- throw new Error(error.message);
- }
- return { game: data.game };
+ const { game } = await adminApiGetGame(token, Number(gameId));
+ return { game };
}
export async function action({ request, params }: ActionFunctionArgs) {
@@ -63,22 +51,9 @@ export async function action({ request, params }: ActionFunctionArgs) {
throw new Error("Invalid action");
}
- const { error } = await apiClient.PUT("/admin/games/{game_id}", {
- params: {
- path: {
- game_id: Number(gameId),
- },
- header: {
- Authorization: `Bearer ${token}`,
- },
- },
- body: {
- state: nextState,
- },
+ await adminApiPutGame(token, Number(gameId), {
+ state: nextState,
});
- if (error) {
- throw new Error(error.message);
- }
return null;
}
diff --git a/frontend/app/routes/admin.users.tsx b/frontend/app/routes/admin.users.tsx
index 6c9b60d..c2e4cc7 100644
--- a/frontend/app/routes/admin.users.tsx
+++ b/frontend/app/routes/admin.users.tsx
@@ -1,7 +1,7 @@
import type { LoaderFunctionArgs, MetaFunction } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
import { isAuthenticated } from "../.server/auth";
-import { apiClient } from "../.server/api/client";
+import { adminApiGetUsers } from "../.server/api/client";
export const meta: MetaFunction = () => {
return [{ title: "[Admin] Users | iOSDC Japan 2024 Albatross.swift" }];
@@ -14,17 +14,8 @@ export async function loader({ request }: LoaderFunctionArgs) {
if (!user.is_admin) {
throw new Error("Unauthorized");
}
- const { data, error } = await apiClient.GET("/admin/users", {
- params: {
- header: {
- Authorization: `Bearer ${token}`,
- },
- },
- });
- if (error) {
- throw new Error(error.message);
- }
- return { users: data.users };
+ const { users } = await adminApiGetUsers(token);
+ return { users };
}
export default function AdminUsers() {
diff --git a/frontend/app/routes/dashboard.tsx b/frontend/app/routes/dashboard.tsx
index 3cc8e13..b93e5d1 100644
--- a/frontend/app/routes/dashboard.tsx
+++ b/frontend/app/routes/dashboard.tsx
@@ -2,7 +2,7 @@ import type { LoaderFunctionArgs, MetaFunction } from "@remix-run/node";
import { redirect } from "@remix-run/node";
import { Link, useLoaderData, Form } from "@remix-run/react";
import { isAuthenticated } from "../.server/auth";
-import { apiClient } from "../.server/api/client";
+import { apiGetGames } from "../.server/api/client";
export const meta: MetaFunction = () => {
return [{ title: "Dashboard | iOSDC Japan 2024 Albatross.swift" }];
@@ -15,22 +15,10 @@ export async function loader({ request }: LoaderFunctionArgs) {
if (user.is_admin) {
return redirect("/admin/dashboard");
}
- const { data, error } = await apiClient.GET("/games", {
- params: {
- query: {
- player_id: user.user_id,
- },
- header: {
- Authorization: `Bearer ${token}`,
- },
- },
- });
- if (error) {
- throw new Error(error.message);
- }
+ const { games } = await apiGetGames(token);
return {
user,
- games: data.games,
+ games,
};
}
diff --git a/frontend/app/routes/golf.$gameId.play.tsx b/frontend/app/routes/golf.$gameId.play.tsx
index 919d8df..248c4e4 100644
--- a/frontend/app/routes/golf.$gameId.play.tsx
+++ b/frontend/app/routes/golf.$gameId.play.tsx
@@ -2,7 +2,7 @@ import type { LoaderFunctionArgs, MetaFunction } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
import { ClientOnly } from "remix-utils/client-only";
import { isAuthenticated } from "../.server/auth";
-import { apiClient } from "../.server/api/client";
+import { apiGetGame, apiGetToken } from "../.server/api/client";
import GolfPlayApp from "../components/GolfPlayApp.client";
import GolfPlayAppConnecting from "../components/GolfPlayApps/GolfPlayAppConnecting";
@@ -22,34 +22,10 @@ export async function loader({ params, request }: LoaderFunctionArgs) {
});
const fetchGame = async () => {
- const { data, error } = await apiClient.GET("/games/{game_id}", {
- params: {
- path: {
- game_id: Number(params.gameId),
- },
- header: {
- Authorization: `Bearer ${token}`,
- },
- },
- });
- if (error) {
- throw new Error(error.message);
- }
- return data.game;
+ return (await apiGetGame(token, Number(params.gameId))).game;
};
-
const fetchSockToken = async () => {
- const { data, error } = await apiClient.GET("/token", {
- params: {
- header: {
- Authorization: `Bearer ${token}`,
- },
- },
- });
- if (error) {
- throw new Error(error.message);
- }
- return data.token;
+ return (await apiGetToken(token)).token;
};
const [game, sockToken] = await Promise.all([fetchGame(), fetchSockToken()]);
diff --git a/frontend/app/routes/golf.$gameId.watch.tsx b/frontend/app/routes/golf.$gameId.watch.tsx
index c2e269e..1a14e78 100644
--- a/frontend/app/routes/golf.$gameId.watch.tsx
+++ b/frontend/app/routes/golf.$gameId.watch.tsx
@@ -2,7 +2,7 @@ import type { LoaderFunctionArgs, MetaFunction } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
import { ClientOnly } from "remix-utils/client-only";
import { isAuthenticated } from "../.server/auth";
-import { apiClient } from "../.server/api/client";
+import { apiGetGame, apiGetToken } from "../.server/api/client";
import GolfWatchApp from "../components/GolfWatchApp.client";
import GolfWatchAppConnecting from "../components/GolfWatchApps/GolfWatchAppConnecting";
@@ -22,34 +22,10 @@ export async function loader({ params, request }: LoaderFunctionArgs) {
});
const fetchGame = async () => {
- const { data, error } = await apiClient.GET("/games/{game_id}", {
- params: {
- path: {
- game_id: Number(params.gameId),
- },
- header: {
- Authorization: `Bearer ${token}`,
- },
- },
- });
- if (error) {
- throw new Error(error.message);
- }
- return data.game;
+ return (await apiGetGame(token, Number(params.gameId))).game;
};
-
const fetchSockToken = async () => {
- const { data, error } = await apiClient.GET("/token", {
- params: {
- header: {
- Authorization: `Bearer ${token}`,
- },
- },
- });
- if (error) {
- throw new Error(error.message);
- }
- return data.token;
+ return (await apiGetToken(token)).token;
};
const [game, sockToken] = await Promise.all([fetchGame(), fetchSockToken()]);
diff --git a/frontend/package.json b/frontend/package.json
index aefb693..12aa6ba 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -7,6 +7,7 @@
"build": "remix vite:build",
"dev": "remix vite:dev",
"lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .",
+ "openapi-typescript": "npx --no-install openapi-typescript --output ./app/.server/api/schema.d.ts ../openapi.yaml",
"start": "remix-serve ./build/server/index.js",
"typecheck": "tsc"
},
diff --git a/openapi.yaml b/openapi.yaml
index f28452b..683fadf 100644
--- a/openapi.yaml
+++ b/openapi.yaml
@@ -5,6 +5,7 @@ info:
paths:
/login:
post:
+ operationId: postLogin
summary: User login
requestBody:
required: true
@@ -36,26 +37,13 @@ paths:
required:
- token
'401':
- description: Invalid username or password
- content:
- application/json:
- schema:
- type: object
- properties:
- message:
- type: string
- example: "Invalid credentials"
- required:
- - message
+ $ref: '#/components/responses/Unauthorized'
/token:
get:
+ operationId: getToken
summary: Get a short-lived access token
parameters:
- - in: header
- name: Authorization
- schema:
- type: string
- required: true
+ - $ref: '#/components/parameters/header_authorization'
responses:
'200':
description: Successfully authenticated
@@ -69,32 +57,14 @@ paths:
example: "xxxxx.xxxxx.xxxxx"
required:
- token
- '403':
- description: Forbidden
- content:
- application/json:
- schema:
- type: object
- properties:
- message:
- type: string
- example: "Forbidden operation"
- required:
- - message
+ '401':
+ $ref: '#/components/responses/Unauthorized'
/games:
get:
+ operationId: getGames
summary: List games
parameters:
- - in: query
- name: player_id
- schema:
- type: integer
- required: false
- - in: header
- name: Authorization
- schema:
- type: string
- required: true
+ - $ref: '#/components/parameters/header_authorization'
responses:
'200':
description: List of games
@@ -109,32 +79,17 @@ paths:
$ref: '#/components/schemas/Game'
required:
- games
+ '401':
+ $ref: '#/components/responses/Unauthorized'
'403':
- description: Forbidden
- content:
- application/json:
- schema:
- type: object
- properties:
- message:
- type: string
- example: "Forbidden operation"
- required:
- - message
+ $ref: '#/components/responses/Forbidden'
/games/{game_id}:
get:
+ operationId: getGame
summary: Get a game
parameters:
- - in: path
- name: game_id
- schema:
- type: integer
- required: true
- - in: header
- name: Authorization
- schema:
- type: string
- required: true
+ - $ref: '#/components/parameters/header_authorization'
+ - $ref: '#/components/parameters/path_game_id'
responses:
'200':
description: A game
@@ -147,39 +102,18 @@ paths:
$ref: '#/components/schemas/Game'
required:
- game
+ '401':
+ $ref: '#/components/responses/Unauthorized'
'403':
- description: Forbidden
- content:
- application/json:
- schema:
- type: object
- properties:
- message:
- type: string
- example: "Forbidden operation"
- required:
- - message
+ $ref: '#/components/responses/Forbidden'
'404':
- description: Not found
- content:
- application/json:
- schema:
- type: object
- properties:
- message:
- type: string
- example: "Not found"
- required:
- - message
+ $ref: '#/components/responses/NotFound'
/admin/users:
get:
+ operationId: adminGetUsers
summary: List all users
parameters:
- - in: header
- name: Authorization
- schema:
- type: string
- required: true
+ - $ref: '#/components/parameters/header_authorization'
responses:
'200':
description: List of users
@@ -194,27 +128,16 @@ paths:
$ref: '#/components/schemas/User'
required:
- users
+ '401':
+ $ref: '#/components/responses/Unauthorized'
'403':
- description: Forbidden
- content:
- application/json:
- schema:
- type: object
- properties:
- message:
- type: string
- example: "Forbidden operation"
- required:
- - message
+ $ref: '#/components/responses/Forbidden'
/admin/games:
get:
+ operationId: adminGetGames
summary: List games
parameters:
- - in: header
- name: Authorization
- schema:
- type: string
- required: true
+ - $ref: '#/components/parameters/header_authorization'
responses:
'200':
description: List of games
@@ -229,32 +152,17 @@ paths:
$ref: '#/components/schemas/Game'
required:
- games
+ '401':
+ $ref: '#/components/responses/Unauthorized'
'403':
- description: Forbidden
- content:
- application/json:
- schema:
- type: object
- properties:
- message:
- type: string
- example: "Forbidden operation"
- required:
- - message
+ $ref: '#/components/responses/Forbidden'
/admin/games/{game_id}:
get:
+ operationId: adminGetGame
summary: Get a game
parameters:
- - in: path
- name: game_id
- schema:
- type: integer
- required: true
- - in: header
- name: Authorization
- schema:
- type: string
- required: true
+ - $ref: '#/components/parameters/header_authorization'
+ - $ref: '#/components/parameters/path_game_id'
responses:
'200':
description: A game
@@ -267,43 +175,18 @@ paths:
$ref: '#/components/schemas/Game'
required:
- game
+ '401':
+ $ref: '#/components/responses/Unauthorized'
'403':
- description: Forbidden
- content:
- application/json:
- schema:
- type: object
- properties:
- message:
- type: string
- example: "Forbidden operation"
- required:
- - message
+ $ref: '#/components/responses/Forbidden'
'404':
- description: Not found
- content:
- application/json:
- schema:
- type: object
- properties:
- message:
- type: string
- example: "Not found"
- required:
- - message
+ $ref: '#/components/responses/NotFound'
put:
+ operationId: adminPutGame
summary: Update a game
parameters:
- - in: path
- name: game_id
- schema:
- type: integer
- required: true
- - in: header
- name: Authorization
- schema:
- type: string
- required: true
+ - $ref: '#/components/parameters/header_authorization'
+ - $ref: '#/components/parameters/path_game_id'
requestBody:
required: true
content:
@@ -340,43 +223,61 @@ paths:
'204':
description: Successfully updated
'400':
- description: Invalid request
- content:
- application/json:
- schema:
- type: object
- properties:
- message:
- type: string
- example: "Invalid request"
- required:
- - message
+ $ref: '#/components/responses/BadRequest'
+ '401':
+ $ref: '#/components/responses/Unauthorized'
'403':
- description: Forbidden
- content:
- application/json:
- schema:
- type: object
- properties:
- message:
- type: string
- example: "Forbidden operation"
- required:
- - message
+ $ref: '#/components/responses/Forbidden'
'404':
- description: Not found
- content:
- application/json:
- schema:
- type: object
- properties:
- message:
- type: string
- example: "Not found"
- required:
- - message
+ $ref: '#/components/responses/NotFound'
components:
+ parameters:
+ header_authorization:
+ in: header
+ name: Authorization
+ schema:
+ type: string
+ required: true
+ path_game_id:
+ in: path
+ name: game_id
+ schema:
+ type: integer
+ required: true
+ responses:
+ BadRequest:
+ description: Bad request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
+ Unauthorized:
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
+ Forbidden:
+ description: Forbidden
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
+ NotFound:
+ description: Not found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/Error'
schemas:
+ Error:
+ type: object
+ properties:
+ message:
+ type: string
+ example: "Invalid request"
+ required:
+ - message
User:
type: object
properties: