diff options
| author | nsfisis <nsfisis@gmail.com> | 2024-07-28 02:11:02 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2024-07-28 02:11:02 +0900 |
| commit | 6603d3ea5a54647e8eda8ec253835eb7a36e5eb2 (patch) | |
| tree | 3a1e823bf464d5b291c0a1cdf004af72eeb10de1 /backend/api | |
| parent | ec5fa50956c7f7f7f57c2dde4fb85ad0f3eb441f (diff) | |
| download | iosdc-japan-2025-albatross-6603d3ea5a54647e8eda8ec253835eb7a36e5eb2.tar.gz iosdc-japan-2025-albatross-6603d3ea5a54647e8eda8ec253835eb7a36e5eb2.tar.zst iosdc-japan-2025-albatross-6603d3ea5a54647e8eda8ec253835eb7a36e5eb2.zip | |
backend: openapi
Diffstat (limited to 'backend/api')
| -rw-r--r-- | backend/api/generated.go | 256 | ||||
| -rw-r--r-- | backend/api/handlers.go | 62 |
2 files changed, 318 insertions, 0 deletions
diff --git a/backend/api/generated.go b/backend/api/generated.go new file mode 100644 index 0000000..23811ca --- /dev/null +++ b/backend/api/generated.go @@ -0,0 +1,256 @@ +// Package api provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.3.0 DO NOT EDIT. +package api + +import ( + "bytes" + "compress/gzip" + "context" + "encoding/base64" + "encoding/json" + "fmt" + "net/http" + "net/url" + "path" + "strings" + + "github.com/getkin/kin-openapi/openapi3" + "github.com/labstack/echo/v4" + strictecho "github.com/oapi-codegen/runtime/strictmiddleware/echo" +) + +// JwtPayload defines model for JwtPayload. +type JwtPayload struct { + DisplayUsername string `json:"display_username"` + IconPath *string `json:"icon_path"` + IsAdmin bool `json:"is_admin"` + UserId float32 `json:"user_id"` + Username string `json:"username"` +} + +// PostApiLoginJSONBody defines parameters for PostApiLogin. +type PostApiLoginJSONBody struct { + Password string `json:"password"` + Username string `json:"username"` +} + +// PostApiLoginJSONRequestBody defines body for PostApiLogin for application/json ContentType. +type PostApiLoginJSONRequestBody PostApiLoginJSONBody + +// ServerInterface represents all server handlers. +type ServerInterface interface { + // User login + // (POST /api/login) + PostApiLogin(ctx echo.Context) error +} + +// ServerInterfaceWrapper converts echo contexts to parameters. +type ServerInterfaceWrapper struct { + Handler ServerInterface +} + +// PostApiLogin converts echo context to params. +func (w *ServerInterfaceWrapper) PostApiLogin(ctx echo.Context) error { + var err error + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.PostApiLogin(ctx) + return err +} + +// This is a simple interface which specifies echo.Route addition functions which +// are present on both echo.Echo and echo.Group, since we want to allow using +// either of them for path registration +type EchoRouter interface { + CONNECT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + DELETE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + GET(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + HEAD(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + OPTIONS(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + PATCH(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + POST(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + PUT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + TRACE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route +} + +// RegisterHandlers adds each server route to the EchoRouter. +func RegisterHandlers(router EchoRouter, si ServerInterface) { + RegisterHandlersWithBaseURL(router, si, "") +} + +// Registers handlers, and prepends BaseURL to the paths, so that the paths +// can be served under a prefix. +func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL string) { + + wrapper := ServerInterfaceWrapper{ + Handler: si, + } + + router.POST(baseURL+"/api/login", wrapper.PostApiLogin) + +} + +type PostApiLoginRequestObject struct { + Body *PostApiLoginJSONRequestBody +} + +type PostApiLoginResponseObject interface { + VisitPostApiLoginResponse(w http.ResponseWriter) error +} + +type PostApiLogin200JSONResponse struct { + Token string `json:"token"` +} + +func (response PostApiLogin200JSONResponse) VisitPostApiLoginResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type PostApiLogin401JSONResponse struct { + Message string `json:"message"` +} + +func (response PostApiLogin401JSONResponse) VisitPostApiLoginResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(401) + + return json.NewEncoder(w).Encode(response) +} + +// StrictServerInterface represents all server handlers. +type StrictServerInterface interface { + // User login + // (POST /api/login) + PostApiLogin(ctx context.Context, request PostApiLoginRequestObject) (PostApiLoginResponseObject, error) +} + +type StrictHandlerFunc = strictecho.StrictEchoHandlerFunc +type StrictMiddlewareFunc = strictecho.StrictEchoMiddlewareFunc + +func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface { + return &strictHandler{ssi: ssi, middlewares: middlewares} +} + +type strictHandler struct { + ssi StrictServerInterface + middlewares []StrictMiddlewareFunc +} + +// PostApiLogin operation middleware +func (sh *strictHandler) PostApiLogin(ctx echo.Context) error { + var request PostApiLoginRequestObject + + var body PostApiLoginJSONRequestBody + if err := ctx.Bind(&body); err != nil { + return err + } + request.Body = &body + + handler := func(ctx echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.PostApiLogin(ctx.Request().Context(), request.(PostApiLoginRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PostApiLogin") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(PostApiLoginResponseObject); ok { + return validResponse.VisitPostApiLoginResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + +// Base64 encoded, gzipped, json marshaled Swagger object +var swaggerSpec = []string{ + + "H4sIAAAAAAAC/6RSwY7aQAz9lcjnKAToKTeqXlj1gFT1VFXISQwMndjT8WTZaJV/ryYpAQrSVoIDEOf5", + "2c/vvUMljRMmDgrFO2h1oAaHvy+nsMHOCtbxyXlx5IOh4V1t1Fnstq2SZ2wo1ugNG2cJCniRAydfhCCF", + "0LlY0eAN76FPwVTCW4fhcNsyMw3uSWdHOXB2dHtIgVtrsYxvg2/pEZVusW4M3zDt0OoFXIpYQo7ouOrW", + "1Dfg+WI5QbltSvJn5L2ouNm9oD4FT79b46mG4sc05IokvT/W1eo/J0Ypj1QF6COl4Z3E6cGEYfbKlhi8", + "qCaGQySxyYnKZLVZQwqv5NUIQwF5Ns/yqEAcMToDBSyzPMshhXjxwbkZOjOzsh/P5kRD/I3eYjDC6xoK", + "2IiGlTNfB9QokDR8lrqL2Eo4EA9t6Jw11dA4O6rwJUH3kXGoehJ/a8BUnS+Wj9LypBV/rz2NfnztS1cM", + "2lBQJ6zj3os8f0J1kF90G1B4i5/s6vtDKSPJ4+Vr0sobF8YEfGurilR3rbVdgm04EIe4KtXxmp/y+RNS", + "GlLF/T9WrPkVramTylMdZ6HVD+Wcif5H0Jn/7GYiPpnsjHBtmwZ9BwV8V/LJmOy+7/s/AQAA//8GnrqA", + "2gQAAA==", +} + +// GetSwagger returns the content of the embedded swagger specification file +// or error if failed to decode +func decodeSpec() ([]byte, error) { + zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, "")) + if err != nil { + return nil, fmt.Errorf("error base64 decoding spec: %w", err) + } + zr, err := gzip.NewReader(bytes.NewReader(zipped)) + if err != nil { + return nil, fmt.Errorf("error decompressing spec: %w", err) + } + var buf bytes.Buffer + _, err = buf.ReadFrom(zr) + if err != nil { + return nil, fmt.Errorf("error decompressing spec: %w", err) + } + + return buf.Bytes(), nil +} + +var rawSpec = decodeSpecCached() + +// a naive cached of a decoded swagger spec +func decodeSpecCached() func() ([]byte, error) { + data, err := decodeSpec() + return func() ([]byte, error) { + return data, err + } +} + +// Constructs a synthetic filesystem for resolving external references when loading openapi specifications. +func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { + res := make(map[string]func() ([]byte, error)) + if len(pathToFile) > 0 { + res[pathToFile] = rawSpec + } + + return res +} + +// GetSwagger returns the Swagger specification corresponding to the generated code +// in this file. The external references of Swagger specification are resolved. +// The logic of resolving external references is tightly connected to "import-mapping" feature. +// Externally referenced files must be embedded in the corresponding golang packages. +// Urls can be supported but this task was out of the scope. +func GetSwagger() (swagger *openapi3.T, err error) { + resolvePath := PathToRawSpec("") + + loader := openapi3.NewLoader() + loader.IsExternalRefsAllowed = true + loader.ReadFromURIFunc = func(loader *openapi3.Loader, url *url.URL) ([]byte, error) { + pathToFile := url.String() + pathToFile = path.Clean(pathToFile) + getSpec, ok := resolvePath[pathToFile] + if !ok { + err1 := fmt.Errorf("path not found: %s", pathToFile) + return nil, err1 + } + return getSpec() + } + var specData []byte + specData, err = rawSpec() + if err != nil { + return + } + swagger, err = loader.LoadFromData(specData) + if err != nil { + return + } + return +} diff --git a/backend/api/handlers.go b/backend/api/handlers.go new file mode 100644 index 0000000..b8f80f3 --- /dev/null +++ b/backend/api/handlers.go @@ -0,0 +1,62 @@ +package api + +import ( + "context" + "net/http" + + "github.com/labstack/echo/v4" + + "github.com/nsfisis/iosdc-2024-albatross-backend/auth" + "github.com/nsfisis/iosdc-2024-albatross-backend/db" +) + +type ApiHandler struct { + q *db.Queries +} + +func NewHandler(queries *db.Queries) *ApiHandler { + return &ApiHandler{ + q: queries, + } +} + +func (h *ApiHandler) PostApiLogin(ctx context.Context, request PostApiLoginRequestObject) (PostApiLoginResponseObject, error) { + username := request.Body.Username + password := request.Body.Password + userId, err := auth.Login(ctx, h.q, username, password) + if err != nil { + return PostApiLogin401JSONResponse{ + Message: "Invalid username or password", + }, echo.NewHTTPError(http.StatusUnauthorized, "Invalid username or password") + } + + user, err := h.q.GetUserById(ctx, int32(userId)) + if err != nil { + return PostApiLogin401JSONResponse{ + Message: "Invalid username or password", + }, echo.NewHTTPError(http.StatusUnauthorized, "Invalid username or password") + } + + jwt, err := auth.NewJWT(&user) + if err != nil { + // TODO + return PostApiLogin401JSONResponse{ + Message: "Internal Server Error", + }, echo.NewHTTPError(http.StatusInternalServerError, "Internal Server Error") + } + + return PostApiLogin200JSONResponse{ + Token: jwt, + }, nil +} + +func _assertJwtPayloadIsCompatibleWithJWTClaims() { + var c auth.JWTClaims + var p JwtPayload + p.UserId = float32(c.UserID) + p.Username = c.Username + p.DisplayUsername = c.DisplayUsername + p.IconPath = c.IconPath + p.IsAdmin = c.IsAdmin + _ = p +} |
