diff --git a/.forgejo/workflows/build-and-deploy.yml b/.forgejo/workflows/build-and-deploy.yml index e7d0a83..2bc0106 100644 --- a/.forgejo/workflows/build-and-deploy.yml +++ b/.forgejo/workflows/build-and-deploy.yml @@ -27,7 +27,7 @@ jobs: cd modules/backend go mod tidy go build -o nyanimedb . - tar -czvf nyanimedb-backend.tar.gz nyanimedb + tar -czvf nyanimedb-backend.tar.gz nyanimedb templates/ - name: Upload built backend to artifactory uses: actions/upload-artifact@v3 @@ -94,7 +94,6 @@ jobs: POSTGRES_PORT: 5432 POSTGRES_VERSION: 18 LOG_LEVEL: ${{ vars.LOG_LEVEL }} - DATABASE_URL: ${{ secrets.DATABASE_URL }} steps: - name: Checkout code diff --git a/Dockerfiles/Dockerfile_backend b/Dockerfiles/Dockerfile_backend index 68c2414..27c9212 100644 --- a/Dockerfiles/Dockerfile_backend +++ b/Dockerfiles/Dockerfile_backend @@ -2,5 +2,6 @@ FROM ubuntu:22.04 WORKDIR /app COPY --chmod=755 modules/backend/nyanimedb /app +COPY modules/backend/templates /app/templates EXPOSE 8080 ENTRYPOINT ["/app/nyanimedb"] \ No newline at end of file diff --git a/Dockerfiles/Dockerfile_frontend b/Dockerfiles/Dockerfile_frontend index 18bc6d7..a7ec893 100644 --- a/Dockerfiles/Dockerfile_frontend +++ b/Dockerfiles/Dockerfile_frontend @@ -1,5 +1,4 @@ FROM nginx:alpine COPY modules/frontend/dist /usr/share/nginx/html -COPY modules/frontend/nginx-default.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/api/api.gen.go b/api/api.gen.go deleted file mode 100644 index 24aebd3..0000000 --- a/api/api.gen.go +++ /dev/null @@ -1,200 +0,0 @@ -// Package oapi provides primitives to interact with the openapi HTTP API. -// -// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.5.0 DO NOT EDIT. -package oapi - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - "time" - - "github.com/gin-gonic/gin" - "github.com/oapi-codegen/runtime" - strictgin "github.com/oapi-codegen/runtime/strictmiddleware/gin" - openapi_types "github.com/oapi-codegen/runtime/types" -) - -// User defines model for User. -type User struct { - // AvatarId ID of the user avatar (references images table) - AvatarId *int64 `json:"avatar_id"` - - // CreationDate Timestamp when the user was created - CreationDate time.Time `json:"creation_date"` - - // DispName Display name - DispName *string `json:"disp_name,omitempty"` - - // Id Unique user ID (primary key) - Id *int64 `json:"id,omitempty"` - - // Mail User email - Mail *openapi_types.Email `json:"mail,omitempty"` - - // Nickname Username (alphanumeric + _ or -) - Nickname string `json:"nickname"` - - // UserDesc User description - UserDesc *string `json:"user_desc,omitempty"` -} - -// GetUsersUserIdParams defines parameters for GetUsersUserId. -type GetUsersUserIdParams struct { - Fields *string `form:"fields,omitempty" json:"fields,omitempty"` -} - -// ServerInterface represents all server handlers. -type ServerInterface interface { - // Get user info - // (GET /users/{user_id}) - GetUsersUserId(c *gin.Context, userId string, params GetUsersUserIdParams) -} - -// ServerInterfaceWrapper converts contexts to parameters. -type ServerInterfaceWrapper struct { - Handler ServerInterface - HandlerMiddlewares []MiddlewareFunc - ErrorHandler func(*gin.Context, error, int) -} - -type MiddlewareFunc func(c *gin.Context) - -// GetUsersUserId operation middleware -func (siw *ServerInterfaceWrapper) GetUsersUserId(c *gin.Context) { - - var err error - - // ------------- Path parameter "user_id" ------------- - var userId string - - err = runtime.BindStyledParameterWithOptions("simple", "user_id", c.Param("user_id"), &userId, runtime.BindStyledParameterOptions{Explode: false, Required: true}) - if err != nil { - siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter user_id: %w", err), http.StatusBadRequest) - return - } - - // Parameter object where we will unmarshal all parameters from the context - var params GetUsersUserIdParams - - // ------------- Optional query parameter "fields" ------------- - - err = runtime.BindQueryParameter("form", true, false, "fields", c.Request.URL.Query(), ¶ms.Fields) - if err != nil { - siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter fields: %w", err), http.StatusBadRequest) - return - } - - for _, middleware := range siw.HandlerMiddlewares { - middleware(c) - if c.IsAborted() { - return - } - } - - siw.Handler.GetUsersUserId(c, userId, params) -} - -// GinServerOptions provides options for the Gin server. -type GinServerOptions struct { - BaseURL string - Middlewares []MiddlewareFunc - ErrorHandler func(*gin.Context, error, int) -} - -// RegisterHandlers creates http.Handler with routing matching OpenAPI spec. -func RegisterHandlers(router gin.IRouter, si ServerInterface) { - RegisterHandlersWithOptions(router, si, GinServerOptions{}) -} - -// RegisterHandlersWithOptions creates http.Handler with additional options -func RegisterHandlersWithOptions(router gin.IRouter, si ServerInterface, options GinServerOptions) { - errorHandler := options.ErrorHandler - if errorHandler == nil { - errorHandler = func(c *gin.Context, err error, statusCode int) { - c.JSON(statusCode, gin.H{"msg": err.Error()}) - } - } - - wrapper := ServerInterfaceWrapper{ - Handler: si, - HandlerMiddlewares: options.Middlewares, - ErrorHandler: errorHandler, - } - - router.GET(options.BaseURL+"/users/:user_id", wrapper.GetUsersUserId) -} - -type GetUsersUserIdRequestObject struct { - UserId string `json:"user_id"` - Params GetUsersUserIdParams -} - -type GetUsersUserIdResponseObject interface { - VisitGetUsersUserIdResponse(w http.ResponseWriter) error -} - -type GetUsersUserId200JSONResponse User - -func (response GetUsersUserId200JSONResponse) VisitGetUsersUserIdResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(200) - - return json.NewEncoder(w).Encode(response) -} - -type GetUsersUserId404Response struct { -} - -func (response GetUsersUserId404Response) VisitGetUsersUserIdResponse(w http.ResponseWriter) error { - w.WriteHeader(404) - return nil -} - -// StrictServerInterface represents all server handlers. -type StrictServerInterface interface { - // Get user info - // (GET /users/{user_id}) - GetUsersUserId(ctx context.Context, request GetUsersUserIdRequestObject) (GetUsersUserIdResponseObject, error) -} - -type StrictHandlerFunc = strictgin.StrictGinHandlerFunc -type StrictMiddlewareFunc = strictgin.StrictGinMiddlewareFunc - -func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface { - return &strictHandler{ssi: ssi, middlewares: middlewares} -} - -type strictHandler struct { - ssi StrictServerInterface - middlewares []StrictMiddlewareFunc -} - -// GetUsersUserId operation middleware -func (sh *strictHandler) GetUsersUserId(ctx *gin.Context, userId string, params GetUsersUserIdParams) { - var request GetUsersUserIdRequestObject - - request.UserId = userId - request.Params = params - - handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { - return sh.ssi.GetUsersUserId(ctx, request.(GetUsersUserIdRequestObject)) - } - for _, middleware := range sh.middlewares { - handler = middleware(handler, "GetUsersUserId") - } - - response, err := handler(ctx, request) - - if err != nil { - ctx.Error(err) - ctx.Status(http.StatusInternalServerError) - } else if validResponse, ok := response.(GetUsersUserIdResponseObject); ok { - if err := validResponse.VisitGetUsersUserIdResponse(ctx.Writer); err != nil { - ctx.Error(err) - } - } else if response != nil { - ctx.Error(fmt.Errorf("unexpected response type: %T", response)) - } -} diff --git a/api/gen.go b/api/gen.go new file mode 100644 index 0000000..2dcb886 --- /dev/null +++ b/api/gen.go @@ -0,0 +1,1958 @@ +// Package api provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.5.0 DO NOT EDIT. +package api + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + + "github.com/gin-gonic/gin" + "github.com/oapi-codegen/runtime" + strictgin "github.com/oapi-codegen/runtime/strictmiddleware/gin" +) + +// Review defines model for Review. +type Review map[string]interface{} + +// Tag defines model for Tag. +type Tag map[string]interface{} + +// Title defines model for Title. +type Title map[string]interface{} + +// User defines model for User. +type User map[string]interface{} + +// UserTitle defines model for UserTitle. +type UserTitle map[string]interface{} + +// GetMediaParams defines parameters for GetMedia. +type GetMediaParams struct { + ImageId string `form:"image_id" json:"image_id"` +} + +// GetTagsParams defines parameters for GetTags. +type GetTagsParams struct { + Limit *int `form:"limit,omitempty" json:"limit,omitempty"` + Offset *int `form:"offset,omitempty" json:"offset,omitempty"` + Fields *string `form:"fields,omitempty" json:"fields,omitempty"` +} + +// GetTitleParams defines parameters for GetTitle. +type GetTitleParams struct { + Query *string `form:"query,omitempty" json:"query,omitempty"` + Limit *int `form:"limit,omitempty" json:"limit,omitempty"` + Offset *int `form:"offset,omitempty" json:"offset,omitempty"` + Fields *string `form:"fields,omitempty" json:"fields,omitempty"` +} + +// GetTitleTitleIdParams defines parameters for GetTitleTitleId. +type GetTitleTitleIdParams struct { + Fields *string `form:"fields,omitempty" json:"fields,omitempty"` +} + +// GetTitleTitleIdReviewsParams defines parameters for GetTitleTitleIdReviews. +type GetTitleTitleIdReviewsParams struct { + Limit *int `form:"limit,omitempty" json:"limit,omitempty"` + Offset *int `form:"offset,omitempty" json:"offset,omitempty"` +} + +// GetUsersParams defines parameters for GetUsers. +type GetUsersParams struct { + Query *string `form:"query,omitempty" json:"query,omitempty"` + Fields *string `form:"fields,omitempty" json:"fields,omitempty"` +} + +// GetUsersUserIdParams defines parameters for GetUsersUserId. +type GetUsersUserIdParams struct { + Fields *string `form:"fields,omitempty" json:"fields,omitempty"` +} + +// GetUsersUserIdReviewsParams defines parameters for GetUsersUserIdReviews. +type GetUsersUserIdReviewsParams struct { + Limit *int `form:"limit,omitempty" json:"limit,omitempty"` + Offset *int `form:"offset,omitempty" json:"offset,omitempty"` +} + +// DeleteUsersUserIdTitlesParams defines parameters for DeleteUsersUserIdTitles. +type DeleteUsersUserIdTitlesParams struct { + TitleId *string `form:"title_id,omitempty" json:"title_id,omitempty"` +} + +// GetUsersUserIdTitlesParams defines parameters for GetUsersUserIdTitles. +type GetUsersUserIdTitlesParams struct { + Query *string `form:"query,omitempty" json:"query,omitempty"` + Limit *int `form:"limit,omitempty" json:"limit,omitempty"` + Offset *int `form:"offset,omitempty" json:"offset,omitempty"` + Fields *string `form:"fields,omitempty" json:"fields,omitempty"` +} + +// PostUsersUserIdTitlesJSONBody defines parameters for PostUsersUserIdTitles. +type PostUsersUserIdTitlesJSONBody struct { + Status *string `json:"status,omitempty"` + TitleId *string `json:"title_id,omitempty"` +} + +// PostReviewsJSONRequestBody defines body for PostReviews for application/json ContentType. +type PostReviewsJSONRequestBody = Review + +// PatchReviewsReviewIdJSONRequestBody defines body for PatchReviewsReviewId for application/json ContentType. +type PatchReviewsReviewIdJSONRequestBody = Review + +// PatchTitleTitleIdJSONRequestBody defines body for PatchTitleTitleId for application/json ContentType. +type PatchTitleTitleIdJSONRequestBody = Title + +// PostUsersJSONRequestBody defines body for PostUsers for application/json ContentType. +type PostUsersJSONRequestBody = User + +// PatchUsersUserIdJSONRequestBody defines body for PatchUsersUserId for application/json ContentType. +type PatchUsersUserIdJSONRequestBody = User + +// PatchUsersUserIdTitlesJSONRequestBody defines body for PatchUsersUserIdTitles for application/json ContentType. +type PatchUsersUserIdTitlesJSONRequestBody = UserTitle + +// PostUsersUserIdTitlesJSONRequestBody defines body for PostUsersUserIdTitles for application/json ContentType. +type PostUsersUserIdTitlesJSONRequestBody PostUsersUserIdTitlesJSONBody + +// ServerInterface represents all server handlers. +type ServerInterface interface { + // Get image path + // (GET /media) + GetMedia(c *gin.Context, params GetMediaParams) + // Upload image + // (POST /media) + PostMedia(c *gin.Context) + // Add review + // (POST /reviews) + PostReviews(c *gin.Context) + // Delete review + // (DELETE /reviews/{review_id}) + DeleteReviewsReviewId(c *gin.Context, reviewId string) + // Update review + // (PATCH /reviews/{review_id}) + PatchReviewsReviewId(c *gin.Context, reviewId string) + // Get tags + // (GET /tags) + GetTags(c *gin.Context, params GetTagsParams) + // Get titles + // (GET /title) + GetTitle(c *gin.Context, params GetTitleParams) + // Get title description + // (GET /title/{title_id}) + GetTitleTitleId(c *gin.Context, titleId string, params GetTitleTitleIdParams) + // Update title info + // (PATCH /title/{title_id}) + PatchTitleTitleId(c *gin.Context, titleId string) + // Get title reviews + // (GET /title/{title_id}/reviews) + GetTitleTitleIdReviews(c *gin.Context, titleId string, params GetTitleTitleIdReviewsParams) + // Search user + // (GET /users) + GetUsers(c *gin.Context, params GetUsersParams) + // Add new user + // (POST /users) + PostUsers(c *gin.Context) + // Delete user + // (DELETE /users/{user_id}) + DeleteUsersUserId(c *gin.Context, userId string) + // Get user info + // (GET /users/{user_id}) + GetUsersUserId(c *gin.Context, userId string, params GetUsersUserIdParams) + // Update user + // (PATCH /users/{user_id}) + PatchUsersUserId(c *gin.Context, userId string) + // Get user reviews + // (GET /users/{user_id}/reviews) + GetUsersUserIdReviews(c *gin.Context, userId string, params GetUsersUserIdReviewsParams) + // Delete user title + // (DELETE /users/{user_id}/titles) + DeleteUsersUserIdTitles(c *gin.Context, userId string, params DeleteUsersUserIdTitlesParams) + // Get user titles + // (GET /users/{user_id}/titles) + GetUsersUserIdTitles(c *gin.Context, userId string, params GetUsersUserIdTitlesParams) + // Update user title + // (PATCH /users/{user_id}/titles) + PatchUsersUserIdTitles(c *gin.Context, userId string) + // Add user title + // (POST /users/{user_id}/titles) + PostUsersUserIdTitles(c *gin.Context, userId string) +} + +// ServerInterfaceWrapper converts contexts to parameters. +type ServerInterfaceWrapper struct { + Handler ServerInterface + HandlerMiddlewares []MiddlewareFunc + ErrorHandler func(*gin.Context, error, int) +} + +type MiddlewareFunc func(c *gin.Context) + +// GetMedia operation middleware +func (siw *ServerInterfaceWrapper) GetMedia(c *gin.Context) { + + var err error + + // Parameter object where we will unmarshal all parameters from the context + var params GetMediaParams + + // ------------- Required query parameter "image_id" ------------- + + if paramValue := c.Query("image_id"); paramValue != "" { + + } else { + siw.ErrorHandler(c, fmt.Errorf("Query argument image_id is required, but not found"), http.StatusBadRequest) + return + } + + err = runtime.BindQueryParameter("form", true, true, "image_id", c.Request.URL.Query(), ¶ms.ImageId) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter image_id: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.GetMedia(c, params) +} + +// PostMedia operation middleware +func (siw *ServerInterfaceWrapper) PostMedia(c *gin.Context) { + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.PostMedia(c) +} + +// PostReviews operation middleware +func (siw *ServerInterfaceWrapper) PostReviews(c *gin.Context) { + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.PostReviews(c) +} + +// DeleteReviewsReviewId operation middleware +func (siw *ServerInterfaceWrapper) DeleteReviewsReviewId(c *gin.Context) { + + var err error + + // ------------- Path parameter "review_id" ------------- + var reviewId string + + err = runtime.BindStyledParameterWithOptions("simple", "review_id", c.Param("review_id"), &reviewId, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter review_id: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.DeleteReviewsReviewId(c, reviewId) +} + +// PatchReviewsReviewId operation middleware +func (siw *ServerInterfaceWrapper) PatchReviewsReviewId(c *gin.Context) { + + var err error + + // ------------- Path parameter "review_id" ------------- + var reviewId string + + err = runtime.BindStyledParameterWithOptions("simple", "review_id", c.Param("review_id"), &reviewId, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter review_id: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.PatchReviewsReviewId(c, reviewId) +} + +// GetTags operation middleware +func (siw *ServerInterfaceWrapper) GetTags(c *gin.Context) { + + var err error + + // Parameter object where we will unmarshal all parameters from the context + var params GetTagsParams + + // ------------- Optional query parameter "limit" ------------- + + err = runtime.BindQueryParameter("form", true, false, "limit", c.Request.URL.Query(), ¶ms.Limit) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter limit: %w", err), http.StatusBadRequest) + return + } + + // ------------- Optional query parameter "offset" ------------- + + err = runtime.BindQueryParameter("form", true, false, "offset", c.Request.URL.Query(), ¶ms.Offset) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter offset: %w", err), http.StatusBadRequest) + return + } + + // ------------- Optional query parameter "fields" ------------- + + err = runtime.BindQueryParameter("form", true, false, "fields", c.Request.URL.Query(), ¶ms.Fields) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter fields: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.GetTags(c, params) +} + +// GetTitle operation middleware +func (siw *ServerInterfaceWrapper) GetTitle(c *gin.Context) { + + var err error + + // Parameter object where we will unmarshal all parameters from the context + var params GetTitleParams + + // ------------- Optional query parameter "query" ------------- + + err = runtime.BindQueryParameter("form", true, false, "query", c.Request.URL.Query(), ¶ms.Query) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter query: %w", err), http.StatusBadRequest) + return + } + + // ------------- Optional query parameter "limit" ------------- + + err = runtime.BindQueryParameter("form", true, false, "limit", c.Request.URL.Query(), ¶ms.Limit) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter limit: %w", err), http.StatusBadRequest) + return + } + + // ------------- Optional query parameter "offset" ------------- + + err = runtime.BindQueryParameter("form", true, false, "offset", c.Request.URL.Query(), ¶ms.Offset) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter offset: %w", err), http.StatusBadRequest) + return + } + + // ------------- Optional query parameter "fields" ------------- + + err = runtime.BindQueryParameter("form", true, false, "fields", c.Request.URL.Query(), ¶ms.Fields) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter fields: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.GetTitle(c, params) +} + +// GetTitleTitleId operation middleware +func (siw *ServerInterfaceWrapper) GetTitleTitleId(c *gin.Context) { + + var err error + + // ------------- Path parameter "title_id" ------------- + var titleId string + + err = runtime.BindStyledParameterWithOptions("simple", "title_id", c.Param("title_id"), &titleId, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter title_id: %w", err), http.StatusBadRequest) + return + } + + // Parameter object where we will unmarshal all parameters from the context + var params GetTitleTitleIdParams + + // ------------- Optional query parameter "fields" ------------- + + err = runtime.BindQueryParameter("form", true, false, "fields", c.Request.URL.Query(), ¶ms.Fields) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter fields: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.GetTitleTitleId(c, titleId, params) +} + +// PatchTitleTitleId operation middleware +func (siw *ServerInterfaceWrapper) PatchTitleTitleId(c *gin.Context) { + + var err error + + // ------------- Path parameter "title_id" ------------- + var titleId string + + err = runtime.BindStyledParameterWithOptions("simple", "title_id", c.Param("title_id"), &titleId, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter title_id: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.PatchTitleTitleId(c, titleId) +} + +// GetTitleTitleIdReviews operation middleware +func (siw *ServerInterfaceWrapper) GetTitleTitleIdReviews(c *gin.Context) { + + var err error + + // ------------- Path parameter "title_id" ------------- + var titleId string + + err = runtime.BindStyledParameterWithOptions("simple", "title_id", c.Param("title_id"), &titleId, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter title_id: %w", err), http.StatusBadRequest) + return + } + + // Parameter object where we will unmarshal all parameters from the context + var params GetTitleTitleIdReviewsParams + + // ------------- Optional query parameter "limit" ------------- + + err = runtime.BindQueryParameter("form", true, false, "limit", c.Request.URL.Query(), ¶ms.Limit) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter limit: %w", err), http.StatusBadRequest) + return + } + + // ------------- Optional query parameter "offset" ------------- + + err = runtime.BindQueryParameter("form", true, false, "offset", c.Request.URL.Query(), ¶ms.Offset) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter offset: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.GetTitleTitleIdReviews(c, titleId, params) +} + +// GetUsers operation middleware +func (siw *ServerInterfaceWrapper) GetUsers(c *gin.Context) { + + var err error + + // Parameter object where we will unmarshal all parameters from the context + var params GetUsersParams + + // ------------- Optional query parameter "query" ------------- + + err = runtime.BindQueryParameter("form", true, false, "query", c.Request.URL.Query(), ¶ms.Query) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter query: %w", err), http.StatusBadRequest) + return + } + + // ------------- Optional query parameter "fields" ------------- + + err = runtime.BindQueryParameter("form", true, false, "fields", c.Request.URL.Query(), ¶ms.Fields) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter fields: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.GetUsers(c, params) +} + +// PostUsers operation middleware +func (siw *ServerInterfaceWrapper) PostUsers(c *gin.Context) { + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.PostUsers(c) +} + +// DeleteUsersUserId operation middleware +func (siw *ServerInterfaceWrapper) DeleteUsersUserId(c *gin.Context) { + + var err error + + // ------------- Path parameter "user_id" ------------- + var userId string + + err = runtime.BindStyledParameterWithOptions("simple", "user_id", c.Param("user_id"), &userId, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter user_id: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.DeleteUsersUserId(c, userId) +} + +// GetUsersUserId operation middleware +func (siw *ServerInterfaceWrapper) GetUsersUserId(c *gin.Context) { + + var err error + + // ------------- Path parameter "user_id" ------------- + var userId string + + err = runtime.BindStyledParameterWithOptions("simple", "user_id", c.Param("user_id"), &userId, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter user_id: %w", err), http.StatusBadRequest) + return + } + + // Parameter object where we will unmarshal all parameters from the context + var params GetUsersUserIdParams + + // ------------- Optional query parameter "fields" ------------- + + err = runtime.BindQueryParameter("form", true, false, "fields", c.Request.URL.Query(), ¶ms.Fields) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter fields: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.GetUsersUserId(c, userId, params) +} + +// PatchUsersUserId operation middleware +func (siw *ServerInterfaceWrapper) PatchUsersUserId(c *gin.Context) { + + var err error + + // ------------- Path parameter "user_id" ------------- + var userId string + + err = runtime.BindStyledParameterWithOptions("simple", "user_id", c.Param("user_id"), &userId, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter user_id: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.PatchUsersUserId(c, userId) +} + +// GetUsersUserIdReviews operation middleware +func (siw *ServerInterfaceWrapper) GetUsersUserIdReviews(c *gin.Context) { + + var err error + + // ------------- Path parameter "user_id" ------------- + var userId string + + err = runtime.BindStyledParameterWithOptions("simple", "user_id", c.Param("user_id"), &userId, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter user_id: %w", err), http.StatusBadRequest) + return + } + + // Parameter object where we will unmarshal all parameters from the context + var params GetUsersUserIdReviewsParams + + // ------------- Optional query parameter "limit" ------------- + + err = runtime.BindQueryParameter("form", true, false, "limit", c.Request.URL.Query(), ¶ms.Limit) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter limit: %w", err), http.StatusBadRequest) + return + } + + // ------------- Optional query parameter "offset" ------------- + + err = runtime.BindQueryParameter("form", true, false, "offset", c.Request.URL.Query(), ¶ms.Offset) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter offset: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.GetUsersUserIdReviews(c, userId, params) +} + +// DeleteUsersUserIdTitles operation middleware +func (siw *ServerInterfaceWrapper) DeleteUsersUserIdTitles(c *gin.Context) { + + var err error + + // ------------- Path parameter "user_id" ------------- + var userId string + + err = runtime.BindStyledParameterWithOptions("simple", "user_id", c.Param("user_id"), &userId, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter user_id: %w", err), http.StatusBadRequest) + return + } + + // Parameter object where we will unmarshal all parameters from the context + var params DeleteUsersUserIdTitlesParams + + // ------------- Optional query parameter "title_id" ------------- + + err = runtime.BindQueryParameter("form", true, false, "title_id", c.Request.URL.Query(), ¶ms.TitleId) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter title_id: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.DeleteUsersUserIdTitles(c, userId, params) +} + +// GetUsersUserIdTitles operation middleware +func (siw *ServerInterfaceWrapper) GetUsersUserIdTitles(c *gin.Context) { + + var err error + + // ------------- Path parameter "user_id" ------------- + var userId string + + err = runtime.BindStyledParameterWithOptions("simple", "user_id", c.Param("user_id"), &userId, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter user_id: %w", err), http.StatusBadRequest) + return + } + + // Parameter object where we will unmarshal all parameters from the context + var params GetUsersUserIdTitlesParams + + // ------------- Optional query parameter "query" ------------- + + err = runtime.BindQueryParameter("form", true, false, "query", c.Request.URL.Query(), ¶ms.Query) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter query: %w", err), http.StatusBadRequest) + return + } + + // ------------- Optional query parameter "limit" ------------- + + err = runtime.BindQueryParameter("form", true, false, "limit", c.Request.URL.Query(), ¶ms.Limit) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter limit: %w", err), http.StatusBadRequest) + return + } + + // ------------- Optional query parameter "offset" ------------- + + err = runtime.BindQueryParameter("form", true, false, "offset", c.Request.URL.Query(), ¶ms.Offset) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter offset: %w", err), http.StatusBadRequest) + return + } + + // ------------- Optional query parameter "fields" ------------- + + err = runtime.BindQueryParameter("form", true, false, "fields", c.Request.URL.Query(), ¶ms.Fields) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter fields: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.GetUsersUserIdTitles(c, userId, params) +} + +// PatchUsersUserIdTitles operation middleware +func (siw *ServerInterfaceWrapper) PatchUsersUserIdTitles(c *gin.Context) { + + var err error + + // ------------- Path parameter "user_id" ------------- + var userId string + + err = runtime.BindStyledParameterWithOptions("simple", "user_id", c.Param("user_id"), &userId, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter user_id: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.PatchUsersUserIdTitles(c, userId) +} + +// PostUsersUserIdTitles operation middleware +func (siw *ServerInterfaceWrapper) PostUsersUserIdTitles(c *gin.Context) { + + var err error + + // ------------- Path parameter "user_id" ------------- + var userId string + + err = runtime.BindStyledParameterWithOptions("simple", "user_id", c.Param("user_id"), &userId, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter user_id: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.PostUsersUserIdTitles(c, userId) +} + +// GinServerOptions provides options for the Gin server. +type GinServerOptions struct { + BaseURL string + Middlewares []MiddlewareFunc + ErrorHandler func(*gin.Context, error, int) +} + +// RegisterHandlers creates http.Handler with routing matching OpenAPI spec. +func RegisterHandlers(router gin.IRouter, si ServerInterface) { + RegisterHandlersWithOptions(router, si, GinServerOptions{}) +} + +// RegisterHandlersWithOptions creates http.Handler with additional options +func RegisterHandlersWithOptions(router gin.IRouter, si ServerInterface, options GinServerOptions) { + errorHandler := options.ErrorHandler + if errorHandler == nil { + errorHandler = func(c *gin.Context, err error, statusCode int) { + c.JSON(statusCode, gin.H{"msg": err.Error()}) + } + } + + wrapper := ServerInterfaceWrapper{ + Handler: si, + HandlerMiddlewares: options.Middlewares, + ErrorHandler: errorHandler, + } + + router.GET(options.BaseURL+"/media", wrapper.GetMedia) + router.POST(options.BaseURL+"/media", wrapper.PostMedia) + router.POST(options.BaseURL+"/reviews", wrapper.PostReviews) + router.DELETE(options.BaseURL+"/reviews/:review_id", wrapper.DeleteReviewsReviewId) + router.PATCH(options.BaseURL+"/reviews/:review_id", wrapper.PatchReviewsReviewId) + router.GET(options.BaseURL+"/tags", wrapper.GetTags) + router.GET(options.BaseURL+"/title", wrapper.GetTitle) + router.GET(options.BaseURL+"/title/:title_id", wrapper.GetTitleTitleId) + router.PATCH(options.BaseURL+"/title/:title_id", wrapper.PatchTitleTitleId) + router.GET(options.BaseURL+"/title/:title_id/reviews", wrapper.GetTitleTitleIdReviews) + router.GET(options.BaseURL+"/users", wrapper.GetUsers) + router.POST(options.BaseURL+"/users", wrapper.PostUsers) + router.DELETE(options.BaseURL+"/users/:user_id", wrapper.DeleteUsersUserId) + router.GET(options.BaseURL+"/users/:user_id", wrapper.GetUsersUserId) + router.PATCH(options.BaseURL+"/users/:user_id", wrapper.PatchUsersUserId) + router.GET(options.BaseURL+"/users/:user_id/reviews", wrapper.GetUsersUserIdReviews) + router.DELETE(options.BaseURL+"/users/:user_id/titles", wrapper.DeleteUsersUserIdTitles) + router.GET(options.BaseURL+"/users/:user_id/titles", wrapper.GetUsersUserIdTitles) + router.PATCH(options.BaseURL+"/users/:user_id/titles", wrapper.PatchUsersUserIdTitles) + router.POST(options.BaseURL+"/users/:user_id/titles", wrapper.PostUsersUserIdTitles) +} + +type GetMediaRequestObject struct { + Params GetMediaParams +} + +type GetMediaResponseObject interface { + VisitGetMediaResponse(w http.ResponseWriter) error +} + +type GetMedia200JSONResponse struct { + Error *string `json:"error,omitempty"` + ImagePath *string `json:"image_path,omitempty"` + Success *bool `json:"success,omitempty"` +} + +func (response GetMedia200JSONResponse) VisitGetMediaResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type PostMediaRequestObject struct { +} + +type PostMediaResponseObject interface { + VisitPostMediaResponse(w http.ResponseWriter) error +} + +type PostMedia200JSONResponse struct { + Error *string `json:"error,omitempty"` + ImageId *string `json:"image_id,omitempty"` + Success *bool `json:"success,omitempty"` +} + +func (response PostMedia200JSONResponse) VisitPostMediaResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type PostReviewsRequestObject struct { + Body *PostReviewsJSONRequestBody +} + +type PostReviewsResponseObject interface { + VisitPostReviewsResponse(w http.ResponseWriter) error +} + +type PostReviews200JSONResponse struct { + Error *string `json:"error,omitempty"` + Success *bool `json:"success,omitempty"` +} + +func (response PostReviews200JSONResponse) VisitPostReviewsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type DeleteReviewsReviewIdRequestObject struct { + ReviewId string `json:"review_id"` +} + +type DeleteReviewsReviewIdResponseObject interface { + VisitDeleteReviewsReviewIdResponse(w http.ResponseWriter) error +} + +type DeleteReviewsReviewId200JSONResponse struct { + Error *string `json:"error,omitempty"` + Success *bool `json:"success,omitempty"` +} + +func (response DeleteReviewsReviewId200JSONResponse) VisitDeleteReviewsReviewIdResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type PatchReviewsReviewIdRequestObject struct { + ReviewId string `json:"review_id"` + Body *PatchReviewsReviewIdJSONRequestBody +} + +type PatchReviewsReviewIdResponseObject interface { + VisitPatchReviewsReviewIdResponse(w http.ResponseWriter) error +} + +type PatchReviewsReviewId200JSONResponse struct { + Error *string `json:"error,omitempty"` + Success *bool `json:"success,omitempty"` +} + +func (response PatchReviewsReviewId200JSONResponse) VisitPatchReviewsReviewIdResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetTagsRequestObject struct { + Params GetTagsParams +} + +type GetTagsResponseObject interface { + VisitGetTagsResponse(w http.ResponseWriter) error +} + +type GetTags200JSONResponse []Tag + +func (response GetTags200JSONResponse) VisitGetTagsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetTitleRequestObject struct { + Params GetTitleParams +} + +type GetTitleResponseObject interface { + VisitGetTitleResponse(w http.ResponseWriter) error +} + +type GetTitle200JSONResponse []Title + +func (response GetTitle200JSONResponse) VisitGetTitleResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetTitle204Response struct { +} + +func (response GetTitle204Response) VisitGetTitleResponse(w http.ResponseWriter) error { + w.WriteHeader(204) + return nil +} + +type GetTitleTitleIdRequestObject struct { + TitleId string `json:"title_id"` + Params GetTitleTitleIdParams +} + +type GetTitleTitleIdResponseObject interface { + VisitGetTitleTitleIdResponse(w http.ResponseWriter) error +} + +type GetTitleTitleId200JSONResponse Title + +func (response GetTitleTitleId200JSONResponse) VisitGetTitleTitleIdResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetTitleTitleId404Response struct { +} + +func (response GetTitleTitleId404Response) VisitGetTitleTitleIdResponse(w http.ResponseWriter) error { + w.WriteHeader(404) + return nil +} + +type PatchTitleTitleIdRequestObject struct { + TitleId string `json:"title_id"` + Body *PatchTitleTitleIdJSONRequestBody +} + +type PatchTitleTitleIdResponseObject interface { + VisitPatchTitleTitleIdResponse(w http.ResponseWriter) error +} + +type PatchTitleTitleId200JSONResponse struct { + Error *string `json:"error,omitempty"` + Success *bool `json:"success,omitempty"` + UserJson *User `json:"user_json,omitempty"` +} + +func (response PatchTitleTitleId200JSONResponse) VisitPatchTitleTitleIdResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetTitleTitleIdReviewsRequestObject struct { + TitleId string `json:"title_id"` + Params GetTitleTitleIdReviewsParams +} + +type GetTitleTitleIdReviewsResponseObject interface { + VisitGetTitleTitleIdReviewsResponse(w http.ResponseWriter) error +} + +type GetTitleTitleIdReviews200JSONResponse []Review + +func (response GetTitleTitleIdReviews200JSONResponse) VisitGetTitleTitleIdReviewsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetTitleTitleIdReviews204Response struct { +} + +func (response GetTitleTitleIdReviews204Response) VisitGetTitleTitleIdReviewsResponse(w http.ResponseWriter) error { + w.WriteHeader(204) + return nil +} + +type GetUsersRequestObject struct { + Params GetUsersParams +} + +type GetUsersResponseObject interface { + VisitGetUsersResponse(w http.ResponseWriter) error +} + +type GetUsers200JSONResponse []User + +func (response GetUsers200JSONResponse) VisitGetUsersResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type PostUsersRequestObject struct { + Body *PostUsersJSONRequestBody +} + +type PostUsersResponseObject interface { + VisitPostUsersResponse(w http.ResponseWriter) error +} + +type PostUsers200JSONResponse struct { + Error *string `json:"error,omitempty"` + Success *bool `json:"success,omitempty"` + UserJson *User `json:"user_json,omitempty"` +} + +func (response PostUsers200JSONResponse) VisitPostUsersResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type DeleteUsersUserIdRequestObject struct { + UserId string `json:"user_id"` +} + +type DeleteUsersUserIdResponseObject interface { + VisitDeleteUsersUserIdResponse(w http.ResponseWriter) error +} + +type DeleteUsersUserId200JSONResponse struct { + Error *string `json:"error,omitempty"` + Success *bool `json:"success,omitempty"` +} + +func (response DeleteUsersUserId200JSONResponse) VisitDeleteUsersUserIdResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetUsersUserIdRequestObject struct { + UserId string `json:"user_id"` + Params GetUsersUserIdParams +} + +type GetUsersUserIdResponseObject interface { + VisitGetUsersUserIdResponse(w http.ResponseWriter) error +} + +type GetUsersUserId200JSONResponse User + +func (response GetUsersUserId200JSONResponse) VisitGetUsersUserIdResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetUsersUserId404Response struct { +} + +func (response GetUsersUserId404Response) VisitGetUsersUserIdResponse(w http.ResponseWriter) error { + w.WriteHeader(404) + return nil +} + +type PatchUsersUserIdRequestObject struct { + UserId string `json:"user_id"` + Body *PatchUsersUserIdJSONRequestBody +} + +type PatchUsersUserIdResponseObject interface { + VisitPatchUsersUserIdResponse(w http.ResponseWriter) error +} + +type PatchUsersUserId200JSONResponse struct { + Error *string `json:"error,omitempty"` + Success *bool `json:"success,omitempty"` +} + +func (response PatchUsersUserId200JSONResponse) VisitPatchUsersUserIdResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetUsersUserIdReviewsRequestObject struct { + UserId string `json:"user_id"` + Params GetUsersUserIdReviewsParams +} + +type GetUsersUserIdReviewsResponseObject interface { + VisitGetUsersUserIdReviewsResponse(w http.ResponseWriter) error +} + +type GetUsersUserIdReviews200JSONResponse []Review + +func (response GetUsersUserIdReviews200JSONResponse) VisitGetUsersUserIdReviewsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type DeleteUsersUserIdTitlesRequestObject struct { + UserId string `json:"user_id"` + Params DeleteUsersUserIdTitlesParams +} + +type DeleteUsersUserIdTitlesResponseObject interface { + VisitDeleteUsersUserIdTitlesResponse(w http.ResponseWriter) error +} + +type DeleteUsersUserIdTitles200JSONResponse struct { + Error *string `json:"error,omitempty"` + Success *bool `json:"success,omitempty"` +} + +func (response DeleteUsersUserIdTitles200JSONResponse) VisitDeleteUsersUserIdTitlesResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetUsersUserIdTitlesRequestObject struct { + UserId string `json:"user_id"` + Params GetUsersUserIdTitlesParams +} + +type GetUsersUserIdTitlesResponseObject interface { + VisitGetUsersUserIdTitlesResponse(w http.ResponseWriter) error +} + +type GetUsersUserIdTitles200JSONResponse []UserTitle + +func (response GetUsersUserIdTitles200JSONResponse) VisitGetUsersUserIdTitlesResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetUsersUserIdTitles204Response struct { +} + +func (response GetUsersUserIdTitles204Response) VisitGetUsersUserIdTitlesResponse(w http.ResponseWriter) error { + w.WriteHeader(204) + return nil +} + +type PatchUsersUserIdTitlesRequestObject struct { + UserId string `json:"user_id"` + Body *PatchUsersUserIdTitlesJSONRequestBody +} + +type PatchUsersUserIdTitlesResponseObject interface { + VisitPatchUsersUserIdTitlesResponse(w http.ResponseWriter) error +} + +type PatchUsersUserIdTitles200JSONResponse struct { + Error *string `json:"error,omitempty"` + Success *bool `json:"success,omitempty"` +} + +func (response PatchUsersUserIdTitles200JSONResponse) VisitPatchUsersUserIdTitlesResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type PostUsersUserIdTitlesRequestObject struct { + UserId string `json:"user_id"` + Body *PostUsersUserIdTitlesJSONRequestBody +} + +type PostUsersUserIdTitlesResponseObject interface { + VisitPostUsersUserIdTitlesResponse(w http.ResponseWriter) error +} + +type PostUsersUserIdTitles200JSONResponse struct { + Error *string `json:"error,omitempty"` + Success *bool `json:"success,omitempty"` +} + +func (response PostUsersUserIdTitles200JSONResponse) VisitPostUsersUserIdTitlesResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +// StrictServerInterface represents all server handlers. +type StrictServerInterface interface { + // Get image path + // (GET /media) + GetMedia(ctx context.Context, request GetMediaRequestObject) (GetMediaResponseObject, error) + // Upload image + // (POST /media) + PostMedia(ctx context.Context, request PostMediaRequestObject) (PostMediaResponseObject, error) + // Add review + // (POST /reviews) + PostReviews(ctx context.Context, request PostReviewsRequestObject) (PostReviewsResponseObject, error) + // Delete review + // (DELETE /reviews/{review_id}) + DeleteReviewsReviewId(ctx context.Context, request DeleteReviewsReviewIdRequestObject) (DeleteReviewsReviewIdResponseObject, error) + // Update review + // (PATCH /reviews/{review_id}) + PatchReviewsReviewId(ctx context.Context, request PatchReviewsReviewIdRequestObject) (PatchReviewsReviewIdResponseObject, error) + // Get tags + // (GET /tags) + GetTags(ctx context.Context, request GetTagsRequestObject) (GetTagsResponseObject, error) + // Get titles + // (GET /title) + GetTitle(ctx context.Context, request GetTitleRequestObject) (GetTitleResponseObject, error) + // Get title description + // (GET /title/{title_id}) + GetTitleTitleId(ctx context.Context, request GetTitleTitleIdRequestObject) (GetTitleTitleIdResponseObject, error) + // Update title info + // (PATCH /title/{title_id}) + PatchTitleTitleId(ctx context.Context, request PatchTitleTitleIdRequestObject) (PatchTitleTitleIdResponseObject, error) + // Get title reviews + // (GET /title/{title_id}/reviews) + GetTitleTitleIdReviews(ctx context.Context, request GetTitleTitleIdReviewsRequestObject) (GetTitleTitleIdReviewsResponseObject, error) + // Search user + // (GET /users) + GetUsers(ctx context.Context, request GetUsersRequestObject) (GetUsersResponseObject, error) + // Add new user + // (POST /users) + PostUsers(ctx context.Context, request PostUsersRequestObject) (PostUsersResponseObject, error) + // Delete user + // (DELETE /users/{user_id}) + DeleteUsersUserId(ctx context.Context, request DeleteUsersUserIdRequestObject) (DeleteUsersUserIdResponseObject, error) + // Get user info + // (GET /users/{user_id}) + GetUsersUserId(ctx context.Context, request GetUsersUserIdRequestObject) (GetUsersUserIdResponseObject, error) + // Update user + // (PATCH /users/{user_id}) + PatchUsersUserId(ctx context.Context, request PatchUsersUserIdRequestObject) (PatchUsersUserIdResponseObject, error) + // Get user reviews + // (GET /users/{user_id}/reviews) + GetUsersUserIdReviews(ctx context.Context, request GetUsersUserIdReviewsRequestObject) (GetUsersUserIdReviewsResponseObject, error) + // Delete user title + // (DELETE /users/{user_id}/titles) + DeleteUsersUserIdTitles(ctx context.Context, request DeleteUsersUserIdTitlesRequestObject) (DeleteUsersUserIdTitlesResponseObject, error) + // Get user titles + // (GET /users/{user_id}/titles) + GetUsersUserIdTitles(ctx context.Context, request GetUsersUserIdTitlesRequestObject) (GetUsersUserIdTitlesResponseObject, error) + // Update user title + // (PATCH /users/{user_id}/titles) + PatchUsersUserIdTitles(ctx context.Context, request PatchUsersUserIdTitlesRequestObject) (PatchUsersUserIdTitlesResponseObject, error) + // Add user title + // (POST /users/{user_id}/titles) + PostUsersUserIdTitles(ctx context.Context, request PostUsersUserIdTitlesRequestObject) (PostUsersUserIdTitlesResponseObject, error) +} + +type StrictHandlerFunc = strictgin.StrictGinHandlerFunc +type StrictMiddlewareFunc = strictgin.StrictGinMiddlewareFunc + +func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface { + return &strictHandler{ssi: ssi, middlewares: middlewares} +} + +type strictHandler struct { + ssi StrictServerInterface + middlewares []StrictMiddlewareFunc +} + +// GetMedia operation middleware +func (sh *strictHandler) GetMedia(ctx *gin.Context, params GetMediaParams) { + var request GetMediaRequestObject + + request.Params = params + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.GetMedia(ctx, request.(GetMediaRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetMedia") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(GetMediaResponseObject); ok { + if err := validResponse.VisitGetMediaResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// PostMedia operation middleware +func (sh *strictHandler) PostMedia(ctx *gin.Context) { + var request PostMediaRequestObject + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.PostMedia(ctx, request.(PostMediaRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PostMedia") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(PostMediaResponseObject); ok { + if err := validResponse.VisitPostMediaResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// PostReviews operation middleware +func (sh *strictHandler) PostReviews(ctx *gin.Context) { + var request PostReviewsRequestObject + + var body PostReviewsJSONRequestBody + if err := ctx.ShouldBindJSON(&body); err != nil { + ctx.Status(http.StatusBadRequest) + ctx.Error(err) + return + } + request.Body = &body + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.PostReviews(ctx, request.(PostReviewsRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PostReviews") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(PostReviewsResponseObject); ok { + if err := validResponse.VisitPostReviewsResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// DeleteReviewsReviewId operation middleware +func (sh *strictHandler) DeleteReviewsReviewId(ctx *gin.Context, reviewId string) { + var request DeleteReviewsReviewIdRequestObject + + request.ReviewId = reviewId + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.DeleteReviewsReviewId(ctx, request.(DeleteReviewsReviewIdRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "DeleteReviewsReviewId") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(DeleteReviewsReviewIdResponseObject); ok { + if err := validResponse.VisitDeleteReviewsReviewIdResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// PatchReviewsReviewId operation middleware +func (sh *strictHandler) PatchReviewsReviewId(ctx *gin.Context, reviewId string) { + var request PatchReviewsReviewIdRequestObject + + request.ReviewId = reviewId + + var body PatchReviewsReviewIdJSONRequestBody + if err := ctx.ShouldBindJSON(&body); err != nil { + ctx.Status(http.StatusBadRequest) + ctx.Error(err) + return + } + request.Body = &body + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.PatchReviewsReviewId(ctx, request.(PatchReviewsReviewIdRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PatchReviewsReviewId") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(PatchReviewsReviewIdResponseObject); ok { + if err := validResponse.VisitPatchReviewsReviewIdResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// GetTags operation middleware +func (sh *strictHandler) GetTags(ctx *gin.Context, params GetTagsParams) { + var request GetTagsRequestObject + + request.Params = params + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.GetTags(ctx, request.(GetTagsRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetTags") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(GetTagsResponseObject); ok { + if err := validResponse.VisitGetTagsResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// GetTitle operation middleware +func (sh *strictHandler) GetTitle(ctx *gin.Context, params GetTitleParams) { + var request GetTitleRequestObject + + request.Params = params + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.GetTitle(ctx, request.(GetTitleRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetTitle") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(GetTitleResponseObject); ok { + if err := validResponse.VisitGetTitleResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// GetTitleTitleId operation middleware +func (sh *strictHandler) GetTitleTitleId(ctx *gin.Context, titleId string, params GetTitleTitleIdParams) { + var request GetTitleTitleIdRequestObject + + request.TitleId = titleId + request.Params = params + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.GetTitleTitleId(ctx, request.(GetTitleTitleIdRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetTitleTitleId") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(GetTitleTitleIdResponseObject); ok { + if err := validResponse.VisitGetTitleTitleIdResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// PatchTitleTitleId operation middleware +func (sh *strictHandler) PatchTitleTitleId(ctx *gin.Context, titleId string) { + var request PatchTitleTitleIdRequestObject + + request.TitleId = titleId + + var body PatchTitleTitleIdJSONRequestBody + if err := ctx.ShouldBindJSON(&body); err != nil { + ctx.Status(http.StatusBadRequest) + ctx.Error(err) + return + } + request.Body = &body + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.PatchTitleTitleId(ctx, request.(PatchTitleTitleIdRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PatchTitleTitleId") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(PatchTitleTitleIdResponseObject); ok { + if err := validResponse.VisitPatchTitleTitleIdResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// GetTitleTitleIdReviews operation middleware +func (sh *strictHandler) GetTitleTitleIdReviews(ctx *gin.Context, titleId string, params GetTitleTitleIdReviewsParams) { + var request GetTitleTitleIdReviewsRequestObject + + request.TitleId = titleId + request.Params = params + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.GetTitleTitleIdReviews(ctx, request.(GetTitleTitleIdReviewsRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetTitleTitleIdReviews") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(GetTitleTitleIdReviewsResponseObject); ok { + if err := validResponse.VisitGetTitleTitleIdReviewsResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// GetUsers operation middleware +func (sh *strictHandler) GetUsers(ctx *gin.Context, params GetUsersParams) { + var request GetUsersRequestObject + + request.Params = params + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.GetUsers(ctx, request.(GetUsersRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetUsers") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(GetUsersResponseObject); ok { + if err := validResponse.VisitGetUsersResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// PostUsers operation middleware +func (sh *strictHandler) PostUsers(ctx *gin.Context) { + var request PostUsersRequestObject + + var body PostUsersJSONRequestBody + if err := ctx.ShouldBindJSON(&body); err != nil { + ctx.Status(http.StatusBadRequest) + ctx.Error(err) + return + } + request.Body = &body + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.PostUsers(ctx, request.(PostUsersRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PostUsers") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(PostUsersResponseObject); ok { + if err := validResponse.VisitPostUsersResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// DeleteUsersUserId operation middleware +func (sh *strictHandler) DeleteUsersUserId(ctx *gin.Context, userId string) { + var request DeleteUsersUserIdRequestObject + + request.UserId = userId + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.DeleteUsersUserId(ctx, request.(DeleteUsersUserIdRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "DeleteUsersUserId") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(DeleteUsersUserIdResponseObject); ok { + if err := validResponse.VisitDeleteUsersUserIdResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// GetUsersUserId operation middleware +func (sh *strictHandler) GetUsersUserId(ctx *gin.Context, userId string, params GetUsersUserIdParams) { + var request GetUsersUserIdRequestObject + + request.UserId = userId + request.Params = params + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.GetUsersUserId(ctx, request.(GetUsersUserIdRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetUsersUserId") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(GetUsersUserIdResponseObject); ok { + if err := validResponse.VisitGetUsersUserIdResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// PatchUsersUserId operation middleware +func (sh *strictHandler) PatchUsersUserId(ctx *gin.Context, userId string) { + var request PatchUsersUserIdRequestObject + + request.UserId = userId + + var body PatchUsersUserIdJSONRequestBody + if err := ctx.ShouldBindJSON(&body); err != nil { + ctx.Status(http.StatusBadRequest) + ctx.Error(err) + return + } + request.Body = &body + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.PatchUsersUserId(ctx, request.(PatchUsersUserIdRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PatchUsersUserId") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(PatchUsersUserIdResponseObject); ok { + if err := validResponse.VisitPatchUsersUserIdResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// GetUsersUserIdReviews operation middleware +func (sh *strictHandler) GetUsersUserIdReviews(ctx *gin.Context, userId string, params GetUsersUserIdReviewsParams) { + var request GetUsersUserIdReviewsRequestObject + + request.UserId = userId + request.Params = params + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.GetUsersUserIdReviews(ctx, request.(GetUsersUserIdReviewsRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetUsersUserIdReviews") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(GetUsersUserIdReviewsResponseObject); ok { + if err := validResponse.VisitGetUsersUserIdReviewsResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// DeleteUsersUserIdTitles operation middleware +func (sh *strictHandler) DeleteUsersUserIdTitles(ctx *gin.Context, userId string, params DeleteUsersUserIdTitlesParams) { + var request DeleteUsersUserIdTitlesRequestObject + + request.UserId = userId + request.Params = params + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.DeleteUsersUserIdTitles(ctx, request.(DeleteUsersUserIdTitlesRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "DeleteUsersUserIdTitles") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(DeleteUsersUserIdTitlesResponseObject); ok { + if err := validResponse.VisitDeleteUsersUserIdTitlesResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// GetUsersUserIdTitles operation middleware +func (sh *strictHandler) GetUsersUserIdTitles(ctx *gin.Context, userId string, params GetUsersUserIdTitlesParams) { + var request GetUsersUserIdTitlesRequestObject + + request.UserId = userId + request.Params = params + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.GetUsersUserIdTitles(ctx, request.(GetUsersUserIdTitlesRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetUsersUserIdTitles") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(GetUsersUserIdTitlesResponseObject); ok { + if err := validResponse.VisitGetUsersUserIdTitlesResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// PatchUsersUserIdTitles operation middleware +func (sh *strictHandler) PatchUsersUserIdTitles(ctx *gin.Context, userId string) { + var request PatchUsersUserIdTitlesRequestObject + + request.UserId = userId + + var body PatchUsersUserIdTitlesJSONRequestBody + if err := ctx.ShouldBindJSON(&body); err != nil { + ctx.Status(http.StatusBadRequest) + ctx.Error(err) + return + } + request.Body = &body + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.PatchUsersUserIdTitles(ctx, request.(PatchUsersUserIdTitlesRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PatchUsersUserIdTitles") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(PatchUsersUserIdTitlesResponseObject); ok { + if err := validResponse.VisitPatchUsersUserIdTitlesResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// PostUsersUserIdTitles operation middleware +func (sh *strictHandler) PostUsersUserIdTitles(ctx *gin.Context, userId string) { + var request PostUsersUserIdTitlesRequestObject + + request.UserId = userId + + var body PostUsersUserIdTitlesJSONRequestBody + if err := ctx.ShouldBindJSON(&body); err != nil { + ctx.Status(http.StatusBadRequest) + ctx.Error(err) + return + } + request.Body = &body + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.PostUsersUserIdTitles(ctx, request.(PostUsersUserIdTitlesRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PostUsersUserIdTitles") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(PostUsersUserIdTitlesResponseObject); ok { + if err := validResponse.VisitPostUsersUserIdTitlesResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} diff --git a/api/oapi-codegen.yaml b/api/oapi-codegen.yaml index 32e029a..1d470ac 100644 --- a/api/oapi-codegen.yaml +++ b/api/oapi-codegen.yaml @@ -1,6 +1,6 @@ -package: oapi +package: api generate: strict-server: true gin-server: true models: true -output: api/api.gen.go \ No newline at end of file +output: gen.go \ No newline at end of file diff --git a/api/openapi.yaml b/api/openapi.yaml index b20f677..5a35e20 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -3,126 +3,126 @@ info: title: Titles, Users, Reviews, Tags, and Media API version: 1.0.0 servers: - - url: /api/v1 + - url: https://api.example.com paths: - # /title: - # get: - # summary: Get titles - # parameters: - # - in: query - # name: query - # schema: - # type: string - # - in: query - # name: limit - # schema: - # type: integer - # default: 10 - # - in: query - # name: offset - # schema: - # type: integer - # default: 0 - # - in: query - # name: fields - # schema: - # type: string - # default: all - # responses: - # '200': - # description: List of titles - # content: - # application/json: - # schema: - # type: array - # items: - # $ref: '#/components/schemas/Title' - # '204': - # description: No titles found + /title: + get: + summary: Get titles + parameters: + - in: query + name: query + schema: + type: string + - in: query + name: limit + schema: + type: integer + default: 10 + - in: query + name: offset + schema: + type: integer + default: 0 + - in: query + name: fields + schema: + type: string + default: all + responses: + '200': + description: List of titles + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Title' + '204': + description: No titles found -# /title/{title_id}: -# get: -# summary: Get title description -# parameters: -# - in: path -# name: title_id -# required: true -# schema: -# type: string -# - in: query -# name: fields -# schema: -# type: string -# default: all -# responses: -# '200': -# description: Title description -# content: -# application/json: -# schema: -# $ref: '#/components/schemas/Title' -# '404': -# description: Title not found + /title/{title_id}: + get: + summary: Get title description + parameters: + - in: path + name: title_id + required: true + schema: + type: string + - in: query + name: fields + schema: + type: string + default: all + responses: + '200': + description: Title description + content: + application/json: + schema: + $ref: '#/components/schemas/Title' + '404': + description: Title not found -# patch: -# summary: Update title info -# parameters: -# - in: path -# name: title_id -# required: true -# schema: -# type: string -# requestBody: -# required: true -# content: -# application/json: -# schema: -# $ref: '#/components/schemas/Title' -# responses: -# '200': -# description: Update result -# content: -# application/json: -# schema: -# type: object -# properties: -# success: -# type: boolean -# error: -# type: string -# user_json: -# $ref: '#/components/schemas/User' + patch: + summary: Update title info + parameters: + - in: path + name: title_id + required: true + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Title' + responses: + '200': + description: Update result + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + error: + type: string + user_json: + $ref: '#/components/schemas/User' -# /title/{title_id}/reviews: -# get: -# summary: Get title reviews -# parameters: -# - in: path -# name: title_id -# required: true -# schema: -# type: string -# - in: query -# name: limit -# schema: -# type: integer -# default: 10 -# - in: query -# name: offset -# schema: -# type: integer -# default: 0 -# responses: -# '200': -# description: List of reviews -# content: -# application/json: -# schema: -# type: array -# items: -# $ref: '#/components/schemas/Review' -# '204': -# description: No reviews found + /title/{title_id}/reviews: + get: + summary: Get title reviews + parameters: + - in: path + name: title_id + required: true + schema: + type: string + - in: query + name: limit + schema: + type: integer + default: 10 + - in: query + name: offset + schema: + type: integer + default: 0 + responses: + '200': + description: List of reviews + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Review' + '204': + description: No reviews found /users/{user_id}: get: @@ -148,390 +148,390 @@ paths: '404': description: User not found - # patch: - # summary: Update user - # parameters: - # - in: path - # name: user_id - # required: true - # schema: - # type: string - # requestBody: - # required: true - # content: - # application/json: - # schema: - # $ref: '#/components/schemas/User' - # responses: - # '200': - # description: Update result - # content: - # application/json: - # schema: - # type: object - # properties: - # success: - # type: boolean - # error: - # type: string + patch: + summary: Update user + parameters: + - in: path + name: user_id + required: true + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/User' + responses: + '200': + description: Update result + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + error: + type: string - # delete: - # summary: Delete user - # parameters: - # - in: path - # name: user_id - # required: true - # schema: - # type: string - # responses: - # '200': - # description: Delete result - # content: - # application/json: - # schema: - # type: object - # properties: - # success: - # type: boolean - # error: - # type: string + delete: + summary: Delete user + parameters: + - in: path + name: user_id + required: true + schema: + type: string + responses: + '200': + description: Delete result + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + error: + type: string - # /users: - # get: - # summary: Search user - # parameters: - # - in: query - # name: query - # schema: - # type: string - # - in: query - # name: fields - # schema: - # type: string - # responses: - # '200': - # description: List of users - # content: - # application/json: - # schema: - # type: array - # items: - # $ref: '#/components/schemas/User' + /users: + get: + summary: Search user + parameters: + - in: query + name: query + schema: + type: string + - in: query + name: fields + schema: + type: string + responses: + '200': + description: List of users + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/User' - # post: - # summary: Add new user - # requestBody: - # required: true - # content: - # application/json: - # schema: - # $ref: '#/components/schemas/User' - # responses: - # '200': - # description: Add result - # content: - # application/json: - # schema: - # type: object - # properties: - # success: - # type: boolean - # error: - # type: string - # user_json: - # $ref: '#/components/schemas/User' + post: + summary: Add new user + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/User' + responses: + '200': + description: Add result + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + error: + type: string + user_json: + $ref: '#/components/schemas/User' -# /users/{user_id}/titles: -# get: -# summary: Get user titles -# parameters: -# - in: path -# name: user_id -# required: true -# schema: -# type: string -# - in: query -# name: query -# schema: -# type: string -# - in: query -# name: limit -# schema: -# type: integer -# default: 10 -# - in: query -# name: offset -# schema: -# type: integer -# default: 0 -# - in: query -# name: fields -# schema: -# type: string -# default: all -# responses: -# '200': -# description: List of user titles -# content: -# application/json: -# schema: -# type: array -# items: -# $ref: '#/components/schemas/UserTitle' -# '204': -# description: No titles found + /users/{user_id}/titles: + get: + summary: Get user titles + parameters: + - in: path + name: user_id + required: true + schema: + type: string + - in: query + name: query + schema: + type: string + - in: query + name: limit + schema: + type: integer + default: 10 + - in: query + name: offset + schema: + type: integer + default: 0 + - in: query + name: fields + schema: + type: string + default: all + responses: + '200': + description: List of user titles + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/UserTitle' + '204': + description: No titles found -# post: -# summary: Add user title -# parameters: -# - in: path -# name: user_id -# required: true -# schema: -# type: string -# requestBody: -# required: true -# content: -# application/json: -# schema: -# type: object -# properties: -# title_id: -# type: string -# status: -# type: string -# responses: -# '200': -# description: Add result -# content: -# application/json: -# schema: -# type: object -# properties: -# success: -# type: boolean -# error: -# type: string + post: + summary: Add user title + parameters: + - in: path + name: user_id + required: true + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + title_id: + type: string + status: + type: string + responses: + '200': + description: Add result + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + error: + type: string -# patch: -# summary: Update user title -# parameters: -# - in: path -# name: user_id -# required: true -# schema: -# type: string -# requestBody: -# required: true -# content: -# application/json: -# schema: -# $ref: '#/components/schemas/UserTitle' -# responses: -# '200': -# description: Update result -# content: -# application/json: -# schema: -# type: object -# properties: -# success: -# type: boolean -# error: -# type: string + patch: + summary: Update user title + parameters: + - in: path + name: user_id + required: true + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/UserTitle' + responses: + '200': + description: Update result + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + error: + type: string -# delete: -# summary: Delete user title -# parameters: -# - in: path -# name: user_id -# required: true -# schema: -# type: string -# - in: query -# name: title_id -# schema: -# type: string -# responses: -# '200': -# description: Delete result -# content: -# application/json: -# schema: -# type: object -# properties: -# success: -# type: boolean -# error: -# type: string + delete: + summary: Delete user title + parameters: + - in: path + name: user_id + required: true + schema: + type: string + - in: query + name: title_id + schema: + type: string + responses: + '200': + description: Delete result + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + error: + type: string -# /users/{user_id}/reviews: -# get: -# summary: Get user reviews -# parameters: -# - in: path -# name: user_id -# required: true -# schema: -# type: string -# - in: query -# name: limit -# schema: -# type: integer -# default: 10 -# - in: query -# name: offset -# schema: -# type: integer -# default: 0 -# responses: -# '200': -# description: List of reviews -# content: -# application/json: -# schema: -# type: array -# items: -# $ref: '#/components/schemas/Review' + /users/{user_id}/reviews: + get: + summary: Get user reviews + parameters: + - in: path + name: user_id + required: true + schema: + type: string + - in: query + name: limit + schema: + type: integer + default: 10 + - in: query + name: offset + schema: + type: integer + default: 0 + responses: + '200': + description: List of reviews + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Review' -# /reviews: -# post: -# summary: Add review -# requestBody: -# required: true -# content: -# application/json: -# schema: -# $ref: '#/components/schemas/Review' -# responses: -# '200': -# description: Add result -# content: -# application/json: -# schema: -# type: object -# properties: -# success: -# type: boolean -# error: -# type: string + /reviews: + post: + summary: Add review + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Review' + responses: + '200': + description: Add result + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + error: + type: string -# /reviews/{review_id}: -# patch: -# summary: Update review -# parameters: -# - in: path -# name: review_id -# required: true -# schema: -# type: string -# requestBody: -# required: true -# content: -# application/json: -# schema: -# $ref: '#/components/schemas/Review' -# responses: -# '200': -# description: Update result -# content: -# application/json: -# schema: -# type: object -# properties: -# success: -# type: boolean -# error: -# type: string -# delete: -# summary: Delete review -# parameters: -# - in: path -# name: review_id -# required: true -# schema: -# type: string -# responses: -# '200': -# description: Delete result -# content: -# application/json: -# schema: -# type: object -# properties: -# success: -# type: boolean -# error: -# type: string + /reviews/{review_id}: + patch: + summary: Update review + parameters: + - in: path + name: review_id + required: true + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Review' + responses: + '200': + description: Update result + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + error: + type: string + delete: + summary: Delete review + parameters: + - in: path + name: review_id + required: true + schema: + type: string + responses: + '200': + description: Delete result + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + error: + type: string -# /tags: -# get: -# summary: Get tags -# parameters: -# - in: query -# name: limit -# schema: -# type: integer -# default: 10 -# - in: query -# name: offset -# schema: -# type: integer -# default: 0 -# - in: query -# name: fields -# schema: -# type: string -# responses: -# '200': -# description: List of tags -# content: -# application/json: -# schema: -# type: array -# items: -# $ref: '#/components/schemas/Tag' + /tags: + get: + summary: Get tags + parameters: + - in: query + name: limit + schema: + type: integer + default: 10 + - in: query + name: offset + schema: + type: integer + default: 0 + - in: query + name: fields + schema: + type: string + responses: + '200': + description: List of tags + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Tag' -# /media: -# post: -# summary: Upload image -# responses: -# '200': -# description: Upload result -# content: -# application/json: -# schema: -# type: object -# properties: -# success: -# type: boolean -# error: -# type: string -# image_id: -# type: string + /media: + post: + summary: Upload image + responses: + '200': + description: Upload result + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + error: + type: string + image_id: + type: string -# get: -# summary: Get image path -# parameters: -# - in: query -# name: image_id -# required: true -# schema: -# type: string -# responses: -# '200': -# description: Image path -# content: -# application/json: -# schema: -# type: object -# properties: -# success: -# type: boolean -# error: -# type: string -# image_path: -# type: string + get: + summary: Get image path + parameters: + - in: query + name: image_id + required: true + schema: + type: string + responses: + '200': + description: Image path + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + error: + type: string + image_path: + type: string components: schemas: @@ -540,47 +540,7 @@ components: additionalProperties: true User: type: object - properties: - id: - type: integer - format: int64 - description: Unique user ID (primary key) - example: 1 - avatar_id: - type: integer - format: int64 - description: ID of the user avatar (references images table) - nullable: true - example: null - mail: - type: string - format: email - description: User email - example: "john.doe@example.com" - nickname: - type: string - description: Username (alphanumeric + _ or -) - maxLength: 16 - example: "john_doe_42" - disp_name: - type: string - description: Display name - maxLength: 32 - example: "John Doe" - user_desc: - type: string - description: User description - maxLength: 512 - example: "Just a regular user." - creation_date: - type: string - format: date-time - description: Timestamp when the user was created - example: "2025-10-10T23:45:47.908073Z" - required: - - user_id - - nickname - - creation_date + additionalProperties: true UserTitle: type: object additionalProperties: true diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index 1a96253..266f9d5 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -10,7 +10,7 @@ services: ports: - "${POSTGRES_PORT}:5432" volumes: - - postgres_data:/var/lib/postgresql + - postgres_data:/var/lib/postgresql/data # pgadmin: # image: dpage/pgadmin4:${PGADMIN_VERSION} @@ -32,7 +32,6 @@ services: restart: always environment: LOG_LEVEL: ${LOG_LEVEL} - DATABASE_URL: ${DATABASE_URL} ports: - "8080:8080" depends_on: diff --git a/deploy/generate.sh b/deploy/generate.sh deleted file mode 100644 index d7d94a2..0000000 --- a/deploy/generate.sh +++ /dev/null @@ -1,3 +0,0 @@ -npx openapi-typescript-codegen --input ..\..\api\openapi.yaml --output ./src/api --client axios -oapi-codegen --config=api/oapi-codegen.yaml .\api\openapi.yaml -sqlc generate -f .\sql\sqlc.yaml \ No newline at end of file diff --git a/modules/backend/api/gen.go b/modules/backend/api/gen.go new file mode 100644 index 0000000..2dcb886 --- /dev/null +++ b/modules/backend/api/gen.go @@ -0,0 +1,1958 @@ +// Package api provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.5.0 DO NOT EDIT. +package api + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + + "github.com/gin-gonic/gin" + "github.com/oapi-codegen/runtime" + strictgin "github.com/oapi-codegen/runtime/strictmiddleware/gin" +) + +// Review defines model for Review. +type Review map[string]interface{} + +// Tag defines model for Tag. +type Tag map[string]interface{} + +// Title defines model for Title. +type Title map[string]interface{} + +// User defines model for User. +type User map[string]interface{} + +// UserTitle defines model for UserTitle. +type UserTitle map[string]interface{} + +// GetMediaParams defines parameters for GetMedia. +type GetMediaParams struct { + ImageId string `form:"image_id" json:"image_id"` +} + +// GetTagsParams defines parameters for GetTags. +type GetTagsParams struct { + Limit *int `form:"limit,omitempty" json:"limit,omitempty"` + Offset *int `form:"offset,omitempty" json:"offset,omitempty"` + Fields *string `form:"fields,omitempty" json:"fields,omitempty"` +} + +// GetTitleParams defines parameters for GetTitle. +type GetTitleParams struct { + Query *string `form:"query,omitempty" json:"query,omitempty"` + Limit *int `form:"limit,omitempty" json:"limit,omitempty"` + Offset *int `form:"offset,omitempty" json:"offset,omitempty"` + Fields *string `form:"fields,omitempty" json:"fields,omitempty"` +} + +// GetTitleTitleIdParams defines parameters for GetTitleTitleId. +type GetTitleTitleIdParams struct { + Fields *string `form:"fields,omitempty" json:"fields,omitempty"` +} + +// GetTitleTitleIdReviewsParams defines parameters for GetTitleTitleIdReviews. +type GetTitleTitleIdReviewsParams struct { + Limit *int `form:"limit,omitempty" json:"limit,omitempty"` + Offset *int `form:"offset,omitempty" json:"offset,omitempty"` +} + +// GetUsersParams defines parameters for GetUsers. +type GetUsersParams struct { + Query *string `form:"query,omitempty" json:"query,omitempty"` + Fields *string `form:"fields,omitempty" json:"fields,omitempty"` +} + +// GetUsersUserIdParams defines parameters for GetUsersUserId. +type GetUsersUserIdParams struct { + Fields *string `form:"fields,omitempty" json:"fields,omitempty"` +} + +// GetUsersUserIdReviewsParams defines parameters for GetUsersUserIdReviews. +type GetUsersUserIdReviewsParams struct { + Limit *int `form:"limit,omitempty" json:"limit,omitempty"` + Offset *int `form:"offset,omitempty" json:"offset,omitempty"` +} + +// DeleteUsersUserIdTitlesParams defines parameters for DeleteUsersUserIdTitles. +type DeleteUsersUserIdTitlesParams struct { + TitleId *string `form:"title_id,omitempty" json:"title_id,omitempty"` +} + +// GetUsersUserIdTitlesParams defines parameters for GetUsersUserIdTitles. +type GetUsersUserIdTitlesParams struct { + Query *string `form:"query,omitempty" json:"query,omitempty"` + Limit *int `form:"limit,omitempty" json:"limit,omitempty"` + Offset *int `form:"offset,omitempty" json:"offset,omitempty"` + Fields *string `form:"fields,omitempty" json:"fields,omitempty"` +} + +// PostUsersUserIdTitlesJSONBody defines parameters for PostUsersUserIdTitles. +type PostUsersUserIdTitlesJSONBody struct { + Status *string `json:"status,omitempty"` + TitleId *string `json:"title_id,omitempty"` +} + +// PostReviewsJSONRequestBody defines body for PostReviews for application/json ContentType. +type PostReviewsJSONRequestBody = Review + +// PatchReviewsReviewIdJSONRequestBody defines body for PatchReviewsReviewId for application/json ContentType. +type PatchReviewsReviewIdJSONRequestBody = Review + +// PatchTitleTitleIdJSONRequestBody defines body for PatchTitleTitleId for application/json ContentType. +type PatchTitleTitleIdJSONRequestBody = Title + +// PostUsersJSONRequestBody defines body for PostUsers for application/json ContentType. +type PostUsersJSONRequestBody = User + +// PatchUsersUserIdJSONRequestBody defines body for PatchUsersUserId for application/json ContentType. +type PatchUsersUserIdJSONRequestBody = User + +// PatchUsersUserIdTitlesJSONRequestBody defines body for PatchUsersUserIdTitles for application/json ContentType. +type PatchUsersUserIdTitlesJSONRequestBody = UserTitle + +// PostUsersUserIdTitlesJSONRequestBody defines body for PostUsersUserIdTitles for application/json ContentType. +type PostUsersUserIdTitlesJSONRequestBody PostUsersUserIdTitlesJSONBody + +// ServerInterface represents all server handlers. +type ServerInterface interface { + // Get image path + // (GET /media) + GetMedia(c *gin.Context, params GetMediaParams) + // Upload image + // (POST /media) + PostMedia(c *gin.Context) + // Add review + // (POST /reviews) + PostReviews(c *gin.Context) + // Delete review + // (DELETE /reviews/{review_id}) + DeleteReviewsReviewId(c *gin.Context, reviewId string) + // Update review + // (PATCH /reviews/{review_id}) + PatchReviewsReviewId(c *gin.Context, reviewId string) + // Get tags + // (GET /tags) + GetTags(c *gin.Context, params GetTagsParams) + // Get titles + // (GET /title) + GetTitle(c *gin.Context, params GetTitleParams) + // Get title description + // (GET /title/{title_id}) + GetTitleTitleId(c *gin.Context, titleId string, params GetTitleTitleIdParams) + // Update title info + // (PATCH /title/{title_id}) + PatchTitleTitleId(c *gin.Context, titleId string) + // Get title reviews + // (GET /title/{title_id}/reviews) + GetTitleTitleIdReviews(c *gin.Context, titleId string, params GetTitleTitleIdReviewsParams) + // Search user + // (GET /users) + GetUsers(c *gin.Context, params GetUsersParams) + // Add new user + // (POST /users) + PostUsers(c *gin.Context) + // Delete user + // (DELETE /users/{user_id}) + DeleteUsersUserId(c *gin.Context, userId string) + // Get user info + // (GET /users/{user_id}) + GetUsersUserId(c *gin.Context, userId string, params GetUsersUserIdParams) + // Update user + // (PATCH /users/{user_id}) + PatchUsersUserId(c *gin.Context, userId string) + // Get user reviews + // (GET /users/{user_id}/reviews) + GetUsersUserIdReviews(c *gin.Context, userId string, params GetUsersUserIdReviewsParams) + // Delete user title + // (DELETE /users/{user_id}/titles) + DeleteUsersUserIdTitles(c *gin.Context, userId string, params DeleteUsersUserIdTitlesParams) + // Get user titles + // (GET /users/{user_id}/titles) + GetUsersUserIdTitles(c *gin.Context, userId string, params GetUsersUserIdTitlesParams) + // Update user title + // (PATCH /users/{user_id}/titles) + PatchUsersUserIdTitles(c *gin.Context, userId string) + // Add user title + // (POST /users/{user_id}/titles) + PostUsersUserIdTitles(c *gin.Context, userId string) +} + +// ServerInterfaceWrapper converts contexts to parameters. +type ServerInterfaceWrapper struct { + Handler ServerInterface + HandlerMiddlewares []MiddlewareFunc + ErrorHandler func(*gin.Context, error, int) +} + +type MiddlewareFunc func(c *gin.Context) + +// GetMedia operation middleware +func (siw *ServerInterfaceWrapper) GetMedia(c *gin.Context) { + + var err error + + // Parameter object where we will unmarshal all parameters from the context + var params GetMediaParams + + // ------------- Required query parameter "image_id" ------------- + + if paramValue := c.Query("image_id"); paramValue != "" { + + } else { + siw.ErrorHandler(c, fmt.Errorf("Query argument image_id is required, but not found"), http.StatusBadRequest) + return + } + + err = runtime.BindQueryParameter("form", true, true, "image_id", c.Request.URL.Query(), ¶ms.ImageId) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter image_id: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.GetMedia(c, params) +} + +// PostMedia operation middleware +func (siw *ServerInterfaceWrapper) PostMedia(c *gin.Context) { + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.PostMedia(c) +} + +// PostReviews operation middleware +func (siw *ServerInterfaceWrapper) PostReviews(c *gin.Context) { + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.PostReviews(c) +} + +// DeleteReviewsReviewId operation middleware +func (siw *ServerInterfaceWrapper) DeleteReviewsReviewId(c *gin.Context) { + + var err error + + // ------------- Path parameter "review_id" ------------- + var reviewId string + + err = runtime.BindStyledParameterWithOptions("simple", "review_id", c.Param("review_id"), &reviewId, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter review_id: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.DeleteReviewsReviewId(c, reviewId) +} + +// PatchReviewsReviewId operation middleware +func (siw *ServerInterfaceWrapper) PatchReviewsReviewId(c *gin.Context) { + + var err error + + // ------------- Path parameter "review_id" ------------- + var reviewId string + + err = runtime.BindStyledParameterWithOptions("simple", "review_id", c.Param("review_id"), &reviewId, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter review_id: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.PatchReviewsReviewId(c, reviewId) +} + +// GetTags operation middleware +func (siw *ServerInterfaceWrapper) GetTags(c *gin.Context) { + + var err error + + // Parameter object where we will unmarshal all parameters from the context + var params GetTagsParams + + // ------------- Optional query parameter "limit" ------------- + + err = runtime.BindQueryParameter("form", true, false, "limit", c.Request.URL.Query(), ¶ms.Limit) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter limit: %w", err), http.StatusBadRequest) + return + } + + // ------------- Optional query parameter "offset" ------------- + + err = runtime.BindQueryParameter("form", true, false, "offset", c.Request.URL.Query(), ¶ms.Offset) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter offset: %w", err), http.StatusBadRequest) + return + } + + // ------------- Optional query parameter "fields" ------------- + + err = runtime.BindQueryParameter("form", true, false, "fields", c.Request.URL.Query(), ¶ms.Fields) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter fields: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.GetTags(c, params) +} + +// GetTitle operation middleware +func (siw *ServerInterfaceWrapper) GetTitle(c *gin.Context) { + + var err error + + // Parameter object where we will unmarshal all parameters from the context + var params GetTitleParams + + // ------------- Optional query parameter "query" ------------- + + err = runtime.BindQueryParameter("form", true, false, "query", c.Request.URL.Query(), ¶ms.Query) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter query: %w", err), http.StatusBadRequest) + return + } + + // ------------- Optional query parameter "limit" ------------- + + err = runtime.BindQueryParameter("form", true, false, "limit", c.Request.URL.Query(), ¶ms.Limit) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter limit: %w", err), http.StatusBadRequest) + return + } + + // ------------- Optional query parameter "offset" ------------- + + err = runtime.BindQueryParameter("form", true, false, "offset", c.Request.URL.Query(), ¶ms.Offset) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter offset: %w", err), http.StatusBadRequest) + return + } + + // ------------- Optional query parameter "fields" ------------- + + err = runtime.BindQueryParameter("form", true, false, "fields", c.Request.URL.Query(), ¶ms.Fields) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter fields: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.GetTitle(c, params) +} + +// GetTitleTitleId operation middleware +func (siw *ServerInterfaceWrapper) GetTitleTitleId(c *gin.Context) { + + var err error + + // ------------- Path parameter "title_id" ------------- + var titleId string + + err = runtime.BindStyledParameterWithOptions("simple", "title_id", c.Param("title_id"), &titleId, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter title_id: %w", err), http.StatusBadRequest) + return + } + + // Parameter object where we will unmarshal all parameters from the context + var params GetTitleTitleIdParams + + // ------------- Optional query parameter "fields" ------------- + + err = runtime.BindQueryParameter("form", true, false, "fields", c.Request.URL.Query(), ¶ms.Fields) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter fields: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.GetTitleTitleId(c, titleId, params) +} + +// PatchTitleTitleId operation middleware +func (siw *ServerInterfaceWrapper) PatchTitleTitleId(c *gin.Context) { + + var err error + + // ------------- Path parameter "title_id" ------------- + var titleId string + + err = runtime.BindStyledParameterWithOptions("simple", "title_id", c.Param("title_id"), &titleId, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter title_id: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.PatchTitleTitleId(c, titleId) +} + +// GetTitleTitleIdReviews operation middleware +func (siw *ServerInterfaceWrapper) GetTitleTitleIdReviews(c *gin.Context) { + + var err error + + // ------------- Path parameter "title_id" ------------- + var titleId string + + err = runtime.BindStyledParameterWithOptions("simple", "title_id", c.Param("title_id"), &titleId, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter title_id: %w", err), http.StatusBadRequest) + return + } + + // Parameter object where we will unmarshal all parameters from the context + var params GetTitleTitleIdReviewsParams + + // ------------- Optional query parameter "limit" ------------- + + err = runtime.BindQueryParameter("form", true, false, "limit", c.Request.URL.Query(), ¶ms.Limit) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter limit: %w", err), http.StatusBadRequest) + return + } + + // ------------- Optional query parameter "offset" ------------- + + err = runtime.BindQueryParameter("form", true, false, "offset", c.Request.URL.Query(), ¶ms.Offset) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter offset: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.GetTitleTitleIdReviews(c, titleId, params) +} + +// GetUsers operation middleware +func (siw *ServerInterfaceWrapper) GetUsers(c *gin.Context) { + + var err error + + // Parameter object where we will unmarshal all parameters from the context + var params GetUsersParams + + // ------------- Optional query parameter "query" ------------- + + err = runtime.BindQueryParameter("form", true, false, "query", c.Request.URL.Query(), ¶ms.Query) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter query: %w", err), http.StatusBadRequest) + return + } + + // ------------- Optional query parameter "fields" ------------- + + err = runtime.BindQueryParameter("form", true, false, "fields", c.Request.URL.Query(), ¶ms.Fields) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter fields: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.GetUsers(c, params) +} + +// PostUsers operation middleware +func (siw *ServerInterfaceWrapper) PostUsers(c *gin.Context) { + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.PostUsers(c) +} + +// DeleteUsersUserId operation middleware +func (siw *ServerInterfaceWrapper) DeleteUsersUserId(c *gin.Context) { + + var err error + + // ------------- Path parameter "user_id" ------------- + var userId string + + err = runtime.BindStyledParameterWithOptions("simple", "user_id", c.Param("user_id"), &userId, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter user_id: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.DeleteUsersUserId(c, userId) +} + +// GetUsersUserId operation middleware +func (siw *ServerInterfaceWrapper) GetUsersUserId(c *gin.Context) { + + var err error + + // ------------- Path parameter "user_id" ------------- + var userId string + + err = runtime.BindStyledParameterWithOptions("simple", "user_id", c.Param("user_id"), &userId, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter user_id: %w", err), http.StatusBadRequest) + return + } + + // Parameter object where we will unmarshal all parameters from the context + var params GetUsersUserIdParams + + // ------------- Optional query parameter "fields" ------------- + + err = runtime.BindQueryParameter("form", true, false, "fields", c.Request.URL.Query(), ¶ms.Fields) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter fields: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.GetUsersUserId(c, userId, params) +} + +// PatchUsersUserId operation middleware +func (siw *ServerInterfaceWrapper) PatchUsersUserId(c *gin.Context) { + + var err error + + // ------------- Path parameter "user_id" ------------- + var userId string + + err = runtime.BindStyledParameterWithOptions("simple", "user_id", c.Param("user_id"), &userId, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter user_id: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.PatchUsersUserId(c, userId) +} + +// GetUsersUserIdReviews operation middleware +func (siw *ServerInterfaceWrapper) GetUsersUserIdReviews(c *gin.Context) { + + var err error + + // ------------- Path parameter "user_id" ------------- + var userId string + + err = runtime.BindStyledParameterWithOptions("simple", "user_id", c.Param("user_id"), &userId, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter user_id: %w", err), http.StatusBadRequest) + return + } + + // Parameter object where we will unmarshal all parameters from the context + var params GetUsersUserIdReviewsParams + + // ------------- Optional query parameter "limit" ------------- + + err = runtime.BindQueryParameter("form", true, false, "limit", c.Request.URL.Query(), ¶ms.Limit) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter limit: %w", err), http.StatusBadRequest) + return + } + + // ------------- Optional query parameter "offset" ------------- + + err = runtime.BindQueryParameter("form", true, false, "offset", c.Request.URL.Query(), ¶ms.Offset) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter offset: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.GetUsersUserIdReviews(c, userId, params) +} + +// DeleteUsersUserIdTitles operation middleware +func (siw *ServerInterfaceWrapper) DeleteUsersUserIdTitles(c *gin.Context) { + + var err error + + // ------------- Path parameter "user_id" ------------- + var userId string + + err = runtime.BindStyledParameterWithOptions("simple", "user_id", c.Param("user_id"), &userId, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter user_id: %w", err), http.StatusBadRequest) + return + } + + // Parameter object where we will unmarshal all parameters from the context + var params DeleteUsersUserIdTitlesParams + + // ------------- Optional query parameter "title_id" ------------- + + err = runtime.BindQueryParameter("form", true, false, "title_id", c.Request.URL.Query(), ¶ms.TitleId) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter title_id: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.DeleteUsersUserIdTitles(c, userId, params) +} + +// GetUsersUserIdTitles operation middleware +func (siw *ServerInterfaceWrapper) GetUsersUserIdTitles(c *gin.Context) { + + var err error + + // ------------- Path parameter "user_id" ------------- + var userId string + + err = runtime.BindStyledParameterWithOptions("simple", "user_id", c.Param("user_id"), &userId, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter user_id: %w", err), http.StatusBadRequest) + return + } + + // Parameter object where we will unmarshal all parameters from the context + var params GetUsersUserIdTitlesParams + + // ------------- Optional query parameter "query" ------------- + + err = runtime.BindQueryParameter("form", true, false, "query", c.Request.URL.Query(), ¶ms.Query) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter query: %w", err), http.StatusBadRequest) + return + } + + // ------------- Optional query parameter "limit" ------------- + + err = runtime.BindQueryParameter("form", true, false, "limit", c.Request.URL.Query(), ¶ms.Limit) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter limit: %w", err), http.StatusBadRequest) + return + } + + // ------------- Optional query parameter "offset" ------------- + + err = runtime.BindQueryParameter("form", true, false, "offset", c.Request.URL.Query(), ¶ms.Offset) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter offset: %w", err), http.StatusBadRequest) + return + } + + // ------------- Optional query parameter "fields" ------------- + + err = runtime.BindQueryParameter("form", true, false, "fields", c.Request.URL.Query(), ¶ms.Fields) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter fields: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.GetUsersUserIdTitles(c, userId, params) +} + +// PatchUsersUserIdTitles operation middleware +func (siw *ServerInterfaceWrapper) PatchUsersUserIdTitles(c *gin.Context) { + + var err error + + // ------------- Path parameter "user_id" ------------- + var userId string + + err = runtime.BindStyledParameterWithOptions("simple", "user_id", c.Param("user_id"), &userId, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter user_id: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.PatchUsersUserIdTitles(c, userId) +} + +// PostUsersUserIdTitles operation middleware +func (siw *ServerInterfaceWrapper) PostUsersUserIdTitles(c *gin.Context) { + + var err error + + // ------------- Path parameter "user_id" ------------- + var userId string + + err = runtime.BindStyledParameterWithOptions("simple", "user_id", c.Param("user_id"), &userId, runtime.BindStyledParameterOptions{Explode: false, Required: true}) + if err != nil { + siw.ErrorHandler(c, fmt.Errorf("Invalid format for parameter user_id: %w", err), http.StatusBadRequest) + return + } + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.PostUsersUserIdTitles(c, userId) +} + +// GinServerOptions provides options for the Gin server. +type GinServerOptions struct { + BaseURL string + Middlewares []MiddlewareFunc + ErrorHandler func(*gin.Context, error, int) +} + +// RegisterHandlers creates http.Handler with routing matching OpenAPI spec. +func RegisterHandlers(router gin.IRouter, si ServerInterface) { + RegisterHandlersWithOptions(router, si, GinServerOptions{}) +} + +// RegisterHandlersWithOptions creates http.Handler with additional options +func RegisterHandlersWithOptions(router gin.IRouter, si ServerInterface, options GinServerOptions) { + errorHandler := options.ErrorHandler + if errorHandler == nil { + errorHandler = func(c *gin.Context, err error, statusCode int) { + c.JSON(statusCode, gin.H{"msg": err.Error()}) + } + } + + wrapper := ServerInterfaceWrapper{ + Handler: si, + HandlerMiddlewares: options.Middlewares, + ErrorHandler: errorHandler, + } + + router.GET(options.BaseURL+"/media", wrapper.GetMedia) + router.POST(options.BaseURL+"/media", wrapper.PostMedia) + router.POST(options.BaseURL+"/reviews", wrapper.PostReviews) + router.DELETE(options.BaseURL+"/reviews/:review_id", wrapper.DeleteReviewsReviewId) + router.PATCH(options.BaseURL+"/reviews/:review_id", wrapper.PatchReviewsReviewId) + router.GET(options.BaseURL+"/tags", wrapper.GetTags) + router.GET(options.BaseURL+"/title", wrapper.GetTitle) + router.GET(options.BaseURL+"/title/:title_id", wrapper.GetTitleTitleId) + router.PATCH(options.BaseURL+"/title/:title_id", wrapper.PatchTitleTitleId) + router.GET(options.BaseURL+"/title/:title_id/reviews", wrapper.GetTitleTitleIdReviews) + router.GET(options.BaseURL+"/users", wrapper.GetUsers) + router.POST(options.BaseURL+"/users", wrapper.PostUsers) + router.DELETE(options.BaseURL+"/users/:user_id", wrapper.DeleteUsersUserId) + router.GET(options.BaseURL+"/users/:user_id", wrapper.GetUsersUserId) + router.PATCH(options.BaseURL+"/users/:user_id", wrapper.PatchUsersUserId) + router.GET(options.BaseURL+"/users/:user_id/reviews", wrapper.GetUsersUserIdReviews) + router.DELETE(options.BaseURL+"/users/:user_id/titles", wrapper.DeleteUsersUserIdTitles) + router.GET(options.BaseURL+"/users/:user_id/titles", wrapper.GetUsersUserIdTitles) + router.PATCH(options.BaseURL+"/users/:user_id/titles", wrapper.PatchUsersUserIdTitles) + router.POST(options.BaseURL+"/users/:user_id/titles", wrapper.PostUsersUserIdTitles) +} + +type GetMediaRequestObject struct { + Params GetMediaParams +} + +type GetMediaResponseObject interface { + VisitGetMediaResponse(w http.ResponseWriter) error +} + +type GetMedia200JSONResponse struct { + Error *string `json:"error,omitempty"` + ImagePath *string `json:"image_path,omitempty"` + Success *bool `json:"success,omitempty"` +} + +func (response GetMedia200JSONResponse) VisitGetMediaResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type PostMediaRequestObject struct { +} + +type PostMediaResponseObject interface { + VisitPostMediaResponse(w http.ResponseWriter) error +} + +type PostMedia200JSONResponse struct { + Error *string `json:"error,omitempty"` + ImageId *string `json:"image_id,omitempty"` + Success *bool `json:"success,omitempty"` +} + +func (response PostMedia200JSONResponse) VisitPostMediaResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type PostReviewsRequestObject struct { + Body *PostReviewsJSONRequestBody +} + +type PostReviewsResponseObject interface { + VisitPostReviewsResponse(w http.ResponseWriter) error +} + +type PostReviews200JSONResponse struct { + Error *string `json:"error,omitempty"` + Success *bool `json:"success,omitempty"` +} + +func (response PostReviews200JSONResponse) VisitPostReviewsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type DeleteReviewsReviewIdRequestObject struct { + ReviewId string `json:"review_id"` +} + +type DeleteReviewsReviewIdResponseObject interface { + VisitDeleteReviewsReviewIdResponse(w http.ResponseWriter) error +} + +type DeleteReviewsReviewId200JSONResponse struct { + Error *string `json:"error,omitempty"` + Success *bool `json:"success,omitempty"` +} + +func (response DeleteReviewsReviewId200JSONResponse) VisitDeleteReviewsReviewIdResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type PatchReviewsReviewIdRequestObject struct { + ReviewId string `json:"review_id"` + Body *PatchReviewsReviewIdJSONRequestBody +} + +type PatchReviewsReviewIdResponseObject interface { + VisitPatchReviewsReviewIdResponse(w http.ResponseWriter) error +} + +type PatchReviewsReviewId200JSONResponse struct { + Error *string `json:"error,omitempty"` + Success *bool `json:"success,omitempty"` +} + +func (response PatchReviewsReviewId200JSONResponse) VisitPatchReviewsReviewIdResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetTagsRequestObject struct { + Params GetTagsParams +} + +type GetTagsResponseObject interface { + VisitGetTagsResponse(w http.ResponseWriter) error +} + +type GetTags200JSONResponse []Tag + +func (response GetTags200JSONResponse) VisitGetTagsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetTitleRequestObject struct { + Params GetTitleParams +} + +type GetTitleResponseObject interface { + VisitGetTitleResponse(w http.ResponseWriter) error +} + +type GetTitle200JSONResponse []Title + +func (response GetTitle200JSONResponse) VisitGetTitleResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetTitle204Response struct { +} + +func (response GetTitle204Response) VisitGetTitleResponse(w http.ResponseWriter) error { + w.WriteHeader(204) + return nil +} + +type GetTitleTitleIdRequestObject struct { + TitleId string `json:"title_id"` + Params GetTitleTitleIdParams +} + +type GetTitleTitleIdResponseObject interface { + VisitGetTitleTitleIdResponse(w http.ResponseWriter) error +} + +type GetTitleTitleId200JSONResponse Title + +func (response GetTitleTitleId200JSONResponse) VisitGetTitleTitleIdResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetTitleTitleId404Response struct { +} + +func (response GetTitleTitleId404Response) VisitGetTitleTitleIdResponse(w http.ResponseWriter) error { + w.WriteHeader(404) + return nil +} + +type PatchTitleTitleIdRequestObject struct { + TitleId string `json:"title_id"` + Body *PatchTitleTitleIdJSONRequestBody +} + +type PatchTitleTitleIdResponseObject interface { + VisitPatchTitleTitleIdResponse(w http.ResponseWriter) error +} + +type PatchTitleTitleId200JSONResponse struct { + Error *string `json:"error,omitempty"` + Success *bool `json:"success,omitempty"` + UserJson *User `json:"user_json,omitempty"` +} + +func (response PatchTitleTitleId200JSONResponse) VisitPatchTitleTitleIdResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetTitleTitleIdReviewsRequestObject struct { + TitleId string `json:"title_id"` + Params GetTitleTitleIdReviewsParams +} + +type GetTitleTitleIdReviewsResponseObject interface { + VisitGetTitleTitleIdReviewsResponse(w http.ResponseWriter) error +} + +type GetTitleTitleIdReviews200JSONResponse []Review + +func (response GetTitleTitleIdReviews200JSONResponse) VisitGetTitleTitleIdReviewsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetTitleTitleIdReviews204Response struct { +} + +func (response GetTitleTitleIdReviews204Response) VisitGetTitleTitleIdReviewsResponse(w http.ResponseWriter) error { + w.WriteHeader(204) + return nil +} + +type GetUsersRequestObject struct { + Params GetUsersParams +} + +type GetUsersResponseObject interface { + VisitGetUsersResponse(w http.ResponseWriter) error +} + +type GetUsers200JSONResponse []User + +func (response GetUsers200JSONResponse) VisitGetUsersResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type PostUsersRequestObject struct { + Body *PostUsersJSONRequestBody +} + +type PostUsersResponseObject interface { + VisitPostUsersResponse(w http.ResponseWriter) error +} + +type PostUsers200JSONResponse struct { + Error *string `json:"error,omitempty"` + Success *bool `json:"success,omitempty"` + UserJson *User `json:"user_json,omitempty"` +} + +func (response PostUsers200JSONResponse) VisitPostUsersResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type DeleteUsersUserIdRequestObject struct { + UserId string `json:"user_id"` +} + +type DeleteUsersUserIdResponseObject interface { + VisitDeleteUsersUserIdResponse(w http.ResponseWriter) error +} + +type DeleteUsersUserId200JSONResponse struct { + Error *string `json:"error,omitempty"` + Success *bool `json:"success,omitempty"` +} + +func (response DeleteUsersUserId200JSONResponse) VisitDeleteUsersUserIdResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetUsersUserIdRequestObject struct { + UserId string `json:"user_id"` + Params GetUsersUserIdParams +} + +type GetUsersUserIdResponseObject interface { + VisitGetUsersUserIdResponse(w http.ResponseWriter) error +} + +type GetUsersUserId200JSONResponse User + +func (response GetUsersUserId200JSONResponse) VisitGetUsersUserIdResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetUsersUserId404Response struct { +} + +func (response GetUsersUserId404Response) VisitGetUsersUserIdResponse(w http.ResponseWriter) error { + w.WriteHeader(404) + return nil +} + +type PatchUsersUserIdRequestObject struct { + UserId string `json:"user_id"` + Body *PatchUsersUserIdJSONRequestBody +} + +type PatchUsersUserIdResponseObject interface { + VisitPatchUsersUserIdResponse(w http.ResponseWriter) error +} + +type PatchUsersUserId200JSONResponse struct { + Error *string `json:"error,omitempty"` + Success *bool `json:"success,omitempty"` +} + +func (response PatchUsersUserId200JSONResponse) VisitPatchUsersUserIdResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetUsersUserIdReviewsRequestObject struct { + UserId string `json:"user_id"` + Params GetUsersUserIdReviewsParams +} + +type GetUsersUserIdReviewsResponseObject interface { + VisitGetUsersUserIdReviewsResponse(w http.ResponseWriter) error +} + +type GetUsersUserIdReviews200JSONResponse []Review + +func (response GetUsersUserIdReviews200JSONResponse) VisitGetUsersUserIdReviewsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type DeleteUsersUserIdTitlesRequestObject struct { + UserId string `json:"user_id"` + Params DeleteUsersUserIdTitlesParams +} + +type DeleteUsersUserIdTitlesResponseObject interface { + VisitDeleteUsersUserIdTitlesResponse(w http.ResponseWriter) error +} + +type DeleteUsersUserIdTitles200JSONResponse struct { + Error *string `json:"error,omitempty"` + Success *bool `json:"success,omitempty"` +} + +func (response DeleteUsersUserIdTitles200JSONResponse) VisitDeleteUsersUserIdTitlesResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetUsersUserIdTitlesRequestObject struct { + UserId string `json:"user_id"` + Params GetUsersUserIdTitlesParams +} + +type GetUsersUserIdTitlesResponseObject interface { + VisitGetUsersUserIdTitlesResponse(w http.ResponseWriter) error +} + +type GetUsersUserIdTitles200JSONResponse []UserTitle + +func (response GetUsersUserIdTitles200JSONResponse) VisitGetUsersUserIdTitlesResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetUsersUserIdTitles204Response struct { +} + +func (response GetUsersUserIdTitles204Response) VisitGetUsersUserIdTitlesResponse(w http.ResponseWriter) error { + w.WriteHeader(204) + return nil +} + +type PatchUsersUserIdTitlesRequestObject struct { + UserId string `json:"user_id"` + Body *PatchUsersUserIdTitlesJSONRequestBody +} + +type PatchUsersUserIdTitlesResponseObject interface { + VisitPatchUsersUserIdTitlesResponse(w http.ResponseWriter) error +} + +type PatchUsersUserIdTitles200JSONResponse struct { + Error *string `json:"error,omitempty"` + Success *bool `json:"success,omitempty"` +} + +func (response PatchUsersUserIdTitles200JSONResponse) VisitPatchUsersUserIdTitlesResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type PostUsersUserIdTitlesRequestObject struct { + UserId string `json:"user_id"` + Body *PostUsersUserIdTitlesJSONRequestBody +} + +type PostUsersUserIdTitlesResponseObject interface { + VisitPostUsersUserIdTitlesResponse(w http.ResponseWriter) error +} + +type PostUsersUserIdTitles200JSONResponse struct { + Error *string `json:"error,omitempty"` + Success *bool `json:"success,omitempty"` +} + +func (response PostUsersUserIdTitles200JSONResponse) VisitPostUsersUserIdTitlesResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +// StrictServerInterface represents all server handlers. +type StrictServerInterface interface { + // Get image path + // (GET /media) + GetMedia(ctx context.Context, request GetMediaRequestObject) (GetMediaResponseObject, error) + // Upload image + // (POST /media) + PostMedia(ctx context.Context, request PostMediaRequestObject) (PostMediaResponseObject, error) + // Add review + // (POST /reviews) + PostReviews(ctx context.Context, request PostReviewsRequestObject) (PostReviewsResponseObject, error) + // Delete review + // (DELETE /reviews/{review_id}) + DeleteReviewsReviewId(ctx context.Context, request DeleteReviewsReviewIdRequestObject) (DeleteReviewsReviewIdResponseObject, error) + // Update review + // (PATCH /reviews/{review_id}) + PatchReviewsReviewId(ctx context.Context, request PatchReviewsReviewIdRequestObject) (PatchReviewsReviewIdResponseObject, error) + // Get tags + // (GET /tags) + GetTags(ctx context.Context, request GetTagsRequestObject) (GetTagsResponseObject, error) + // Get titles + // (GET /title) + GetTitle(ctx context.Context, request GetTitleRequestObject) (GetTitleResponseObject, error) + // Get title description + // (GET /title/{title_id}) + GetTitleTitleId(ctx context.Context, request GetTitleTitleIdRequestObject) (GetTitleTitleIdResponseObject, error) + // Update title info + // (PATCH /title/{title_id}) + PatchTitleTitleId(ctx context.Context, request PatchTitleTitleIdRequestObject) (PatchTitleTitleIdResponseObject, error) + // Get title reviews + // (GET /title/{title_id}/reviews) + GetTitleTitleIdReviews(ctx context.Context, request GetTitleTitleIdReviewsRequestObject) (GetTitleTitleIdReviewsResponseObject, error) + // Search user + // (GET /users) + GetUsers(ctx context.Context, request GetUsersRequestObject) (GetUsersResponseObject, error) + // Add new user + // (POST /users) + PostUsers(ctx context.Context, request PostUsersRequestObject) (PostUsersResponseObject, error) + // Delete user + // (DELETE /users/{user_id}) + DeleteUsersUserId(ctx context.Context, request DeleteUsersUserIdRequestObject) (DeleteUsersUserIdResponseObject, error) + // Get user info + // (GET /users/{user_id}) + GetUsersUserId(ctx context.Context, request GetUsersUserIdRequestObject) (GetUsersUserIdResponseObject, error) + // Update user + // (PATCH /users/{user_id}) + PatchUsersUserId(ctx context.Context, request PatchUsersUserIdRequestObject) (PatchUsersUserIdResponseObject, error) + // Get user reviews + // (GET /users/{user_id}/reviews) + GetUsersUserIdReviews(ctx context.Context, request GetUsersUserIdReviewsRequestObject) (GetUsersUserIdReviewsResponseObject, error) + // Delete user title + // (DELETE /users/{user_id}/titles) + DeleteUsersUserIdTitles(ctx context.Context, request DeleteUsersUserIdTitlesRequestObject) (DeleteUsersUserIdTitlesResponseObject, error) + // Get user titles + // (GET /users/{user_id}/titles) + GetUsersUserIdTitles(ctx context.Context, request GetUsersUserIdTitlesRequestObject) (GetUsersUserIdTitlesResponseObject, error) + // Update user title + // (PATCH /users/{user_id}/titles) + PatchUsersUserIdTitles(ctx context.Context, request PatchUsersUserIdTitlesRequestObject) (PatchUsersUserIdTitlesResponseObject, error) + // Add user title + // (POST /users/{user_id}/titles) + PostUsersUserIdTitles(ctx context.Context, request PostUsersUserIdTitlesRequestObject) (PostUsersUserIdTitlesResponseObject, error) +} + +type StrictHandlerFunc = strictgin.StrictGinHandlerFunc +type StrictMiddlewareFunc = strictgin.StrictGinMiddlewareFunc + +func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface { + return &strictHandler{ssi: ssi, middlewares: middlewares} +} + +type strictHandler struct { + ssi StrictServerInterface + middlewares []StrictMiddlewareFunc +} + +// GetMedia operation middleware +func (sh *strictHandler) GetMedia(ctx *gin.Context, params GetMediaParams) { + var request GetMediaRequestObject + + request.Params = params + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.GetMedia(ctx, request.(GetMediaRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetMedia") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(GetMediaResponseObject); ok { + if err := validResponse.VisitGetMediaResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// PostMedia operation middleware +func (sh *strictHandler) PostMedia(ctx *gin.Context) { + var request PostMediaRequestObject + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.PostMedia(ctx, request.(PostMediaRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PostMedia") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(PostMediaResponseObject); ok { + if err := validResponse.VisitPostMediaResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// PostReviews operation middleware +func (sh *strictHandler) PostReviews(ctx *gin.Context) { + var request PostReviewsRequestObject + + var body PostReviewsJSONRequestBody + if err := ctx.ShouldBindJSON(&body); err != nil { + ctx.Status(http.StatusBadRequest) + ctx.Error(err) + return + } + request.Body = &body + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.PostReviews(ctx, request.(PostReviewsRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PostReviews") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(PostReviewsResponseObject); ok { + if err := validResponse.VisitPostReviewsResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// DeleteReviewsReviewId operation middleware +func (sh *strictHandler) DeleteReviewsReviewId(ctx *gin.Context, reviewId string) { + var request DeleteReviewsReviewIdRequestObject + + request.ReviewId = reviewId + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.DeleteReviewsReviewId(ctx, request.(DeleteReviewsReviewIdRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "DeleteReviewsReviewId") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(DeleteReviewsReviewIdResponseObject); ok { + if err := validResponse.VisitDeleteReviewsReviewIdResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// PatchReviewsReviewId operation middleware +func (sh *strictHandler) PatchReviewsReviewId(ctx *gin.Context, reviewId string) { + var request PatchReviewsReviewIdRequestObject + + request.ReviewId = reviewId + + var body PatchReviewsReviewIdJSONRequestBody + if err := ctx.ShouldBindJSON(&body); err != nil { + ctx.Status(http.StatusBadRequest) + ctx.Error(err) + return + } + request.Body = &body + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.PatchReviewsReviewId(ctx, request.(PatchReviewsReviewIdRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PatchReviewsReviewId") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(PatchReviewsReviewIdResponseObject); ok { + if err := validResponse.VisitPatchReviewsReviewIdResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// GetTags operation middleware +func (sh *strictHandler) GetTags(ctx *gin.Context, params GetTagsParams) { + var request GetTagsRequestObject + + request.Params = params + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.GetTags(ctx, request.(GetTagsRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetTags") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(GetTagsResponseObject); ok { + if err := validResponse.VisitGetTagsResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// GetTitle operation middleware +func (sh *strictHandler) GetTitle(ctx *gin.Context, params GetTitleParams) { + var request GetTitleRequestObject + + request.Params = params + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.GetTitle(ctx, request.(GetTitleRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetTitle") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(GetTitleResponseObject); ok { + if err := validResponse.VisitGetTitleResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// GetTitleTitleId operation middleware +func (sh *strictHandler) GetTitleTitleId(ctx *gin.Context, titleId string, params GetTitleTitleIdParams) { + var request GetTitleTitleIdRequestObject + + request.TitleId = titleId + request.Params = params + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.GetTitleTitleId(ctx, request.(GetTitleTitleIdRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetTitleTitleId") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(GetTitleTitleIdResponseObject); ok { + if err := validResponse.VisitGetTitleTitleIdResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// PatchTitleTitleId operation middleware +func (sh *strictHandler) PatchTitleTitleId(ctx *gin.Context, titleId string) { + var request PatchTitleTitleIdRequestObject + + request.TitleId = titleId + + var body PatchTitleTitleIdJSONRequestBody + if err := ctx.ShouldBindJSON(&body); err != nil { + ctx.Status(http.StatusBadRequest) + ctx.Error(err) + return + } + request.Body = &body + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.PatchTitleTitleId(ctx, request.(PatchTitleTitleIdRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PatchTitleTitleId") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(PatchTitleTitleIdResponseObject); ok { + if err := validResponse.VisitPatchTitleTitleIdResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// GetTitleTitleIdReviews operation middleware +func (sh *strictHandler) GetTitleTitleIdReviews(ctx *gin.Context, titleId string, params GetTitleTitleIdReviewsParams) { + var request GetTitleTitleIdReviewsRequestObject + + request.TitleId = titleId + request.Params = params + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.GetTitleTitleIdReviews(ctx, request.(GetTitleTitleIdReviewsRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetTitleTitleIdReviews") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(GetTitleTitleIdReviewsResponseObject); ok { + if err := validResponse.VisitGetTitleTitleIdReviewsResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// GetUsers operation middleware +func (sh *strictHandler) GetUsers(ctx *gin.Context, params GetUsersParams) { + var request GetUsersRequestObject + + request.Params = params + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.GetUsers(ctx, request.(GetUsersRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetUsers") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(GetUsersResponseObject); ok { + if err := validResponse.VisitGetUsersResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// PostUsers operation middleware +func (sh *strictHandler) PostUsers(ctx *gin.Context) { + var request PostUsersRequestObject + + var body PostUsersJSONRequestBody + if err := ctx.ShouldBindJSON(&body); err != nil { + ctx.Status(http.StatusBadRequest) + ctx.Error(err) + return + } + request.Body = &body + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.PostUsers(ctx, request.(PostUsersRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PostUsers") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(PostUsersResponseObject); ok { + if err := validResponse.VisitPostUsersResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// DeleteUsersUserId operation middleware +func (sh *strictHandler) DeleteUsersUserId(ctx *gin.Context, userId string) { + var request DeleteUsersUserIdRequestObject + + request.UserId = userId + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.DeleteUsersUserId(ctx, request.(DeleteUsersUserIdRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "DeleteUsersUserId") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(DeleteUsersUserIdResponseObject); ok { + if err := validResponse.VisitDeleteUsersUserIdResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// GetUsersUserId operation middleware +func (sh *strictHandler) GetUsersUserId(ctx *gin.Context, userId string, params GetUsersUserIdParams) { + var request GetUsersUserIdRequestObject + + request.UserId = userId + request.Params = params + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.GetUsersUserId(ctx, request.(GetUsersUserIdRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetUsersUserId") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(GetUsersUserIdResponseObject); ok { + if err := validResponse.VisitGetUsersUserIdResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// PatchUsersUserId operation middleware +func (sh *strictHandler) PatchUsersUserId(ctx *gin.Context, userId string) { + var request PatchUsersUserIdRequestObject + + request.UserId = userId + + var body PatchUsersUserIdJSONRequestBody + if err := ctx.ShouldBindJSON(&body); err != nil { + ctx.Status(http.StatusBadRequest) + ctx.Error(err) + return + } + request.Body = &body + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.PatchUsersUserId(ctx, request.(PatchUsersUserIdRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PatchUsersUserId") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(PatchUsersUserIdResponseObject); ok { + if err := validResponse.VisitPatchUsersUserIdResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// GetUsersUserIdReviews operation middleware +func (sh *strictHandler) GetUsersUserIdReviews(ctx *gin.Context, userId string, params GetUsersUserIdReviewsParams) { + var request GetUsersUserIdReviewsRequestObject + + request.UserId = userId + request.Params = params + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.GetUsersUserIdReviews(ctx, request.(GetUsersUserIdReviewsRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetUsersUserIdReviews") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(GetUsersUserIdReviewsResponseObject); ok { + if err := validResponse.VisitGetUsersUserIdReviewsResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// DeleteUsersUserIdTitles operation middleware +func (sh *strictHandler) DeleteUsersUserIdTitles(ctx *gin.Context, userId string, params DeleteUsersUserIdTitlesParams) { + var request DeleteUsersUserIdTitlesRequestObject + + request.UserId = userId + request.Params = params + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.DeleteUsersUserIdTitles(ctx, request.(DeleteUsersUserIdTitlesRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "DeleteUsersUserIdTitles") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(DeleteUsersUserIdTitlesResponseObject); ok { + if err := validResponse.VisitDeleteUsersUserIdTitlesResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// GetUsersUserIdTitles operation middleware +func (sh *strictHandler) GetUsersUserIdTitles(ctx *gin.Context, userId string, params GetUsersUserIdTitlesParams) { + var request GetUsersUserIdTitlesRequestObject + + request.UserId = userId + request.Params = params + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.GetUsersUserIdTitles(ctx, request.(GetUsersUserIdTitlesRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetUsersUserIdTitles") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(GetUsersUserIdTitlesResponseObject); ok { + if err := validResponse.VisitGetUsersUserIdTitlesResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// PatchUsersUserIdTitles operation middleware +func (sh *strictHandler) PatchUsersUserIdTitles(ctx *gin.Context, userId string) { + var request PatchUsersUserIdTitlesRequestObject + + request.UserId = userId + + var body PatchUsersUserIdTitlesJSONRequestBody + if err := ctx.ShouldBindJSON(&body); err != nil { + ctx.Status(http.StatusBadRequest) + ctx.Error(err) + return + } + request.Body = &body + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.PatchUsersUserIdTitles(ctx, request.(PatchUsersUserIdTitlesRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PatchUsersUserIdTitles") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(PatchUsersUserIdTitlesResponseObject); ok { + if err := validResponse.VisitPatchUsersUserIdTitlesResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// PostUsersUserIdTitles operation middleware +func (sh *strictHandler) PostUsersUserIdTitles(ctx *gin.Context, userId string) { + var request PostUsersUserIdTitlesRequestObject + + request.UserId = userId + + var body PostUsersUserIdTitlesJSONRequestBody + if err := ctx.ShouldBindJSON(&body); err != nil { + ctx.Status(http.StatusBadRequest) + ctx.Error(err) + return + } + request.Body = &body + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.PostUsersUserIdTitles(ctx, request.(PostUsersUserIdTitlesRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PostUsersUserIdTitles") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(PostUsersUserIdTitlesResponseObject); ok { + if err := validResponse.VisitPostUsersUserIdTitlesResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} diff --git a/modules/backend/api/impl.go b/modules/backend/api/impl.go new file mode 100644 index 0000000..5783f47 --- /dev/null +++ b/modules/backend/api/impl.go @@ -0,0 +1,718 @@ +package api + +import ( + "context" + "encoding/json" + "nyanimedb-server/db" + "strconv" + "time" + + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgtype" + "golang.org/x/crypto/bcrypt" +) + +type Server struct { + db *db.Queries +} + +func NewServer(db *db.Queries) Server { + return Server{db: db} +} + +// ————————————————————————————————————————————— +// ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ +// ————————————————————————————————————————————— + +func parseInt32(s string) (int32, error) { + i, err := strconv.ParseInt(s, 10, 32) + return int32(i), err +} + +func ptr[T any](v T) *T { return &v } + +func pgInt4ToPtr(v pgtype.Int4) *int32 { + if v.Valid { + return &v.Int32 + } + return nil +} + +func pgTextToPtr(v pgtype.Text) *string { + if v.Valid { + return &v.String + } + return nil +} + +func pgFloat8ToPtr(v pgtype.Float8) *float64 { + if v.Valid { + return &v.Float64 + } + return nil +} + +func jsonbToInterface(data []byte) interface{} { + if data == nil { + return nil + } + var out interface{} + if err := json.Unmarshal(data, &out); err != nil { + return string(data) // fallback + } + return out +} + +// ————————————————————————————————————————————— +// ХЕНДЛЕРЫ +// ————————————————————————————————————————————— + +func (s Server) GetMedia(ctx context.Context, req GetMediaRequestObject) (GetMediaResponseObject, error) { + id, err := parseInt32(req.Params.ImageId) + if err != nil { + return GetMedia200JSONResponse{Success: ptr(false), Error: ptr("invalid image_id")}, nil + } + img, err := s.db.GetImageByID(ctx, id) + if err != nil { + if err == pgx.ErrNoRows { + return GetMedia200JSONResponse{Success: ptr(false), Error: ptr("image not found")}, nil + } + return nil, err + } + return GetMedia200JSONResponse{ + Success: ptr(true), + ImagePath: ptr(img.ImagePath), + }, nil +} + +func (s Server) PostMedia(ctx context.Context, req PostMediaRequestObject) (PostMediaResponseObject, error) { + // ❗ Не реализовано: OpenAPI не определяет тело запроса для загрузки + return PostMedia200JSONResponse{ + Success: ptr(false), + Error: ptr("upload not implemented: request body not defined in spec"), + }, nil +} + +func (s Server) GetUsers(ctx context.Context, req GetUsersRequestObject) (GetUsersResponseObject, error) { + users, err := s.db.ListUsers(ctx, db.ListUsersParams{}) + if err != nil { + return nil, err + } + var resp []User + for _, u := range users { + resp = append(resp, mapUser(u)) + } + return GetUsers200JSONResponse(resp), nil +} + +func (s Server) PostUsers(ctx context.Context, req PostUsersRequestObject) (PostUsersResponseObject, error) { + if req.Body == nil { + return PostUsers200JSONResponse{ + Success: ptr(false), + Error: ptr("request body is required"), + }, nil + } + + body := *req.Body + + // Обязательные поля + nickname, ok := body["nickname"].(string) + if !ok || nickname == "" { + return PostUsers200JSONResponse{Success: ptr(false), Error: ptr("nickname is required")}, nil + } + + mail, ok := body["mail"].(string) + if !ok || mail == "" { + return PostUsers200JSONResponse{Success: ptr(false), Error: ptr("mail is required")}, nil + } + + password, ok := body["password"].(string) + if !ok || password == "" { + return PostUsers200JSONResponse{Success: ptr(false), Error: ptr("password is required")}, nil + } + + // Опциональные поля + var avatarID *int32 + if v, ok := body["avatar_id"].(float64); ok { + id := int32(v) + avatarID = &id + } + + dispName, _ := body["disp_name"].(string) + userDesc, _ := body["user_desc"].(string) + + // 🔐 Хешируем пароль + passhashBytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + if err != nil { + return PostUsers200JSONResponse{Success: ptr(false), Error: ptr("failed to hash password")}, nil + } + passhash := string(passhashBytes) + + // Сохраняем в БД + _, err = s.db.CreateUser(ctx, db.CreateUserParams{ + AvatarID: pgtype.Int4{ + Int32: 0, + Valid: avatarID != nil, + }, + Passhash: passhash, + Mail: pgtype.Text{ + String: mail, + Valid: true, + }, + Nickname: nickname, + DispName: pgtype.Text{ + String: dispName, + Valid: dispName != "", + }, + UserDesc: pgtype.Text{ + String: userDesc, + Valid: userDesc != "", + }, + CreationDate: pgtype.Timestamp{ + Time: time.Now(), + Valid: true, + }, + }) + if err != nil { + // Проверяем нарушение уникальности (например, дубль mail или nickname) + if err.Error() == "ERROR: duplicate key value violates unique constraint \"users_mail_key\"" || + err.Error() == "ERROR: duplicate key value violates unique constraint \"users_nickname_key\"" { + return PostUsers200JSONResponse{ + Success: ptr(false), + Error: ptr("user with this email or nickname already exists"), + }, nil + } + return PostUsers200JSONResponse{Success: ptr(false), Error: ptr("database error")}, nil + } + + // Получаем созданного пользователя (без passhash и mail!) + // Предположим, что у вас есть запрос GetUserByNickname или аналогичный + // Но проще — вернуть только ID и nickname + + // ⚠️ Поскольку мы не знаем user_id, можно: + // а) добавить RETURNING в CreateUser (рекомендуется), + // б) сделать отдельный SELECT. + + // Пока вернём минимальный ответ + userResp := User{ + "nickname": nickname, + // "user_id" можно добавить, если обновите query.sql + } + + return PostUsers200JSONResponse{ + Success: ptr(true), + UserJson: &userResp, + }, nil +} +func (s Server) DeleteUsersUserId(ctx context.Context, req DeleteUsersUserIdRequestObject) (DeleteUsersUserIdResponseObject, error) { + userID, err := parseInt32(req.UserId) + if err != nil { + return DeleteUsersUserId200JSONResponse{Success: ptr(false), Error: ptr("invalid user_id")}, nil + } + err = s.db.DeleteUser(ctx, userID) + if err != nil { + if err == pgx.ErrNoRows { + return DeleteUsersUserId200JSONResponse{Success: ptr(false), Error: ptr("user not found")}, nil + } + return nil, err + } + return DeleteUsersUserId200JSONResponse{Success: ptr(true)}, nil +} + +func (s Server) GetUsersUserId(ctx context.Context, req GetUsersUserIdRequestObject) (GetUsersUserIdResponseObject, error) { + userID, err := parseInt32(req.UserId) + if err != nil { + return GetUsersUserId404Response{}, nil + } + user, err := s.db.GetUserByID(ctx, userID) + if err != nil { + if err == pgx.ErrNoRows { + return GetUsersUserId404Response{}, nil + } + return nil, err + } + return GetUsersUserId200JSONResponse(mapUser(user)), nil +} + +func (s Server) PatchUsersUserId(ctx context.Context, req PatchUsersUserIdRequestObject) (PatchUsersUserIdResponseObject, error) { + userID, err := parseInt32(req.UserId) + if err != nil { + return PatchUsersUserId200JSONResponse{Success: ptr(false), Error: ptr("invalid user_id")}, nil + } + if req.Body == nil { + return PatchUsersUserId200JSONResponse{Success: ptr(false), Error: ptr("empty body")}, nil + } + + body := *req.Body + args := db.UpdateUserParams{ + UserID: userID, + } + + if v, ok := body["avatar_id"].(float64); ok { + args.AvatarID = pgtype.Int4{Int32: int32(v), Valid: true} + // args.AvatarIDValid = true + } + if v, ok := body["disp_name"].(string); ok { + args.DispName = pgtype.Text{String: v, Valid: true} + // args.DispNameValid = true + } + if v, ok := body["user_desc"].(string); ok { + args.UserDesc = pgtype.Text{String: v, Valid: true} + // args.UserDescValid = true + } + + _, err = s.db.UpdateUser(ctx, args) + if err != nil { + return PatchUsersUserId200JSONResponse{Success: ptr(false), Error: ptr(err.Error())}, nil + } + return PatchUsersUserId200JSONResponse{Success: ptr(true)}, nil +} + +func (s Server) GetUsersUserIdReviews(ctx context.Context, req GetUsersUserIdReviewsRequestObject) (GetUsersUserIdReviewsResponseObject, error) { + userID, err := parseInt32(req.UserId) + if err != nil { + return GetUsersUserIdReviews200JSONResponse{}, nil + } + limit := int32(20) + offset := int32(0) + // if req.Params.Limit != nil { + // limit = int32(*req.Params.Limit) + // } + // if req.Params.Offset != nil { + // offset = int32(*req.Params.Offset) + // } + + reviews, err := s.db.ListReviewsByUser(ctx, db.ListReviewsByUserParams{ + UserID: userID, + Limit: limit, + Offset: offset, + }) + if err != nil { + return nil, err + } + + var resp []Review + for _, r := range reviews { + resp = append(resp, mapReview(r)) + } + return GetUsersUserIdReviews200JSONResponse(resp), nil +} + +func (s Server) DeleteUsersUserIdTitles(ctx context.Context, req DeleteUsersUserIdTitlesRequestObject) (DeleteUsersUserIdTitlesResponseObject, error) { + userID, err := parseInt32(req.UserId) + if err != nil { + return DeleteUsersUserIdTitles200JSONResponse{Success: ptr(false), Error: ptr("invalid user_id")}, nil + } + + if req.Params.TitleId != nil { + titleID, err := parseInt32(*req.Params.TitleId) + if err != nil { + return DeleteUsersUserIdTitles200JSONResponse{Success: ptr(false), Error: ptr("invalid title_id")}, nil + } + err = s.db.DeleteUserTitle(ctx, db.DeleteUserTitleParams{ + UserID: userID, + Column2: titleID, + }) + if err != nil && err != pgx.ErrNoRows { + return nil, err + } + } + // else { + // err = s.db.DeleteAllUserTitles(ctx, userID) + // if err != nil { + // return nil, err + // } + // } + return DeleteUsersUserIdTitles200JSONResponse{Success: ptr(true)}, nil +} + +func (s Server) GetUsersUserIdTitles(ctx context.Context, req GetUsersUserIdTitlesRequestObject) (GetUsersUserIdTitlesResponseObject, error) { + userID, err := parseInt32(req.UserId) + if err != nil { + return GetUsersUserIdTitles200JSONResponse{}, nil + } + limit := int32(100) + offset := int32(0) + if req.Params.Limit != nil { + limit = int32(*req.Params.Limit) + } + if req.Params.Offset != nil { + offset = int32(*req.Params.Offset) + } + + titles, err := s.db.ListUserTitles(ctx, db.ListUserTitlesParams{ + UserID: userID, + Limit: limit, + Offset: offset, + }) + if err != nil { + return nil, err + } + + var resp []UserTitle + for _, t := range titles { + resp = append(resp, mapUserTitle(t)) + } + return GetUsersUserIdTitles200JSONResponse(resp), nil +} + +func (s Server) PatchUsersUserIdTitles(ctx context.Context, req PatchUsersUserIdTitlesRequestObject) (PatchUsersUserIdTitlesResponseObject, error) { + // userID, err := parseInt32(req.UserId) + // if err != nil { + // return PatchUsersUserIdTitles200JSONResponse{Success: ptr(false), Error: ptr("invalid user_id")}, nil + // } + // if req.Body == nil { + // return PatchUsersUserIdTitles200JSONResponse{Success: ptr(false), Error: ptr("empty body")}, nil + // } + + // body := *req.Body + // titleID, ok := body["title_id"].(float64) + // if !ok { + // return PatchUsersUserIdTitles200JSONResponse{Success: ptr(false), Error: ptr("title_id required")}, nil + // } + + // args := db.UpdateUserTitleParams{ + // UserID: userID, + // TitleID: int32(titleID), + // } + + // if v, ok := body["status"].(string); ok { + // args.Status = db.UsertitleStatusT(v) + // // args.StatusValid = true + // } + // if v, ok := body["rate"].(float64); ok { + // args.Rate = pgtype.Int4{Int32: int32(v), Valid: true} + // // args.RateValid = true + // } + // if v, ok := body["review_id"].(float64); ok { + // args.ReviewID = pgtype.Int4{Int32: int32(v), Valid: true} + // // args.ReviewIDValid = true + // } + + // _, err = s.db.UpdateUserTitle(ctx, args) + // if err != nil { + // return PatchUsersUserIdTitles200JSONResponse{Success: ptr(false), Error: ptr(err.Error())}, nil + // } + return PatchUsersUserIdTitles200JSONResponse{Success: ptr(true)}, nil +} + +func (s Server) PostUsersUserIdTitles(ctx context.Context, req PostUsersUserIdTitlesRequestObject) (PostUsersUserIdTitlesResponseObject, error) { + userID, err := parseInt32(req.UserId) + if err != nil { + return PostUsersUserIdTitles200JSONResponse{Success: ptr(false), Error: ptr("invalid user_id")}, nil + } + if req.Body == nil { + return PostUsersUserIdTitles200JSONResponse{Success: ptr(false), Error: ptr("empty body")}, nil + } + + body := req.Body + titleID, err := parseInt32(*body.TitleId) + if err != nil { + return PostUsersUserIdTitles200JSONResponse{Success: ptr(false), Error: ptr("invalid title_id")}, nil + } + + status := db.UsertitleStatusT("planned") + if body.Status != nil { + status = db.UsertitleStatusT(*body.Status) + } + + _, err = s.db.CreateUserTitle(ctx, db.CreateUserTitleParams{ + UserID: userID, + TitleID: titleID, + Status: status, + Rate: pgtype.Int4{Valid: false}, + ReviewID: pgtype.Int4{Valid: false}, + }) + if err != nil { + return PostUsersUserIdTitles200JSONResponse{Success: ptr(false), Error: ptr(err.Error())}, nil + } + return PostUsersUserIdTitles200JSONResponse{Success: ptr(true)}, nil +} + +func (s Server) GetTags(ctx context.Context, req GetTagsRequestObject) (GetTagsResponseObject, error) { + limit := int32(100) + offset := int32(0) + if req.Params.Limit != nil { + limit = int32(*req.Params.Limit) + } + if req.Params.Offset != nil { + offset = int32(*req.Params.Offset) + } + tags, err := s.db.ListTags(ctx, db.ListTagsParams{Limit: limit, Offset: offset}) + if err != nil { + return nil, err + } + var resp []Tag + for _, t := range tags { + resp = append(resp, Tag{ + "tag_id": t.TagID, + "tag_names": jsonbToInterface(t.TagNames), + }) + } + return GetTags200JSONResponse(resp), nil +} + +func (s Server) GetTitle(ctx context.Context, req GetTitleRequestObject) (GetTitleResponseObject, error) { + limit := int32(50) + offset := int32(0) + if req.Params.Limit != nil { + limit = int32(*req.Params.Limit) + } + if req.Params.Offset != nil { + offset = int32(*req.Params.Offset) + } + titles, err := s.db.ListTitles(ctx, db.ListTitlesParams{Limit: limit, Offset: offset}) + if err != nil { + return nil, err + } + var resp []Title + for _, t := range titles { + resp = append(resp, mapTitle(t)) + } + return GetTitle200JSONResponse(resp), nil +} + +func (s Server) GetTitleTitleId(ctx context.Context, req GetTitleTitleIdRequestObject) (GetTitleTitleIdResponseObject, error) { + titleID, err := parseInt32(req.TitleId) + if err != nil { + return GetTitleTitleId404Response{}, nil + } + title, err := s.db.GetTitleByID(ctx, titleID) + if err != nil { + if err == pgx.ErrNoRows { + return GetTitleTitleId404Response{}, nil + } + return nil, err + } + return GetTitleTitleId200JSONResponse(mapTitle(title)), nil +} + +func (s Server) PatchTitleTitleId(ctx context.Context, req PatchTitleTitleIdRequestObject) (PatchTitleTitleIdResponseObject, error) { + titleID, err := parseInt32(req.TitleId) + if err != nil { + return PatchTitleTitleId200JSONResponse{Success: ptr(false), Error: ptr("invalid title_id")}, nil + } + if req.Body == nil { + return PatchTitleTitleId200JSONResponse{Success: ptr(false), Error: ptr("empty body")}, nil + } + + body := *req.Body + args := db.UpdateTitleParams{ + TitleID: titleID, + } + + if v, ok := body["title_names"].(map[string]interface{}); ok { + data, _ := json.Marshal(v) + args.TitleNames = data + // args.TitleNamesValid = true + } + if v, ok := body["studio_id"].(float64); ok { + args.StudioID = pgtype.Int4{Int32: int32(v), Valid: true} + // args.StudioIDValid = true + } + if v, ok := body["poster_id"].(float64); ok { + args.PosterID = pgtype.Int4{Int32: int32(v), Valid: true} + // args.PosterIDValid = true + } + // if v, ok := body["title_status"].(string); ok { + // args.TitleStatus = db.NullTitleStatusT(v) + // // args.TitleStatusValid = true + // } + if v, ok := body["release_year"].(float64); ok { + args.ReleaseYear = pgtype.Int4{Int32: int32(v), Valid: true} + // args.ReleaseYearValid = true + } + if v, ok := body["episodes_aired"].(float64); ok { + args.EpisodesAired = pgtype.Int4{Int32: int32(v), Valid: true} + // args.EpisodesAiredValid = true + } + if v, ok := body["episodes_all"].(float64); ok { + args.EpisodesAll = pgtype.Int4{Int32: int32(v), Valid: true} + // args.EpisodesAllValid = true + } + + _, err = s.db.UpdateTitle(ctx, args) + if err != nil { + return PatchTitleTitleId200JSONResponse{Success: ptr(false), Error: ptr(err.Error())}, nil + } + return PatchTitleTitleId200JSONResponse{Success: ptr(true)}, nil +} + +func (s Server) GetTitleTitleIdReviews(ctx context.Context, req GetTitleTitleIdReviewsRequestObject) (GetTitleTitleIdReviewsResponseObject, error) { + titleID, err := parseInt32(req.TitleId) + if err != nil { + return GetTitleTitleIdReviews200JSONResponse{}, nil + } + limit := int32(20) + offset := int32(0) + if req.Params.Limit != nil { + limit = int32(*req.Params.Limit) + } + if req.Params.Offset != nil { + offset = int32(*req.Params.Offset) + } + + reviews, err := s.db.ListReviewsByTitle(ctx, db.ListReviewsByTitleParams{ + TitleID: titleID, + Limit: limit, + Offset: offset, + }) + if err != nil { + return nil, err + } + + var resp []Review + for _, r := range reviews { + resp = append(resp, mapReview(r)) + } + return GetTitleTitleIdReviews200JSONResponse(resp), nil +} + +func (s Server) PostReviews(ctx context.Context, req PostReviewsRequestObject) (PostReviewsResponseObject, error) { + if req.Body == nil { + return PostReviews200JSONResponse{Success: ptr(false), Error: ptr("empty body")}, nil + } + + body := *req.Body + userID, ok1 := body["user_id"].(float64) + titleID, ok2 := body["title_id"].(float64) + reviewText, ok3 := body["review_text"].(string) + if !ok1 || !ok2 || !ok3 { + return PostReviews200JSONResponse{Success: ptr(false), Error: ptr("user_id, title_id, review_text required")}, nil + } + + var imageIDs []int32 + if ids, ok := body["image_ids"].([]interface{}); ok { + for _, id := range ids { + if f, ok := id.(float64); ok { + imageIDs = append(imageIDs, int32(f)) + } + } + } + + _, err := s.db.CreateReview(ctx, db.CreateReviewParams{ + UserID: int32(userID), + TitleID: int32(titleID), + ImageIds: imageIDs, + ReviewText: reviewText, + CreationDate: pgtype.Timestamp{Time: time.Now(), Valid: true}, + }) + if err != nil { + return PostReviews200JSONResponse{Success: ptr(false), Error: ptr(err.Error())}, nil + } + + return PostReviews200JSONResponse{Success: ptr(true)}, nil +} + +func (s Server) DeleteReviewsReviewId(ctx context.Context, req DeleteReviewsReviewIdRequestObject) (DeleteReviewsReviewIdResponseObject, error) { + reviewID, err := parseInt32(req.ReviewId) + if err != nil { + return DeleteReviewsReviewId200JSONResponse{Success: ptr(false), Error: ptr("invalid review_id")}, nil + } + err = s.db.DeleteReview(ctx, reviewID) + if err != nil { + if err == pgx.ErrNoRows { + return DeleteReviewsReviewId200JSONResponse{Success: ptr(false), Error: ptr("review not found")}, nil + } + return nil, err + } + return DeleteReviewsReviewId200JSONResponse{Success: ptr(true)}, nil +} + +func (s Server) PatchReviewsReviewId(ctx context.Context, req PatchReviewsReviewIdRequestObject) (PatchReviewsReviewIdResponseObject, error) { + reviewID, err := parseInt32(req.ReviewId) + if err != nil { + return PatchReviewsReviewId200JSONResponse{Success: ptr(false), Error: ptr("invalid review_id")}, nil + } + if req.Body == nil { + return PatchReviewsReviewId200JSONResponse{Success: ptr(false), Error: ptr("empty body")}, nil + } + + body := *req.Body + args := db.UpdateReviewParams{ + ReviewID: reviewID, + } + + if v, ok := body["review_text"].(string); ok { + args.ReviewText = pgtype.Text{String: v, Valid: true} + // args.ReviewTextValid = true + } + if ids, ok := body["image_ids"].([]interface{}); ok { + var imageIDs []int32 + for _, id := range ids { + if f, ok := id.(float64); ok { + imageIDs = append(imageIDs, int32(f)) + } + } + args.ImageIds = imageIDs + // args.ImageIdsValid = true + } + + _, err = s.db.UpdateReview(ctx, args) + if err != nil { + return PatchReviewsReviewId200JSONResponse{Success: ptr(false), Error: ptr(err.Error())}, nil + } + return PatchReviewsReviewId200JSONResponse{Success: ptr(true)}, nil +} + +// ————————————————————————————————————————————— +// МАППИНГИ +// ————————————————————————————————————————————— + +func mapUser(u db.Users) User { + return User{ + "user_id": u.UserID, + "avatar_id": pgInt4ToPtr(u.AvatarID), + "nickname": u.Nickname, + "disp_name": pgTextToPtr(u.DispName), + "user_desc": pgTextToPtr(u.UserDesc), + "creation_date": u.CreationDate.Time, + // mail и passhash НЕ возвращаем! + } +} + +func mapTitle(t db.Titles) Title { + var releaseSeason interface{} + if t.ReleaseSeason.Valid { + releaseSeason = string(t.ReleaseSeason.ReleaseSeasonT) + } + + return Title{ + "title_id": t.TitleID, + "title_names": jsonbToInterface(t.TitleNames), + "studio_id": t.StudioID, + "poster_id": pgInt4ToPtr(t.PosterID), + "signal_ids": t.SignalIds, + "title_status": string(t.TitleStatus), + "rating": pgFloat8ToPtr(t.Rating), + "rating_count": pgInt4ToPtr(t.RatingCount), + "release_year": pgInt4ToPtr(t.ReleaseYear), + "release_season": releaseSeason, + "season": pgInt4ToPtr(t.Season), + "episodes_aired": pgInt4ToPtr(t.EpisodesAired), + "episodes_all": pgInt4ToPtr(t.EpisodesAll), + "episodes_len": jsonbToInterface(t.EpisodesLen), + } +} + +func mapReview(r db.Reviews) Review { + return Review{ + "review_id": r.ReviewID, + "user_id": r.UserID, + "title_id": r.TitleID, + "image_ids": r.ImageIds, + "review_text": r.ReviewText, + "creation_date": r.CreationDate.Time, + } +} + +func mapUserTitle(ut db.Usertitles) UserTitle { + return UserTitle{ + "usertitle_id": ut.UsertitleID, + "user_id": ut.UserID, + "title_id": ut.TitleID, + "status": string(ut.Status), + "rate": pgInt4ToPtr(ut.Rate), + "review_id": pgInt4ToPtr(ut.ReviewID), + } +} diff --git a/sql/db.go b/modules/backend/db/db.go similarity index 97% rename from sql/db.go rename to modules/backend/db/db.go index 7a56507..9d485b5 100644 --- a/sql/db.go +++ b/modules/backend/db/db.go @@ -2,7 +2,7 @@ // versions: // sqlc v1.30.0 -package sqlc +package db import ( "context" diff --git a/sql/models.go b/modules/backend/db/models.go similarity index 56% rename from sql/models.go rename to modules/backend/db/models.go index 928d5ac..ba76782 100644 --- a/sql/models.go +++ b/modules/backend/db/models.go @@ -2,12 +2,11 @@ // versions: // sqlc v1.30.0 -package sqlc +package db import ( "database/sql/driver" "fmt" - "time" "github.com/jackc/pgx/v5/pgtype" ) @@ -185,76 +184,83 @@ func (ns NullUsertitleStatusT) Value() (driver.Value, error) { return string(ns.UsertitleStatusT), nil } -type Image struct { - ID int64 `json:"id"` - StorageType StorageTypeT `json:"storage_type"` - ImagePath string `json:"image_path"` +type Images struct { + ImageID int32 `db:"image_id" json:"image_id"` + StorageType StorageTypeT `db:"storage_type" json:"storage_type"` + ImagePath string `db:"image_path" json:"image_path"` } -type Provider struct { - ID int64 `json:"id"` - ProviderName string `json:"provider_name"` - Credentials []byte `json:"credentials"` +type Providers struct { + ProviderID int32 `db:"provider_id" json:"provider_id"` + ProviderName string `db:"provider_name" json:"provider_name"` } -type Signal struct { - ID int64 `json:"id"` - TitleID *int64 `json:"title_id"` - RawData []byte `json:"raw_data"` - ProviderID int64 `json:"provider_id"` - Pending bool `json:"pending"` +type Reviews struct { + ReviewID int32 `db:"review_id" json:"review_id"` + UserID int32 `db:"user_id" json:"user_id"` + TitleID int32 `db:"title_id" json:"title_id"` + ImageIds []int32 `db:"image_ids" json:"image_ids"` + ReviewText string `db:"review_text" json:"review_text"` + CreationDate pgtype.Timestamp `db:"creation_date" json:"creation_date"` } -type Studio struct { - ID int64 `json:"id"` - StudioName *string `json:"studio_name"` - IllustID *int64 `json:"illust_id"` - StudioDesc *string `json:"studio_desc"` +type Signals struct { + SignalID int32 `db:"signal_id" json:"signal_id"` + RawData []byte `db:"raw_data" json:"raw_data"` + ProviderID int32 `db:"provider_id" json:"provider_id"` + Dirty bool `db:"dirty" json:"dirty"` } -type Tag struct { - ID int64 `json:"id"` - TagNames []byte `json:"tag_names"` +type Studios struct { + StudioID int32 `db:"studio_id" json:"studio_id"` + StudioName pgtype.Text `db:"studio_name" json:"studio_name"` + IllustID pgtype.Int4 `db:"illust_id" json:"illust_id"` + StudioDesc pgtype.Text `db:"studio_desc" json:"studio_desc"` } -type Title struct { - ID int64 `json:"id"` - TitleNames []byte `json:"title_names"` - StudioID int64 `json:"studio_id"` - PosterID *int64 `json:"poster_id"` - TitleStatus TitleStatusT `json:"title_status"` - Rating *float64 `json:"rating"` - RatingCount *int32 `json:"rating_count"` - ReleaseYear *int32 `json:"release_year"` - ReleaseSeason NullReleaseSeasonT `json:"release_season"` - Season *int32 `json:"season"` - EpisodesAired *int32 `json:"episodes_aired"` - EpisodesAll *int32 `json:"episodes_all"` - EpisodesLen []byte `json:"episodes_len"` +type Tags struct { + TagID int32 `db:"tag_id" json:"tag_id"` + TagNames []byte `db:"tag_names" json:"tag_names"` } -type TitleTag struct { - TitleID int64 `json:"title_id"` - TagID int64 `json:"tag_id"` +type TitleTags struct { + TitleID int32 `db:"title_id" json:"title_id"` + TagID int32 `db:"tag_id" json:"tag_id"` } -type User struct { - ID int64 `json:"id"` - AvatarID *int64 `json:"avatar_id"` - Passhash string `json:"passhash"` - Mail *string `json:"mail"` - Nickname string `json:"nickname"` - DispName *string `json:"disp_name"` - UserDesc *string `json:"user_desc"` - CreationDate time.Time `json:"creation_date"` - LastLogin pgtype.Timestamptz `json:"last_login"` +type Titles struct { + TitleID int32 `db:"title_id" json:"title_id"` + TitleNames []byte `db:"title_names" json:"title_names"` + StudioID int32 `db:"studio_id" json:"studio_id"` + PosterID pgtype.Int4 `db:"poster_id" json:"poster_id"` + SignalIds []int32 `db:"signal_ids" json:"signal_ids"` + TitleStatus TitleStatusT `db:"title_status" json:"title_status"` + Rating pgtype.Float8 `db:"rating" json:"rating"` + RatingCount pgtype.Int4 `db:"rating_count" json:"rating_count"` + ReleaseYear pgtype.Int4 `db:"release_year" json:"release_year"` + ReleaseSeason NullReleaseSeasonT `db:"release_season" json:"release_season"` + Season pgtype.Int4 `db:"season" json:"season"` + EpisodesAired pgtype.Int4 `db:"episodes_aired" json:"episodes_aired"` + EpisodesAll pgtype.Int4 `db:"episodes_all" json:"episodes_all"` + EpisodesLen []byte `db:"episodes_len" json:"episodes_len"` } -type Usertitle struct { - UserID int64 `json:"user_id"` - TitleID int64 `json:"title_id"` - Status UsertitleStatusT `json:"status"` - Rate *int32 `json:"rate"` - ReviewText *string `json:"review_text"` - ReviewDate pgtype.Timestamptz `json:"review_date"` +type Users struct { + UserID int32 `db:"user_id" json:"user_id"` + AvatarID pgtype.Int4 `db:"avatar_id" json:"avatar_id"` + Passhash string `db:"passhash" json:"passhash"` + Mail pgtype.Text `db:"mail" json:"mail"` + Nickname string `db:"nickname" json:"nickname"` + DispName pgtype.Text `db:"disp_name" json:"disp_name"` + UserDesc pgtype.Text `db:"user_desc" json:"user_desc"` + CreationDate pgtype.Timestamp `db:"creation_date" json:"creation_date"` +} + +type Usertitles struct { + UsertitleID int32 `db:"usertitle_id" json:"usertitle_id"` + UserID int32 `db:"user_id" json:"user_id"` + TitleID int32 `db:"title_id" json:"title_id"` + Status UsertitleStatusT `db:"status" json:"status"` + Rate pgtype.Int4 `db:"rate" json:"rate"` + ReviewID pgtype.Int4 `db:"review_id" json:"review_id"` } diff --git a/modules/backend/db/query.sql.go b/modules/backend/db/query.sql.go new file mode 100644 index 0000000..1fc06ac --- /dev/null +++ b/modules/backend/db/query.sql.go @@ -0,0 +1,712 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 +// source: query.sql + +package db + +import ( + "context" + + "github.com/jackc/pgx/v5/pgtype" +) + +const createImage = `-- name: CreateImage :one +INSERT INTO images (storage_type, image_path) +VALUES ($1, $2) +RETURNING image_id, storage_type, image_path +` + +type CreateImageParams struct { + StorageType StorageTypeT `db:"storage_type" json:"storage_type"` + ImagePath string `db:"image_path" json:"image_path"` +} + +func (q *Queries) CreateImage(ctx context.Context, arg CreateImageParams) (Images, error) { + row := q.db.QueryRow(ctx, createImage, arg.StorageType, arg.ImagePath) + var i Images + err := row.Scan(&i.ImageID, &i.StorageType, &i.ImagePath) + return i, err +} + +const createReview = `-- name: CreateReview :one +INSERT INTO reviews (user_id, title_id, image_ids, review_text, creation_date) +VALUES ($1, $2, $3, $4, $5) +RETURNING review_id, user_id, title_id, image_ids, review_text, creation_date +` + +type CreateReviewParams struct { + UserID int32 `db:"user_id" json:"user_id"` + TitleID int32 `db:"title_id" json:"title_id"` + ImageIds []int32 `db:"image_ids" json:"image_ids"` + ReviewText string `db:"review_text" json:"review_text"` + CreationDate pgtype.Timestamp `db:"creation_date" json:"creation_date"` +} + +func (q *Queries) CreateReview(ctx context.Context, arg CreateReviewParams) (Reviews, error) { + row := q.db.QueryRow(ctx, createReview, + arg.UserID, + arg.TitleID, + arg.ImageIds, + arg.ReviewText, + arg.CreationDate, + ) + var i Reviews + err := row.Scan( + &i.ReviewID, + &i.UserID, + &i.TitleID, + &i.ImageIds, + &i.ReviewText, + &i.CreationDate, + ) + return i, err +} + +const createUser = `-- name: CreateUser :one +INSERT INTO users (avatar_id, passhash, mail, nickname, disp_name, user_desc, creation_date) +VALUES ($1, $2, $3, $4, $5, $6, $7) +RETURNING user_id, avatar_id, nickname, disp_name, user_desc, creation_date +` + +type CreateUserParams struct { + AvatarID pgtype.Int4 `db:"avatar_id" json:"avatar_id"` + Passhash string `db:"passhash" json:"passhash"` + Mail pgtype.Text `db:"mail" json:"mail"` + Nickname string `db:"nickname" json:"nickname"` + DispName pgtype.Text `db:"disp_name" json:"disp_name"` + UserDesc pgtype.Text `db:"user_desc" json:"user_desc"` + CreationDate pgtype.Timestamp `db:"creation_date" json:"creation_date"` +} + +type CreateUserRow struct { + UserID int32 `db:"user_id" json:"user_id"` + AvatarID pgtype.Int4 `db:"avatar_id" json:"avatar_id"` + Nickname string `db:"nickname" json:"nickname"` + DispName pgtype.Text `db:"disp_name" json:"disp_name"` + UserDesc pgtype.Text `db:"user_desc" json:"user_desc"` + CreationDate pgtype.Timestamp `db:"creation_date" json:"creation_date"` +} + +func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (CreateUserRow, error) { + row := q.db.QueryRow(ctx, createUser, + arg.AvatarID, + arg.Passhash, + arg.Mail, + arg.Nickname, + arg.DispName, + arg.UserDesc, + arg.CreationDate, + ) + var i CreateUserRow + err := row.Scan( + &i.UserID, + &i.AvatarID, + &i.Nickname, + &i.DispName, + &i.UserDesc, + &i.CreationDate, + ) + return i, err +} + +const createUserTitle = `-- name: CreateUserTitle :one +INSERT INTO usertitles (user_id, title_id, status, rate, review_id) +VALUES ($1, $2, $3, $4, $5) +RETURNING usertitle_id, user_id, title_id, status, rate, review_id +` + +type CreateUserTitleParams struct { + UserID int32 `db:"user_id" json:"user_id"` + TitleID int32 `db:"title_id" json:"title_id"` + Status UsertitleStatusT `db:"status" json:"status"` + Rate pgtype.Int4 `db:"rate" json:"rate"` + ReviewID pgtype.Int4 `db:"review_id" json:"review_id"` +} + +func (q *Queries) CreateUserTitle(ctx context.Context, arg CreateUserTitleParams) (Usertitles, error) { + row := q.db.QueryRow(ctx, createUserTitle, + arg.UserID, + arg.TitleID, + arg.Status, + arg.Rate, + arg.ReviewID, + ) + var i Usertitles + err := row.Scan( + &i.UsertitleID, + &i.UserID, + &i.TitleID, + &i.Status, + &i.Rate, + &i.ReviewID, + ) + return i, err +} + +const deleteReview = `-- name: DeleteReview :exec +DELETE FROM reviews +WHERE review_id = $1 +` + +func (q *Queries) DeleteReview(ctx context.Context, reviewID int32) error { + _, err := q.db.Exec(ctx, deleteReview, reviewID) + return err +} + +const deleteUser = `-- name: DeleteUser :exec +DELETE FROM users +WHERE user_id = $1 +` + +func (q *Queries) DeleteUser(ctx context.Context, userID int32) error { + _, err := q.db.Exec(ctx, deleteUser, userID) + return err +} + +const deleteUserTitle = `-- name: DeleteUserTitle :exec +DELETE FROM usertitles +WHERE user_id = $1 AND ($2::int IS NULL OR title_id = $2) +` + +type DeleteUserTitleParams struct { + UserID int32 `db:"user_id" json:"user_id"` + Column2 int32 `db:"column_2" json:"column_2"` +} + +func (q *Queries) DeleteUserTitle(ctx context.Context, arg DeleteUserTitleParams) error { + _, err := q.db.Exec(ctx, deleteUserTitle, arg.UserID, arg.Column2) + return err +} + +const getImageByID = `-- name: GetImageByID :one +SELECT image_id, storage_type, image_path +FROM images +WHERE image_id = $1 +` + +func (q *Queries) GetImageByID(ctx context.Context, imageID int32) (Images, error) { + row := q.db.QueryRow(ctx, getImageByID, imageID) + var i Images + err := row.Scan(&i.ImageID, &i.StorageType, &i.ImagePath) + return i, err +} + +const getReviewByID = `-- name: GetReviewByID :one +SELECT review_id, user_id, title_id, image_ids, review_text, creation_date +FROM reviews +WHERE review_id = $1 +` + +func (q *Queries) GetReviewByID(ctx context.Context, reviewID int32) (Reviews, error) { + row := q.db.QueryRow(ctx, getReviewByID, reviewID) + var i Reviews + err := row.Scan( + &i.ReviewID, + &i.UserID, + &i.TitleID, + &i.ImageIds, + &i.ReviewText, + &i.CreationDate, + ) + return i, err +} + +const getTitleByID = `-- name: GetTitleByID :one +SELECT title_id, title_names, studio_id, poster_id, signal_ids, + title_status, rating, rating_count, release_year, release_season, + season, episodes_aired, episodes_all, episodes_len +FROM titles +WHERE title_id = $1 +` + +func (q *Queries) GetTitleByID(ctx context.Context, titleID int32) (Titles, error) { + row := q.db.QueryRow(ctx, getTitleByID, titleID) + var i Titles + err := row.Scan( + &i.TitleID, + &i.TitleNames, + &i.StudioID, + &i.PosterID, + &i.SignalIds, + &i.TitleStatus, + &i.Rating, + &i.RatingCount, + &i.ReleaseYear, + &i.ReleaseSeason, + &i.Season, + &i.EpisodesAired, + &i.EpisodesAll, + &i.EpisodesLen, + ) + return i, err +} + +const getUserByID = `-- name: GetUserByID :one +SELECT user_id, avatar_id, passhash, mail, nickname, disp_name, user_desc, creation_date +FROM users +WHERE user_id = $1 +` + +func (q *Queries) GetUserByID(ctx context.Context, userID int32) (Users, error) { + row := q.db.QueryRow(ctx, getUserByID, userID) + var i Users + err := row.Scan( + &i.UserID, + &i.AvatarID, + &i.Passhash, + &i.Mail, + &i.Nickname, + &i.DispName, + &i.UserDesc, + &i.CreationDate, + ) + return i, err +} + +const getUserTitle = `-- name: GetUserTitle :one +SELECT usertitle_id, user_id, title_id, status, rate, review_id +FROM usertitles +WHERE user_id = $1 AND title_id = $2 +` + +type GetUserTitleParams struct { + UserID int32 `db:"user_id" json:"user_id"` + TitleID int32 `db:"title_id" json:"title_id"` +} + +func (q *Queries) GetUserTitle(ctx context.Context, arg GetUserTitleParams) (Usertitles, error) { + row := q.db.QueryRow(ctx, getUserTitle, arg.UserID, arg.TitleID) + var i Usertitles + err := row.Scan( + &i.UsertitleID, + &i.UserID, + &i.TitleID, + &i.Status, + &i.Rate, + &i.ReviewID, + ) + return i, err +} + +const listReviewsByTitle = `-- name: ListReviewsByTitle :many +SELECT review_id, user_id, title_id, image_ids, review_text, creation_date +FROM reviews +WHERE title_id = $1 +ORDER BY creation_date DESC +LIMIT $2 OFFSET $3 +` + +type ListReviewsByTitleParams struct { + TitleID int32 `db:"title_id" json:"title_id"` + Limit int32 `db:"limit" json:"limit"` + Offset int32 `db:"offset" json:"offset"` +} + +func (q *Queries) ListReviewsByTitle(ctx context.Context, arg ListReviewsByTitleParams) ([]Reviews, error) { + rows, err := q.db.Query(ctx, listReviewsByTitle, arg.TitleID, arg.Limit, arg.Offset) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Reviews + for rows.Next() { + var i Reviews + if err := rows.Scan( + &i.ReviewID, + &i.UserID, + &i.TitleID, + &i.ImageIds, + &i.ReviewText, + &i.CreationDate, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listReviewsByUser = `-- name: ListReviewsByUser :many +SELECT review_id, user_id, title_id, image_ids, review_text, creation_date +FROM reviews +WHERE user_id = $1 +ORDER BY creation_date DESC +LIMIT $2 OFFSET $3 +` + +type ListReviewsByUserParams struct { + UserID int32 `db:"user_id" json:"user_id"` + Limit int32 `db:"limit" json:"limit"` + Offset int32 `db:"offset" json:"offset"` +} + +func (q *Queries) ListReviewsByUser(ctx context.Context, arg ListReviewsByUserParams) ([]Reviews, error) { + rows, err := q.db.Query(ctx, listReviewsByUser, arg.UserID, arg.Limit, arg.Offset) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Reviews + for rows.Next() { + var i Reviews + if err := rows.Scan( + &i.ReviewID, + &i.UserID, + &i.TitleID, + &i.ImageIds, + &i.ReviewText, + &i.CreationDate, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listTags = `-- name: ListTags :many +SELECT tag_id, tag_names +FROM tags +ORDER BY tag_id +LIMIT $1 OFFSET $2 +` + +type ListTagsParams struct { + Limit int32 `db:"limit" json:"limit"` + Offset int32 `db:"offset" json:"offset"` +} + +func (q *Queries) ListTags(ctx context.Context, arg ListTagsParams) ([]Tags, error) { + rows, err := q.db.Query(ctx, listTags, arg.Limit, arg.Offset) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Tags + for rows.Next() { + var i Tags + if err := rows.Scan(&i.TagID, &i.TagNames); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listTitles = `-- name: ListTitles :many +SELECT title_id, title_names, studio_id, poster_id, signal_ids, + title_status, rating, rating_count, release_year, release_season, + season, episodes_aired, episodes_all, episodes_len +FROM titles +ORDER BY title_id +LIMIT $1 OFFSET $2 +` + +type ListTitlesParams struct { + Limit int32 `db:"limit" json:"limit"` + Offset int32 `db:"offset" json:"offset"` +} + +func (q *Queries) ListTitles(ctx context.Context, arg ListTitlesParams) ([]Titles, error) { + rows, err := q.db.Query(ctx, listTitles, arg.Limit, arg.Offset) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Titles + for rows.Next() { + var i Titles + if err := rows.Scan( + &i.TitleID, + &i.TitleNames, + &i.StudioID, + &i.PosterID, + &i.SignalIds, + &i.TitleStatus, + &i.Rating, + &i.RatingCount, + &i.ReleaseYear, + &i.ReleaseSeason, + &i.Season, + &i.EpisodesAired, + &i.EpisodesAll, + &i.EpisodesLen, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listUserTitles = `-- name: ListUserTitles :many +SELECT usertitle_id, user_id, title_id, status, rate, review_id +FROM usertitles +WHERE user_id = $1 +ORDER BY usertitle_id +LIMIT $2 OFFSET $3 +` + +type ListUserTitlesParams struct { + UserID int32 `db:"user_id" json:"user_id"` + Limit int32 `db:"limit" json:"limit"` + Offset int32 `db:"offset" json:"offset"` +} + +func (q *Queries) ListUserTitles(ctx context.Context, arg ListUserTitlesParams) ([]Usertitles, error) { + rows, err := q.db.Query(ctx, listUserTitles, arg.UserID, arg.Limit, arg.Offset) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Usertitles + for rows.Next() { + var i Usertitles + if err := rows.Scan( + &i.UsertitleID, + &i.UserID, + &i.TitleID, + &i.Status, + &i.Rate, + &i.ReviewID, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listUsers = `-- name: ListUsers :many +SELECT user_id, avatar_id, passhash, mail, nickname, disp_name, user_desc, creation_date +FROM users +ORDER BY user_id +LIMIT $1 OFFSET $2 +` + +type ListUsersParams struct { + Limit int32 `db:"limit" json:"limit"` + Offset int32 `db:"offset" json:"offset"` +} + +func (q *Queries) ListUsers(ctx context.Context, arg ListUsersParams) ([]Users, error) { + rows, err := q.db.Query(ctx, listUsers, arg.Limit, arg.Offset) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Users + for rows.Next() { + var i Users + if err := rows.Scan( + &i.UserID, + &i.AvatarID, + &i.Passhash, + &i.Mail, + &i.Nickname, + &i.DispName, + &i.UserDesc, + &i.CreationDate, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const updateReview = `-- name: UpdateReview :one +UPDATE reviews +SET + image_ids = COALESCE($1, image_ids), + review_text = COALESCE($2, review_text) +WHERE review_id = $3 +RETURNING review_id, user_id, title_id, image_ids, review_text, creation_date +` + +type UpdateReviewParams struct { + ImageIds []int32 `db:"image_ids" json:"image_ids"` + ReviewText pgtype.Text `db:"review_text" json:"review_text"` + ReviewID int32 `db:"review_id" json:"review_id"` +} + +func (q *Queries) UpdateReview(ctx context.Context, arg UpdateReviewParams) (Reviews, error) { + row := q.db.QueryRow(ctx, updateReview, arg.ImageIds, arg.ReviewText, arg.ReviewID) + var i Reviews + err := row.Scan( + &i.ReviewID, + &i.UserID, + &i.TitleID, + &i.ImageIds, + &i.ReviewText, + &i.CreationDate, + ) + return i, err +} + +const updateTitle = `-- name: UpdateTitle :one +UPDATE titles +SET + title_names = COALESCE($1, title_names), + studio_id = COALESCE($2, studio_id), + poster_id = COALESCE($3, poster_id), + signal_ids = COALESCE($4, signal_ids), + title_status = COALESCE($5, title_status), + release_year = COALESCE($6, release_year), + release_season = COALESCE($7, release_season), + episodes_aired = COALESCE($8, episodes_aired), + episodes_all = COALESCE($9, episodes_all), + episodes_len = COALESCE($10, episodes_len) +WHERE title_id = $11 +RETURNING title_id, title_names, studio_id, poster_id, signal_ids, title_status, rating, rating_count, release_year, release_season, season, episodes_aired, episodes_all, episodes_len +` + +type UpdateTitleParams struct { + TitleNames []byte `db:"title_names" json:"title_names"` + StudioID pgtype.Int4 `db:"studio_id" json:"studio_id"` + PosterID pgtype.Int4 `db:"poster_id" json:"poster_id"` + SignalIds []int32 `db:"signal_ids" json:"signal_ids"` + TitleStatus NullTitleStatusT `db:"title_status" json:"title_status"` + ReleaseYear pgtype.Int4 `db:"release_year" json:"release_year"` + ReleaseSeason NullReleaseSeasonT `db:"release_season" json:"release_season"` + EpisodesAired pgtype.Int4 `db:"episodes_aired" json:"episodes_aired"` + EpisodesAll pgtype.Int4 `db:"episodes_all" json:"episodes_all"` + EpisodesLen []byte `db:"episodes_len" json:"episodes_len"` + TitleID int32 `db:"title_id" json:"title_id"` +} + +func (q *Queries) UpdateTitle(ctx context.Context, arg UpdateTitleParams) (Titles, error) { + row := q.db.QueryRow(ctx, updateTitle, + arg.TitleNames, + arg.StudioID, + arg.PosterID, + arg.SignalIds, + arg.TitleStatus, + arg.ReleaseYear, + arg.ReleaseSeason, + arg.EpisodesAired, + arg.EpisodesAll, + arg.EpisodesLen, + arg.TitleID, + ) + var i Titles + err := row.Scan( + &i.TitleID, + &i.TitleNames, + &i.StudioID, + &i.PosterID, + &i.SignalIds, + &i.TitleStatus, + &i.Rating, + &i.RatingCount, + &i.ReleaseYear, + &i.ReleaseSeason, + &i.Season, + &i.EpisodesAired, + &i.EpisodesAll, + &i.EpisodesLen, + ) + return i, err +} + +const updateUser = `-- name: UpdateUser :one +UPDATE users +SET + avatar_id = COALESCE($1, avatar_id), + disp_name = COALESCE($2, disp_name), + user_desc = COALESCE($3, user_desc) +WHERE user_id = $4 +RETURNING user_id, avatar_id, nickname, disp_name, user_desc, creation_date +` + +type UpdateUserParams struct { + AvatarID pgtype.Int4 `db:"avatar_id" json:"avatar_id"` + DispName pgtype.Text `db:"disp_name" json:"disp_name"` + UserDesc pgtype.Text `db:"user_desc" json:"user_desc"` + UserID int32 `db:"user_id" json:"user_id"` +} + +type UpdateUserRow struct { + UserID int32 `db:"user_id" json:"user_id"` + AvatarID pgtype.Int4 `db:"avatar_id" json:"avatar_id"` + Nickname string `db:"nickname" json:"nickname"` + DispName pgtype.Text `db:"disp_name" json:"disp_name"` + UserDesc pgtype.Text `db:"user_desc" json:"user_desc"` + CreationDate pgtype.Timestamp `db:"creation_date" json:"creation_date"` +} + +func (q *Queries) UpdateUser(ctx context.Context, arg UpdateUserParams) (UpdateUserRow, error) { + row := q.db.QueryRow(ctx, updateUser, + arg.AvatarID, + arg.DispName, + arg.UserDesc, + arg.UserID, + ) + var i UpdateUserRow + err := row.Scan( + &i.UserID, + &i.AvatarID, + &i.Nickname, + &i.DispName, + &i.UserDesc, + &i.CreationDate, + ) + return i, err +} + +const updateUserTitle = `-- name: UpdateUserTitle :one +UPDATE usertitles +SET + status = COALESCE($3, status), + rate = COALESCE($4, rate), + review_id = COALESCE($5, review_id) +WHERE user_id = $1 AND title_id = $2 +RETURNING usertitle_id, user_id, title_id, status, rate, review_id +` + +type UpdateUserTitleParams struct { + UserID int32 `db:"user_id" json:"user_id"` + TitleID int32 `db:"title_id" json:"title_id"` + Status NullUsertitleStatusT `db:"status" json:"status"` + Rate pgtype.Int4 `db:"rate" json:"rate"` + ReviewID pgtype.Int4 `db:"review_id" json:"review_id"` +} + +func (q *Queries) UpdateUserTitle(ctx context.Context, arg UpdateUserTitleParams) (Usertitles, error) { + row := q.db.QueryRow(ctx, updateUserTitle, + arg.UserID, + arg.TitleID, + arg.Status, + arg.Rate, + arg.ReviewID, + ) + var i Usertitles + err := row.Scan( + &i.UsertitleID, + &i.UserID, + &i.TitleID, + &i.Status, + &i.Rate, + &i.ReviewID, + ) + return i, err +} diff --git a/go.mod b/modules/backend/go.mod similarity index 82% rename from go.mod rename to modules/backend/go.mod index b7a66f2..d8e0cb3 100644 --- a/go.mod +++ b/modules/backend/go.mod @@ -1,23 +1,16 @@ -module nyanimedb +module nyanimedb-server go 1.25.0 -require ( - github.com/gin-contrib/cors v1.7.6 - github.com/gin-gonic/gin v1.11.0 - github.com/jackc/pgx/v5 v5.7.6 - github.com/oapi-codegen/runtime v1.1.2 - github.com/pelletier/go-toml/v2 v2.2.4 - golang.org/x/crypto v0.40.0 -) - require ( github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/bytedance/sonic v1.14.0 // indirect github.com/bytedance/sonic/loader v0.3.0 // indirect github.com/cloudwego/base64x v0.1.6 // indirect github.com/gabriel-vasile/mimetype v1.4.9 // indirect + github.com/gin-contrib/cors v1.7.6 // indirect github.com/gin-contrib/sse v1.1.0 // indirect + github.com/gin-gonic/gin v1.11.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.27.0 // indirect @@ -26,18 +19,23 @@ require ( github.com/google/uuid v1.5.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.7.6 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/oapi-codegen/runtime v1.1.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/quic-go/qpack v0.5.1 // indirect github.com/quic-go/quic-go v0.54.0 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.3.0 // indirect go.uber.org/mock v0.5.0 // indirect golang.org/x/arch v0.20.0 // indirect + golang.org/x/crypto v0.40.0 // indirect golang.org/x/mod v0.25.0 // indirect golang.org/x/net v0.42.0 // indirect golang.org/x/sync v0.16.0 // indirect diff --git a/go.sum b/modules/backend/go.sum similarity index 90% rename from go.sum rename to modules/backend/go.sum index 1af1a7c..a08eb1d 100644 --- a/go.sum +++ b/modules/backend/go.sum @@ -9,8 +9,9 @@ github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFos github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= +github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok= github.com/gin-contrib/cors v1.7.6 h1:3gQ8GMzs1Ylpf70y8bMw4fVpycXIeX1ZemuSQIsnQQY= @@ -19,20 +20,18 @@ github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk= github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls= -github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= -github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4= github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= -github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= -github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -42,8 +41,6 @@ github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7Ulw github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgx/v5 v5.7.6 h1:rWQc5FwZSPX58r1OQmkuaNicxdmExaEz5A2DO2hUuTk= github.com/jackc/pgx/v5 v5.7.6/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M= -github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= -github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= @@ -53,6 +50,7 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -62,12 +60,13 @@ github.com/oapi-codegen/runtime v1.1.2 h1:P2+CubHq8fO4Q6fV1tqDBZHCwpVpvPg7oKiYzQ github.com/oapi-codegen/runtime v1.1.2/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg= github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -77,8 +76,6 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA= @@ -95,6 +92,8 @@ golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= @@ -106,5 +105,4 @@ google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7I google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/modules/backend/handlers/common.go b/modules/backend/handlers/common.go deleted file mode 100644 index b85d022..0000000 --- a/modules/backend/handlers/common.go +++ /dev/null @@ -1,19 +0,0 @@ -package handlers - -import ( - sqlc "nyanimedb/sql" - "strconv" -) - -type Server struct { - db *sqlc.Queries -} - -func NewServer(db *sqlc.Queries) Server { - return Server{db: db} -} - -func parseInt64(s string) (int32, error) { - i, err := strconv.ParseInt(s, 10, 64) - return int32(i), err -} diff --git a/modules/backend/handlers/users.go b/modules/backend/handlers/users.go deleted file mode 100644 index 3da61de..0000000 --- a/modules/backend/handlers/users.go +++ /dev/null @@ -1,50 +0,0 @@ -package handlers - -import ( - "context" - oapi "nyanimedb/api" - sqlc "nyanimedb/sql" - - "github.com/jackc/pgx/v5" - "github.com/oapi-codegen/runtime/types" -) - -// type Server struct { -// db *sqlc.Queries -// } - -// func NewServer(db *sqlc.Queries) Server { -// return Server{db: db} -// } - -// func parseInt64(s string) (int32, error) { -// i, err := strconv.ParseInt(s, 10, 64) -// return int32(i), err -// } - -func mapUser(u sqlc.GetUserByIDRow) oapi.User { - return oapi.User{ - AvatarId: u.AvatarID, - CreationDate: u.CreationDate, - DispName: u.DispName, - Id: &u.ID, - Mail: (*types.Email)(u.Mail), - Nickname: u.Nickname, - UserDesc: u.UserDesc, - } -} - -func (s Server) GetUsersUserId(ctx context.Context, req oapi.GetUsersUserIdRequestObject) (oapi.GetUsersUserIdResponseObject, error) { - userID, err := parseInt64(req.UserId) - if err != nil { - return oapi.GetUsersUserId404Response{}, nil - } - user, err := s.db.GetUserByID(context.TODO(), int64(userID)) - if err != nil { - if err == pgx.ErrNoRows { - return oapi.GetUsersUserId404Response{}, nil - } - return nil, err - } - return oapi.GetUsersUserId200JSONResponse(mapUser(user)), nil -} diff --git a/modules/backend/main.go b/modules/backend/main.go index 42a66d3..5a61226 100644 --- a/modules/backend/main.go +++ b/modules/backend/main.go @@ -3,14 +3,12 @@ package main import ( "context" "fmt" - sqlc "nyanimedb/sql" + "nyanimedb-server/api" + "nyanimedb-server/db" "os" "reflect" "time" - oapi "nyanimedb/api" - handlers "nyanimedb/modules/backend/handlers" - "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" "github.com/jackc/pgx/v5" @@ -41,9 +39,9 @@ func main() { r := gin.Default() - queries := sqlc.New(conn) + queries := db.New(conn) - server := handlers.NewServer(queries) + server := api.NewServer(queries) // r.LoadHTMLGlob("templates/*") r.Use(cors.New(cors.Config{ @@ -55,10 +53,10 @@ func main() { MaxAge: 12 * time.Hour, })) - oapi.RegisterHandlers(r, oapi.NewStrictHandler( + api.RegisterHandlers(r, api.NewStrictHandler( server, // сюда можно добавить middlewares, если нужно - []oapi.StrictMiddlewareFunc{}, + []api.StrictMiddlewareFunc{}, )) // r.GET("/", func(c *gin.Context) { // c.HTML(http.StatusOK, "index.html", gin.H{ diff --git a/modules/backend/queries.sql b/modules/backend/queries.sql deleted file mode 100644 index b1dd8af..0000000 --- a/modules/backend/queries.sql +++ /dev/null @@ -1,142 +0,0 @@ --- name: GetImageByID :one -SELECT id, storage_type, image_path -FROM images -WHERE id = $1; - --- name: CreateImage :one -INSERT INTO images (storage_type, image_path) -VALUES ($1, $2) -RETURNING id, storage_type, image_path; - --- name: GetUserByID :one -SELECT id, avatar_id, mail, nickname, disp_name, user_desc, creation_date -FROM users -WHERE id = $1; - --- -- name: ListUsers :many --- SELECT user_id, avatar_id, passhash, mail, nickname, disp_name, user_desc, creation_date --- FROM users --- ORDER BY user_id --- LIMIT $1 OFFSET $2; - --- -- name: CreateUser :one --- INSERT INTO users (avatar_id, passhash, mail, nickname, disp_name, user_desc, creation_date) --- VALUES ($1, $2, $3, $4, $5, $6, $7) --- RETURNING user_id, avatar_id, nickname, disp_name, user_desc, creation_date; - --- -- name: UpdateUser :one --- UPDATE users --- SET --- avatar_id = COALESCE(sqlc.narg('avatar_id'), avatar_id), --- disp_name = COALESCE(sqlc.narg('disp_name'), disp_name), --- user_desc = COALESCE(sqlc.narg('user_desc'), user_desc), --- passhash = COALESCE(sqlc.narg('passhash'), passhash) --- WHERE user_id = sqlc.arg('user_id') --- RETURNING user_id, avatar_id, nickname, disp_name, user_desc, creation_date; - --- -- name: DeleteUser :exec --- DELETE FROM users --- WHERE user_id = $1; - --- -- name: GetTitleByID :one --- SELECT title_id, title_names, studio_id, poster_id, signal_ids, --- title_status, rating, rating_count, release_year, release_season, --- season, episodes_aired, episodes_all, episodes_len --- FROM titles --- WHERE title_id = $1; - --- -- name: ListTitles :many --- SELECT title_id, title_names, studio_id, poster_id, signal_ids, --- title_status, rating, rating_count, release_year, release_season, --- season, episodes_aired, episodes_all, episodes_len --- FROM titles --- ORDER BY title_id --- LIMIT $1 OFFSET $2; - --- -- name: UpdateTitle :one --- UPDATE titles --- SET --- title_names = COALESCE(sqlc.narg('title_names'), title_names), --- studio_id = COALESCE(sqlc.narg('studio_id'), studio_id), --- poster_id = COALESCE(sqlc.narg('poster_id'), poster_id), --- signal_ids = COALESCE(sqlc.narg('signal_ids'), signal_ids), --- title_status = COALESCE(sqlc.narg('title_status'), title_status), --- release_year = COALESCE(sqlc.narg('release_year'), release_year), --- release_season = COALESCE(sqlc.narg('release_season'), release_season), --- episodes_aired = COALESCE(sqlc.narg('episodes_aired'), episodes_aired), --- episodes_all = COALESCE(sqlc.narg('episodes_all'), episodes_all), --- episodes_len = COALESCE(sqlc.narg('episodes_len'), episodes_len) --- WHERE title_id = sqlc.arg('title_id') --- RETURNING *; - --- -- name: GetReviewByID :one --- SELECT review_id, user_id, title_id, image_ids, review_text, creation_date --- FROM reviews --- WHERE review_id = $1; - --- -- name: CreateReview :one --- INSERT INTO reviews (user_id, title_id, image_ids, review_text, creation_date) --- VALUES ($1, $2, $3, $4, $5) --- RETURNING review_id, user_id, title_id, image_ids, review_text, creation_date; - --- -- name: UpdateReview :one --- UPDATE reviews --- SET --- image_ids = COALESCE(sqlc.narg('image_ids'), image_ids), --- review_text = COALESCE(sqlc.narg('review_text'), review_text) --- WHERE review_id = sqlc.arg('review_id') --- RETURNING *; - --- -- name: DeleteReview :exec --- DELETE FROM reviews --- WHERE review_id = $1; - --- -- name: ListReviewsByTitle :many --- SELECT review_id, user_id, title_id, image_ids, review_text, creation_date --- FROM reviews --- WHERE title_id = $1 --- ORDER BY creation_date DESC --- LIMIT $2 OFFSET $3; - --- -- name: ListReviewsByUser :many --- SELECT review_id, user_id, title_id, image_ids, review_text, creation_date --- FROM reviews --- WHERE user_id = $1 --- ORDER BY creation_date DESC --- LIMIT $2 OFFSET $3; - --- -- name: GetUserTitle :one --- SELECT usertitle_id, user_id, title_id, status, rate, review_id --- FROM usertitles --- WHERE user_id = $1 AND title_id = $2; - --- -- name: ListUserTitles :many --- SELECT usertitle_id, user_id, title_id, status, rate, review_id --- FROM usertitles --- WHERE user_id = $1 --- ORDER BY usertitle_id --- LIMIT $2 OFFSET $3; - --- -- name: CreateUserTitle :one --- INSERT INTO usertitles (user_id, title_id, status, rate, review_id) --- VALUES ($1, $2, $3, $4, $5) --- RETURNING usertitle_id, user_id, title_id, status, rate, review_id; - --- -- name: UpdateUserTitle :one --- UPDATE usertitles --- SET --- status = COALESCE(sqlc.narg('status'), status), --- rate = COALESCE(sqlc.narg('rate'), rate), --- review_id = COALESCE(sqlc.narg('review_id'), review_id) --- WHERE user_id = $1 AND title_id = $2 --- RETURNING *; - --- -- name: DeleteUserTitle :exec --- DELETE FROM usertitles --- WHERE user_id = $1 AND ($2::int IS NULL OR title_id = $2); - --- -- name: ListTags :many --- SELECT tag_id, tag_names --- FROM tags --- ORDER BY tag_id --- LIMIT $1 OFFSET $2; \ No newline at end of file diff --git a/modules/backend/templates/index.html b/modules/backend/templates/index.html new file mode 100644 index 0000000..9f7bd75 --- /dev/null +++ b/modules/backend/templates/index.html @@ -0,0 +1,10 @@ + + + + + {{ .title }} + + +

{{ .message }}

+ + \ No newline at end of file diff --git a/modules/frontend/nginx-default.conf b/modules/frontend/nginx-default.conf deleted file mode 100644 index a538968..0000000 --- a/modules/frontend/nginx-default.conf +++ /dev/null @@ -1,29 +0,0 @@ -server { - listen 80; - listen [::]:80; - server_name localhost; - - root /usr/share/nginx/html; - index index.html; - - location / { - try_files $uri $uri/ /index.html; - } - - location /api/v1/ { - rewrite ^/api/v1/(.*)$ /$1 break; - proxy_pass http://nyanimedb-backend:8080/; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_set_header Host $host; - proxy_cache_bypass $http_upgrade; - } - #error_page 404 /404.html; - - error_page 500 502 503 504 /50x.html; - location = /50x.html { - root /usr/share/nginx/html; - } - -} \ No newline at end of file diff --git a/modules/frontend/package-lock.json b/modules/frontend/package-lock.json index 6a06afb..a282de5 100644 --- a/modules/frontend/package-lock.json +++ b/modules/frontend/package-lock.json @@ -10,8 +10,7 @@ "dependencies": { "axios": "^1.12.2", "react": "^19.1.1", - "react-dom": "^19.1.1", - "react-router-dom": "^7.9.4" + "react-dom": "^19.1.1" }, "devDependencies": { "@eslint/js": "^9.36.0", @@ -23,30 +22,11 @@ "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.22", "globals": "^16.4.0", - "openapi-typescript-codegen": "^0.29.0", "typescript": "~5.9.3", "typescript-eslint": "^8.45.0", "vite": "^7.1.7" } }, - "node_modules/@apidevtools/json-schema-ref-parser": { - "version": "11.9.3", - "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.9.3.tgz", - "integrity": "sha512-60vepv88RwcJtSHrD6MjIL6Ta3SOYbgfnkHb+ppAVK+o9mXprRtulx7VlRl3lN3bbvysAfCS7WMVfhUYemB0IQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jsdevtools/ono": "^7.1.3", - "@types/json-schema": "^7.0.15", - "js-yaml": "^4.1.0" - }, - "engines": { - "node": ">= 16" - }, - "funding": { - "url": "https://github.com/sponsors/philsturgeon" - } - }, "node_modules/@babel/code-frame": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", @@ -1031,13 +1011,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@jsdevtools/ono": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", - "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", - "dev": true, - "license": "MIT" - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1955,19 +1928,6 @@ "node": ">=6" } }, - "node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/caniuse-lite": { "version": "1.0.30001749", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001749.tgz", @@ -2038,16 +1998,6 @@ "node": ">= 0.8" } }, - "node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2062,15 +2012,6 @@ "dev": true, "license": "MIT" }, - "node_modules/cookie": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", - "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2598,21 +2539,6 @@ "node": ">= 6" } }, - "node_modules/fs-extra": { - "version": "11.3.2", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.2.tgz", - "integrity": "sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -2722,13 +2648,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -2736,28 +2655,6 @@ "dev": true, "license": "MIT" }, - "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -2951,19 +2848,6 @@ "node": ">=6" } }, - "node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -3088,16 +2972,6 @@ "node": "*" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -3131,13 +3005,6 @@ "dev": true, "license": "MIT" }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "license": "MIT" - }, "node_modules/node-releases": { "version": "2.0.23", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.23.tgz", @@ -3145,23 +3012,6 @@ "dev": true, "license": "MIT" }, - "node_modules/openapi-typescript-codegen": { - "version": "0.29.0", - "resolved": "https://registry.npmjs.org/openapi-typescript-codegen/-/openapi-typescript-codegen-0.29.0.tgz", - "integrity": "sha512-/wC42PkD0LGjDTEULa/XiWQbv4E9NwLjwLjsaJ/62yOsoYhwvmBR31kPttn1DzQ2OlGe5stACcF/EIkZk43M6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@apidevtools/json-schema-ref-parser": "^11.5.4", - "camelcase": "^6.3.0", - "commander": "^12.0.0", - "fs-extra": "^11.2.0", - "handlebars": "^4.7.8" - }, - "bin": { - "openapi": "bin/index.js" - } - }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -3356,7 +3206,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz", "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -3374,44 +3223,6 @@ "node": ">=0.10.0" } }, - "node_modules/react-router": { - "version": "7.9.4", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.4.tgz", - "integrity": "sha512-SD3G8HKviFHg9xj7dNODUKDFgpG4xqD5nhyd0mYoB5iISepuZAvzSr8ywxgxKJ52yRzf/HWtVHc9AWwoTbljvA==", - "license": "MIT", - "dependencies": { - "cookie": "^1.0.1", - "set-cookie-parser": "^2.6.0" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - } - } - }, - "node_modules/react-router-dom": { - "version": "7.9.4", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.9.4.tgz", - "integrity": "sha512-f30P6bIkmYvnHHa5Gcu65deIXoA2+r3Eb6PJIAddvsT9aGlchMatJ51GgpU470aSqRRbFX22T70yQNUGuW3DfA==", - "license": "MIT", - "dependencies": { - "react-router": "7.9.4" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" - } - }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -3515,12 +3326,6 @@ "semver": "bin/semver.js" } }, - "node_modules/set-cookie-parser": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", - "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", - "license": "MIT" - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -3544,16 +3349,6 @@ "node": ">=8" } }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -3717,20 +3512,6 @@ "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/uglify-js": { - "version": "3.19.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", - "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/undici-types": { "version": "7.14.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.14.0.tgz", @@ -3738,16 +3519,6 @@ "dev": true, "license": "MIT" }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/update-browserslist-db": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", @@ -3923,13 +3694,6 @@ "node": ">=0.10.0" } }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true, - "license": "MIT" - }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/modules/frontend/package.json b/modules/frontend/package.json index b4977aa..9fbc7aa 100644 --- a/modules/frontend/package.json +++ b/modules/frontend/package.json @@ -12,8 +12,7 @@ "dependencies": { "axios": "^1.12.2", "react": "^19.1.1", - "react-dom": "^19.1.1", - "react-router-dom": "^7.9.4" + "react-dom": "^19.1.1" }, "devDependencies": { "@eslint/js": "^9.36.0", @@ -25,7 +24,6 @@ "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.22", "globals": "^16.4.0", - "openapi-typescript-codegen": "^0.29.0", "typescript": "~5.9.3", "typescript-eslint": "^8.45.0", "vite": "^7.1.7" diff --git a/modules/frontend/src/App.tsx b/modules/frontend/src/App.tsx index a88ad57..d9e6fbd 100644 --- a/modules/frontend/src/App.tsx +++ b/modules/frontend/src/App.tsx @@ -1,15 +1,38 @@ -import React from "react"; -import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; -import UserPage from "./components/UserPage/UserPage"; +import React, { useEffect, useState } from "react"; +import { fetchItems } from "./services/api"; +import type { Item } from "./services/api"; +import ItemTemplate from "./components/ItemTemplate"; const App: React.FC = () => { + const [items, setItems] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const getData = async () => { + try { + const data = await fetchItems(); + setItems(data); + } catch (err) { + setError("Failed to fetch items."); + } finally { + setLoading(false); + } + }; + getData(); + }, []); + + if (loading) return
Loading...
; + if (error) return
{error}
; + return ( - - - } /> - - +
+

Items List

+ {items.map((item) => ( + + ))} +
); }; -export default App; \ No newline at end of file +export default App; diff --git a/modules/frontend/src/api/core/ApiError.ts b/modules/frontend/src/api/core/ApiError.ts deleted file mode 100644 index ec7b16a..0000000 --- a/modules/frontend/src/api/core/ApiError.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* generated using openapi-typescript-codegen -- do not edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { ApiRequestOptions } from './ApiRequestOptions'; -import type { ApiResult } from './ApiResult'; - -export class ApiError extends Error { - public readonly url: string; - public readonly status: number; - public readonly statusText: string; - public readonly body: any; - public readonly request: ApiRequestOptions; - - constructor(request: ApiRequestOptions, response: ApiResult, message: string) { - super(message); - - this.name = 'ApiError'; - this.url = response.url; - this.status = response.status; - this.statusText = response.statusText; - this.body = response.body; - this.request = request; - } -} diff --git a/modules/frontend/src/api/core/ApiRequestOptions.ts b/modules/frontend/src/api/core/ApiRequestOptions.ts deleted file mode 100644 index 93143c3..0000000 --- a/modules/frontend/src/api/core/ApiRequestOptions.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* generated using openapi-typescript-codegen -- do not edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export type ApiRequestOptions = { - readonly method: 'GET' | 'PUT' | 'POST' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'PATCH'; - readonly url: string; - readonly path?: Record; - readonly cookies?: Record; - readonly headers?: Record; - readonly query?: Record; - readonly formData?: Record; - readonly body?: any; - readonly mediaType?: string; - readonly responseHeader?: string; - readonly errors?: Record; -}; diff --git a/modules/frontend/src/api/core/ApiResult.ts b/modules/frontend/src/api/core/ApiResult.ts deleted file mode 100644 index ee1126e..0000000 --- a/modules/frontend/src/api/core/ApiResult.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* generated using openapi-typescript-codegen -- do not edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export type ApiResult = { - readonly url: string; - readonly ok: boolean; - readonly status: number; - readonly statusText: string; - readonly body: any; -}; diff --git a/modules/frontend/src/api/core/CancelablePromise.ts b/modules/frontend/src/api/core/CancelablePromise.ts deleted file mode 100644 index d70de92..0000000 --- a/modules/frontend/src/api/core/CancelablePromise.ts +++ /dev/null @@ -1,131 +0,0 @@ -/* generated using openapi-typescript-codegen -- do not edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export class CancelError extends Error { - - constructor(message: string) { - super(message); - this.name = 'CancelError'; - } - - public get isCancelled(): boolean { - return true; - } -} - -export interface OnCancel { - readonly isResolved: boolean; - readonly isRejected: boolean; - readonly isCancelled: boolean; - - (cancelHandler: () => void): void; -} - -export class CancelablePromise implements Promise { - #isResolved: boolean; - #isRejected: boolean; - #isCancelled: boolean; - readonly #cancelHandlers: (() => void)[]; - readonly #promise: Promise; - #resolve?: (value: T | PromiseLike) => void; - #reject?: (reason?: any) => void; - - constructor( - executor: ( - resolve: (value: T | PromiseLike) => void, - reject: (reason?: any) => void, - onCancel: OnCancel - ) => void - ) { - this.#isResolved = false; - this.#isRejected = false; - this.#isCancelled = false; - this.#cancelHandlers = []; - this.#promise = new Promise((resolve, reject) => { - this.#resolve = resolve; - this.#reject = reject; - - const onResolve = (value: T | PromiseLike): void => { - if (this.#isResolved || this.#isRejected || this.#isCancelled) { - return; - } - this.#isResolved = true; - if (this.#resolve) this.#resolve(value); - }; - - const onReject = (reason?: any): void => { - if (this.#isResolved || this.#isRejected || this.#isCancelled) { - return; - } - this.#isRejected = true; - if (this.#reject) this.#reject(reason); - }; - - const onCancel = (cancelHandler: () => void): void => { - if (this.#isResolved || this.#isRejected || this.#isCancelled) { - return; - } - this.#cancelHandlers.push(cancelHandler); - }; - - Object.defineProperty(onCancel, 'isResolved', { - get: (): boolean => this.#isResolved, - }); - - Object.defineProperty(onCancel, 'isRejected', { - get: (): boolean => this.#isRejected, - }); - - Object.defineProperty(onCancel, 'isCancelled', { - get: (): boolean => this.#isCancelled, - }); - - return executor(onResolve, onReject, onCancel as OnCancel); - }); - } - - get [Symbol.toStringTag]() { - return "Cancellable Promise"; - } - - public then( - onFulfilled?: ((value: T) => TResult1 | PromiseLike) | null, - onRejected?: ((reason: any) => TResult2 | PromiseLike) | null - ): Promise { - return this.#promise.then(onFulfilled, onRejected); - } - - public catch( - onRejected?: ((reason: any) => TResult | PromiseLike) | null - ): Promise { - return this.#promise.catch(onRejected); - } - - public finally(onFinally?: (() => void) | null): Promise { - return this.#promise.finally(onFinally); - } - - public cancel(): void { - if (this.#isResolved || this.#isRejected || this.#isCancelled) { - return; - } - this.#isCancelled = true; - if (this.#cancelHandlers.length) { - try { - for (const cancelHandler of this.#cancelHandlers) { - cancelHandler(); - } - } catch (error) { - console.warn('Cancellation threw an error', error); - return; - } - } - this.#cancelHandlers.length = 0; - if (this.#reject) this.#reject(new CancelError('Request aborted')); - } - - public get isCancelled(): boolean { - return this.#isCancelled; - } -} diff --git a/modules/frontend/src/api/core/OpenAPI.ts b/modules/frontend/src/api/core/OpenAPI.ts deleted file mode 100644 index 185e5c3..0000000 --- a/modules/frontend/src/api/core/OpenAPI.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* generated using openapi-typescript-codegen -- do not edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { ApiRequestOptions } from './ApiRequestOptions'; - -type Resolver = (options: ApiRequestOptions) => Promise; -type Headers = Record; - -export type OpenAPIConfig = { - BASE: string; - VERSION: string; - WITH_CREDENTIALS: boolean; - CREDENTIALS: 'include' | 'omit' | 'same-origin'; - TOKEN?: string | Resolver | undefined; - USERNAME?: string | Resolver | undefined; - PASSWORD?: string | Resolver | undefined; - HEADERS?: Headers | Resolver | undefined; - ENCODE_PATH?: ((path: string) => string) | undefined; -}; - -export const OpenAPI: OpenAPIConfig = { - BASE: '/api/v1', - VERSION: '1.0.0', - WITH_CREDENTIALS: false, - CREDENTIALS: 'include', - TOKEN: undefined, - USERNAME: undefined, - PASSWORD: undefined, - HEADERS: undefined, - ENCODE_PATH: undefined, -}; diff --git a/modules/frontend/src/api/core/request.ts b/modules/frontend/src/api/core/request.ts deleted file mode 100644 index 1dc6fef..0000000 --- a/modules/frontend/src/api/core/request.ts +++ /dev/null @@ -1,323 +0,0 @@ -/* generated using openapi-typescript-codegen -- do not edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import axios from 'axios'; -import type { AxiosError, AxiosRequestConfig, AxiosResponse, AxiosInstance } from 'axios'; -import FormData from 'form-data'; - -import { ApiError } from './ApiError'; -import type { ApiRequestOptions } from './ApiRequestOptions'; -import type { ApiResult } from './ApiResult'; -import { CancelablePromise } from './CancelablePromise'; -import type { OnCancel } from './CancelablePromise'; -import type { OpenAPIConfig } from './OpenAPI'; - -export const isDefined = (value: T | null | undefined): value is Exclude => { - return value !== undefined && value !== null; -}; - -export const isString = (value: any): value is string => { - return typeof value === 'string'; -}; - -export const isStringWithValue = (value: any): value is string => { - return isString(value) && value !== ''; -}; - -export const isBlob = (value: any): value is Blob => { - return ( - typeof value === 'object' && - typeof value.type === 'string' && - typeof value.stream === 'function' && - typeof value.arrayBuffer === 'function' && - typeof value.constructor === 'function' && - typeof value.constructor.name === 'string' && - /^(Blob|File)$/.test(value.constructor.name) && - /^(Blob|File)$/.test(value[Symbol.toStringTag]) - ); -}; - -export const isFormData = (value: any): value is FormData => { - return value instanceof FormData; -}; - -export const isSuccess = (status: number): boolean => { - return status >= 200 && status < 300; -}; - -export const base64 = (str: string): string => { - try { - return btoa(str); - } catch (err) { - // @ts-ignore - return Buffer.from(str).toString('base64'); - } -}; - -export const getQueryString = (params: Record): string => { - const qs: string[] = []; - - const append = (key: string, value: any) => { - qs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`); - }; - - const process = (key: string, value: any) => { - if (isDefined(value)) { - if (Array.isArray(value)) { - value.forEach(v => { - process(key, v); - }); - } else if (typeof value === 'object') { - Object.entries(value).forEach(([k, v]) => { - process(`${key}[${k}]`, v); - }); - } else { - append(key, value); - } - } - }; - - Object.entries(params).forEach(([key, value]) => { - process(key, value); - }); - - if (qs.length > 0) { - return `?${qs.join('&')}`; - } - - return ''; -}; - -const getUrl = (config: OpenAPIConfig, options: ApiRequestOptions): string => { - const encoder = config.ENCODE_PATH || encodeURI; - - const path = options.url - .replace('{api-version}', config.VERSION) - .replace(/{(.*?)}/g, (substring: string, group: string) => { - if (options.path?.hasOwnProperty(group)) { - return encoder(String(options.path[group])); - } - return substring; - }); - - const url = `${config.BASE}${path}`; - if (options.query) { - return `${url}${getQueryString(options.query)}`; - } - return url; -}; - -export const getFormData = (options: ApiRequestOptions): FormData | undefined => { - if (options.formData) { - const formData = new FormData(); - - const process = (key: string, value: any) => { - if (isString(value) || isBlob(value)) { - formData.append(key, value); - } else { - formData.append(key, JSON.stringify(value)); - } - }; - - Object.entries(options.formData) - .filter(([_, value]) => isDefined(value)) - .forEach(([key, value]) => { - if (Array.isArray(value)) { - value.forEach(v => process(key, v)); - } else { - process(key, value); - } - }); - - return formData; - } - return undefined; -}; - -type Resolver = (options: ApiRequestOptions) => Promise; - -export const resolve = async (options: ApiRequestOptions, resolver?: T | Resolver): Promise => { - if (typeof resolver === 'function') { - return (resolver as Resolver)(options); - } - return resolver; -}; - -export const getHeaders = async (config: OpenAPIConfig, options: ApiRequestOptions, formData?: FormData): Promise> => { - const [token, username, password, additionalHeaders] = await Promise.all([ - resolve(options, config.TOKEN), - resolve(options, config.USERNAME), - resolve(options, config.PASSWORD), - resolve(options, config.HEADERS), - ]); - - const formHeaders = typeof formData?.getHeaders === 'function' && formData?.getHeaders() || {} - - const headers = Object.entries({ - Accept: 'application/json', - ...additionalHeaders, - ...options.headers, - ...formHeaders, - }) - .filter(([_, value]) => isDefined(value)) - .reduce((headers, [key, value]) => ({ - ...headers, - [key]: String(value), - }), {} as Record); - - if (isStringWithValue(token)) { - headers['Authorization'] = `Bearer ${token}`; - } - - if (isStringWithValue(username) && isStringWithValue(password)) { - const credentials = base64(`${username}:${password}`); - headers['Authorization'] = `Basic ${credentials}`; - } - - if (options.body !== undefined) { - if (options.mediaType) { - headers['Content-Type'] = options.mediaType; - } else if (isBlob(options.body)) { - headers['Content-Type'] = options.body.type || 'application/octet-stream'; - } else if (isString(options.body)) { - headers['Content-Type'] = 'text/plain'; - } else if (!isFormData(options.body)) { - headers['Content-Type'] = 'application/json'; - } - } - - return headers; -}; - -export const getRequestBody = (options: ApiRequestOptions): any => { - if (options.body) { - return options.body; - } - return undefined; -}; - -export const sendRequest = async ( - config: OpenAPIConfig, - options: ApiRequestOptions, - url: string, - body: any, - formData: FormData | undefined, - headers: Record, - onCancel: OnCancel, - axiosClient: AxiosInstance -): Promise> => { - const source = axios.CancelToken.source(); - - const requestConfig: AxiosRequestConfig = { - url, - headers, - data: body ?? formData, - method: options.method, - withCredentials: config.WITH_CREDENTIALS, - withXSRFToken: config.CREDENTIALS === 'include' ? config.WITH_CREDENTIALS : false, - cancelToken: source.token, - }; - - onCancel(() => source.cancel('The user aborted a request.')); - - try { - return await axiosClient.request(requestConfig); - } catch (error) { - const axiosError = error as AxiosError; - if (axiosError.response) { - return axiosError.response; - } - throw error; - } -}; - -export const getResponseHeader = (response: AxiosResponse, responseHeader?: string): string | undefined => { - if (responseHeader) { - const content = response.headers[responseHeader]; - if (isString(content)) { - return content; - } - } - return undefined; -}; - -export const getResponseBody = (response: AxiosResponse): any => { - if (response.status !== 204) { - return response.data; - } - return undefined; -}; - -export const catchErrorCodes = (options: ApiRequestOptions, result: ApiResult): void => { - const errors: Record = { - 400: 'Bad Request', - 401: 'Unauthorized', - 403: 'Forbidden', - 404: 'Not Found', - 500: 'Internal Server Error', - 502: 'Bad Gateway', - 503: 'Service Unavailable', - ...options.errors, - } - - const error = errors[result.status]; - if (error) { - throw new ApiError(options, result, error); - } - - if (!result.ok) { - const errorStatus = result.status ?? 'unknown'; - const errorStatusText = result.statusText ?? 'unknown'; - const errorBody = (() => { - try { - return JSON.stringify(result.body, null, 2); - } catch (e) { - return undefined; - } - })(); - - throw new ApiError(options, result, - `Generic Error: status: ${errorStatus}; status text: ${errorStatusText}; body: ${errorBody}` - ); - } -}; - -/** - * Request method - * @param config The OpenAPI configuration object - * @param options The request options from the service - * @param axiosClient The axios client instance to use - * @returns CancelablePromise - * @throws ApiError - */ -export const request = (config: OpenAPIConfig, options: ApiRequestOptions, axiosClient: AxiosInstance = axios): CancelablePromise => { - return new CancelablePromise(async (resolve, reject, onCancel) => { - try { - const url = getUrl(config, options); - const formData = getFormData(options); - const body = getRequestBody(options); - const headers = await getHeaders(config, options, formData); - - if (!onCancel.isCancelled) { - const response = await sendRequest(config, options, url, body, formData, headers, onCancel, axiosClient); - const responseBody = getResponseBody(response); - const responseHeader = getResponseHeader(response, options.responseHeader); - - const result: ApiResult = { - url, - ok: isSuccess(response.status), - status: response.status, - statusText: response.statusText, - body: responseHeader ?? responseBody, - }; - - catchErrorCodes(options, result); - - resolve(result.body); - } - } catch (error) { - reject(error); - } - }); -}; diff --git a/modules/frontend/src/api/index.ts b/modules/frontend/src/api/index.ts deleted file mode 100644 index e4f4ef4..0000000 --- a/modules/frontend/src/api/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* generated using openapi-typescript-codegen -- do not edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export { ApiError } from './core/ApiError'; -export { CancelablePromise, CancelError } from './core/CancelablePromise'; -export { OpenAPI } from './core/OpenAPI'; -export type { OpenAPIConfig } from './core/OpenAPI'; - -export type { Review } from './models/Review'; -export type { Tag } from './models/Tag'; -export type { Title } from './models/Title'; -export type { User } from './models/User'; -export type { UserTitle } from './models/UserTitle'; - -export { DefaultService } from './services/DefaultService'; diff --git a/modules/frontend/src/api/models/Review.ts b/modules/frontend/src/api/models/Review.ts deleted file mode 100644 index 9b453b7..0000000 --- a/modules/frontend/src/api/models/Review.ts +++ /dev/null @@ -1,5 +0,0 @@ -/* generated using openapi-typescript-codegen -- do not edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export type Review = Record; diff --git a/modules/frontend/src/api/models/Tag.ts b/modules/frontend/src/api/models/Tag.ts deleted file mode 100644 index 9560ea8..0000000 --- a/modules/frontend/src/api/models/Tag.ts +++ /dev/null @@ -1,5 +0,0 @@ -/* generated using openapi-typescript-codegen -- do not edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export type Tag = Record; diff --git a/modules/frontend/src/api/models/Title.ts b/modules/frontend/src/api/models/Title.ts deleted file mode 100644 index 4da7aa3..0000000 --- a/modules/frontend/src/api/models/Title.ts +++ /dev/null @@ -1,5 +0,0 @@ -/* generated using openapi-typescript-codegen -- do not edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export type Title = Record; diff --git a/modules/frontend/src/api/models/User.ts b/modules/frontend/src/api/models/User.ts deleted file mode 100644 index b03d22f..0000000 --- a/modules/frontend/src/api/models/User.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* generated using openapi-typescript-codegen -- do not edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export type User = { - /** - * Unique user ID (primary key) - */ - id?: number; - /** - * ID of the user avatar (references images table) - */ - avatar_id?: number | null; - /** - * User email - */ - mail?: string; - /** - * Username (alphanumeric + _ or -) - */ - nickname: string; - /** - * Display name - */ - disp_name?: string; - /** - * User description - */ - user_desc?: string; - /** - * Timestamp when the user was created - */ - creation_date: string; -}; - diff --git a/modules/frontend/src/api/models/UserTitle.ts b/modules/frontend/src/api/models/UserTitle.ts deleted file mode 100644 index 26d5ddc..0000000 --- a/modules/frontend/src/api/models/UserTitle.ts +++ /dev/null @@ -1,5 +0,0 @@ -/* generated using openapi-typescript-codegen -- do not edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export type UserTitle = Record; diff --git a/modules/frontend/src/api/services/DefaultService.ts b/modules/frontend/src/api/services/DefaultService.ts deleted file mode 100644 index 7ebd129..0000000 --- a/modules/frontend/src/api/services/DefaultService.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* generated using openapi-typescript-codegen -- do not edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { User } from '../models/User'; -import type { CancelablePromise } from '../core/CancelablePromise'; -import { OpenAPI } from '../core/OpenAPI'; -import { request as __request } from '../core/request'; -export class DefaultService { - /** - * Get user info - * @param userId - * @param fields - * @returns User User info - * @throws ApiError - */ - public static getUsers( - userId: string, - fields: string = 'all', - ): CancelablePromise { - return __request(OpenAPI, { - method: 'GET', - url: '/users/{user_id}', - path: { - 'user_id': userId, - }, - query: { - 'fields': fields, - }, - errors: { - 404: `User not found`, - }, - }); - } -} diff --git a/modules/frontend/src/components/UserPage/UserPage.module.css b/modules/frontend/src/components/UserPage/UserPage.module.css deleted file mode 100644 index 75195bf..0000000 --- a/modules/frontend/src/components/UserPage/UserPage.module.css +++ /dev/null @@ -1,97 +0,0 @@ -.container { - display: flex; - justify-content: center; - align-items: flex-start; - padding: 3rem 1rem; - background-color: #f5f6fa; - min-height: 100vh; - font-family: "Inter", sans-serif; -} - -.card { - background-color: #ffffff; - border-radius: 1rem; - box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1); - padding: 2rem; - max-width: 400px; - width: 100%; - display: flex; - flex-direction: column; - align-items: center; - text-align: center; -} - -.avatar { - margin-bottom: 1.5rem; -} - -.avatarImg { - width: 120px; - height: 120px; - border-radius: 50%; - object-fit: cover; - border: 3px solid #4a90e2; -} - -.avatarPlaceholder { - width: 120px; - height: 120px; - border-radius: 50%; - background-color: #dcdde1; - display: flex; - align-items: center; - justify-content: center; - font-size: 3rem; - color: #4a4a4a; - font-weight: bold; - border: 3px solid #4a90e2; -} - -.info { - display: flex; - flex-direction: column; - align-items: center; -} - -.name { - font-size: 1.8rem; - font-weight: 700; - margin: 0.25rem 0; - color: #2f3640; -} - -.nickname { - font-size: 1rem; - color: #718093; - margin-bottom: 1rem; -} - -.desc { - font-size: 1rem; - color: #353b48; - margin-bottom: 1rem; -} - -.created { - font-size: 0.9rem; - color: #7f8fa6; -} - -.loader { - display: flex; - justify-content: center; - align-items: center; - height: 80vh; - font-size: 1.5rem; - color: #4a90e2; -} - -.error { - display: flex; - justify-content: center; - align-items: center; - height: 80vh; - color: #e84118; - font-weight: 600; - font-size: 1.2rem; -} diff --git a/modules/frontend/src/components/UserPage/UserPage.tsx b/modules/frontend/src/components/UserPage/UserPage.tsx deleted file mode 100644 index 0a83679..0000000 --- a/modules/frontend/src/components/UserPage/UserPage.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import React, { useEffect, useState } from "react"; -import { useParams } from "react-router-dom"; // <-- import -import { DefaultService } from "../../api/services/DefaultService"; -import type { User } from "../../api/models/User"; -import styles from "./UserPage.module.css"; - -const UserPage: React.FC = () => { - const { id } = useParams<{ id: string }>(); // <-- get user id from URL - const [user, setUser] = useState(null); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - - useEffect(() => { - if (!id) return; - - const getUserInfo = async () => { - try { - const userInfo = await DefaultService.getUsers(id, "all"); // <-- use dynamic id - setUser(userInfo); - } catch (err) { - console.error(err); - setError("Failed to fetch user info."); - } finally { - setLoading(false); - } - }; - getUserInfo(); - }, [id]); - - if (loading) return
Loading...
; - if (error) return
{error}
; - if (!user) return
User not found.
; - - return ( -
-
-
- {user.avatar_id ? ( - User Avatar - ) : ( -
- {user.disp_name?.[0] || "U"} -
- )} -
- -
-

{user.disp_name || user.nickname}

-

@{user.nickname}

- {user.user_desc &&

{user.user_desc}

} -

- Joined: {new Date(user.creation_date).toLocaleDateString()} -

-
-
-
- ); -}; - -export default UserPage; diff --git a/modules/sql/go.mod b/modules/sql/go.mod new file mode 100644 index 0000000..9508b28 --- /dev/null +++ b/modules/sql/go.mod @@ -0,0 +1,3 @@ +module tutorial.sqlc.dev/app + +go 1.25.2 diff --git a/modules/sql/query.sql b/modules/sql/query.sql new file mode 100644 index 0000000..4b85e7e --- /dev/null +++ b/modules/sql/query.sql @@ -0,0 +1,142 @@ +-- name: GetImageByID :one +SELECT image_id, storage_type, image_path +FROM images +WHERE image_id = $1; + +-- name: CreateImage :one +INSERT INTO images (storage_type, image_path) +VALUES ($1, $2) +RETURNING image_id, storage_type, image_path; + +-- name: GetUserByID :one +SELECT user_id, avatar_id, passhash, mail, nickname, disp_name, user_desc, creation_date +FROM users +WHERE user_id = $1; + +-- name: ListUsers :many +SELECT user_id, avatar_id, passhash, mail, nickname, disp_name, user_desc, creation_date +FROM users +ORDER BY user_id +LIMIT $1 OFFSET $2; + +-- name: CreateUser :one +INSERT INTO users (avatar_id, passhash, mail, nickname, disp_name, user_desc, creation_date) +VALUES ($1, $2, $3, $4, $5, $6, $7) +RETURNING user_id, avatar_id, nickname, disp_name, user_desc, creation_date; + +-- name: UpdateUser :one +UPDATE users +SET + avatar_id = COALESCE(sqlc.narg('avatar_id'), avatar_id), + disp_name = COALESCE(sqlc.narg('disp_name'), disp_name), + user_desc = COALESCE(sqlc.narg('user_desc'), user_desc), + passhash = COALESCE(sqlc.narg('passhash'), passhash), +WHERE user_id = sqlc.arg('user_id') +RETURNING user_id, avatar_id, nickname, disp_name, user_desc, creation_date; + +-- name: DeleteUser :exec +DELETE FROM users +WHERE user_id = $1; + +-- name: GetTitleByID :one +SELECT title_id, title_names, studio_id, poster_id, signal_ids, + title_status, rating, rating_count, release_year, release_season, + season, episodes_aired, episodes_all, episodes_len +FROM titles +WHERE title_id = $1; + +-- name: ListTitles :many +SELECT title_id, title_names, studio_id, poster_id, signal_ids, + title_status, rating, rating_count, release_year, release_season, + season, episodes_aired, episodes_all, episodes_len +FROM titles +ORDER BY title_id +LIMIT $1 OFFSET $2; + +-- name: UpdateTitle :one +UPDATE titles +SET + title_names = COALESCE(sqlc.narg('title_names'), title_names), + studio_id = COALESCE(sqlc.narg('studio_id'), studio_id), + poster_id = COALESCE(sqlc.narg('poster_id'), poster_id), + signal_ids = COALESCE(sqlc.narg('signal_ids'), signal_ids), + title_status = COALESCE(sqlc.narg('title_status'), title_status), + release_year = COALESCE(sqlc.narg('release_year'), release_year), + release_season = COALESCE(sqlc.narg('release_season'), release_season), + episodes_aired = COALESCE(sqlc.narg('episodes_aired'), episodes_aired), + episodes_all = COALESCE(sqlc.narg('episodes_all'), episodes_all), + episodes_len = COALESCE(sqlc.narg('episodes_len'), episodes_len) +WHERE title_id = sqlc.arg('title_id') +RETURNING *; + +-- name: GetReviewByID :one +SELECT review_id, user_id, title_id, image_ids, review_text, creation_date +FROM reviews +WHERE review_id = $1; + +-- name: CreateReview :one +INSERT INTO reviews (user_id, title_id, image_ids, review_text, creation_date) +VALUES ($1, $2, $3, $4, $5) +RETURNING review_id, user_id, title_id, image_ids, review_text, creation_date; + +-- name: UpdateReview :one +UPDATE reviews +SET + image_ids = COALESCE(sqlc.narg('image_ids'), image_ids), + review_text = COALESCE(sqlc.narg('review_text'), review_text) +WHERE review_id = sqlc.arg('review_id') +RETURNING *; + +-- name: DeleteReview :exec +DELETE FROM reviews +WHERE review_id = $1; + +-- name: ListReviewsByTitle :many +SELECT review_id, user_id, title_id, image_ids, review_text, creation_date +FROM reviews +WHERE title_id = $1 +ORDER BY creation_date DESC +LIMIT $2 OFFSET $3; + +-- name: ListReviewsByUser :many +SELECT review_id, user_id, title_id, image_ids, review_text, creation_date +FROM reviews +WHERE user_id = $1 +ORDER BY creation_date DESC +LIMIT $2 OFFSET $3; + +-- name: GetUserTitle :one +SELECT usertitle_id, user_id, title_id, status, rate, review_id +FROM usertitles +WHERE user_id = $1 AND title_id = $2; + +-- name: ListUserTitles :many +SELECT usertitle_id, user_id, title_id, status, rate, review_id +FROM usertitles +WHERE user_id = $1 +ORDER BY usertitle_id +LIMIT $2 OFFSET $3; + +-- name: CreateUserTitle :one +INSERT INTO usertitles (user_id, title_id, status, rate, review_id) +VALUES ($1, $2, $3, $4, $5) +RETURNING usertitle_id, user_id, title_id, status, rate, review_id; + +-- name: UpdateUserTitle :one +UPDATE usertitles +SET + status = COALESCE(sqlc.narg('status'), status), + rate = COALESCE(sqlc.narg('rate'), rate), + review_id = COALESCE(sqlc.narg('review_id'), review_id) +WHERE user_id = $1 AND title_id = $2 +RETURNING *; + +-- name: DeleteUserTitle :exec +DELETE FROM usertitles +WHERE user_id = $1 AND ($2::int IS NULL OR title_id = $2); + +-- name: ListTags :many +SELECT tag_id, tag_names +FROM tags +ORDER BY tag_id +LIMIT $1 OFFSET $2; \ No newline at end of file diff --git a/modules/sql/scheme.sql b/modules/sql/scheme.sql new file mode 100644 index 0000000..df84550 --- /dev/null +++ b/modules/sql/scheme.sql @@ -0,0 +1,97 @@ +-- TODO: +-- title table triggers +-- maybe jsonb constraints +-- actions (delete) +BEGIN; + +CREATE TYPE usertitle_status_t AS ENUM ('finished', 'planned', 'dropped', 'in-progress'); +CREATE TYPE storage_type_t AS ENUM ('local', 's3'); +CREATE TYPE title_status_t AS ENUM ('finished', 'ongoing', 'planned'); +CREATE TYPE release_season_t AS ENUM ('winter', 'spring', 'summer', 'fall'); + +CREATE TABLE providers ( + provider_id serial PRIMARY KEY, + provider_name varchar(64) NOT NULL +); + +CREATE TABLE tags ( + tag_id serial PRIMARY KEY, + tag_names jsonb NOT NULL --mb constraints +); + +CREATE TABLE images ( + image_id serial PRIMARY KEY, + storage_type storage_type_t NOT NULL, + image_path varchar(256) UNIQUE NOT NULL +); + +CREATE TABLE users ( + user_id serial PRIMARY KEY, + avatar_id int REFERENCES images (image_id), + passhash text NOT NULL, + mail varchar(64) CHECK (mail ~ '[a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+'), + nickname varchar(16) NOT NULL CHECK (nickname ~ '^[a-zA-Z0-9_-]+$'), + disp_name varchar(32), + user_desc varchar(512), + creation_date timestamp NOT NULL +); + +CREATE TABLE studios ( + studio_id serial PRIMARY KEY, + studio_name varchar(64) UNIQUE, + illust_id int REFERENCES images (image_id), + studio_desc text +); + +CREATE TABLE titles ( + title_id serial PRIMARY KEY, + title_names jsonb NOT NULL, + studio_id int NOT NULL REFERENCES studios, + poster_id int REFERENCES images (image_id), + signal_ids int[] NOT NULL, + title_status title_status_t NOT NULL, + rating float CHECK (rating > 0 AND rating <= 10), --by trigger + rating_count int CHECK (rating_count >= 0), --by trigger + release_year int CHECK (release_year >= 1900), + release_season release_season_t, + season int CHECK (season >= 0), + episodes_aired int CHECK (episodes_aired >= 0), + episodes_all int CHECK (episodes_all >= 0), + episodes_len jsonb, + CHECK ((episodes_aired IS NULL AND episodes_all IS NULL) + OR (episodes_aired IS NOT NULL AND episodes_all IS NOT NULL + AND episodes_aired <= episodes_all)) +); + +CREATE TABLE reviews ( + review_id serial PRIMARY KEY, + user_id int NOT NULL REFERENCES users, + title_id int NOT NULL REFERENCES titles, + image_ids int[], + review_text text NOT NULL, + creation_date timestamp NOT NULL +); + +CREATE TABLE usertitles ( + usertitle_id serial PRIMARY KEY, + user_id int NOT NULL REFERENCES users, + title_id int NOT NULL REFERENCES titles, + status usertitle_status_t NOT NULL, + rate int CHECK (rate > 0 AND rate <= 10), + review_id int REFERENCES reviews +); + +CREATE TABLE title_tags ( + PRIMARY KEY (title_id, tag_id), + title_id int NOT NULL REFERENCES titles, + tag_id int NOT NULL REFERENCES tags +); + +CREATE TABLE signals ( + signal_id serial PRIMARY KEY, + raw_data jsonb NOT NULL, + provider_id int NOT NULL REFERENCES providers, + dirty bool NOT NULL +); + +COMMIT; \ No newline at end of file diff --git a/modules/sql/sqlc-generate.yaml b/modules/sql/sqlc-generate.yaml new file mode 100644 index 0000000..8a625de --- /dev/null +++ b/modules/sql/sqlc-generate.yaml @@ -0,0 +1,13 @@ +version: "2" +sql: + - engine: "postgresql" + queries: "query.sql" + schema: "scheme.sql" + gen: + go: + package: "nyanimedb.backend" + out: "sqlc" + sql_package: "pgx/v5" + sql_driver: "github.com/lib/pq" + emit_db_tags: true + emit_exact_table_names: true \ No newline at end of file diff --git a/modules/sql/sqlc.yaml b/modules/sql/sqlc.yaml new file mode 100644 index 0000000..079626a --- /dev/null +++ b/modules/sql/sqlc.yaml @@ -0,0 +1,14 @@ +version: "2" +sql: + - engine: "postgresql" + queries: "query.sql" + schema: "scheme.sql" + gen: + go: + package: "nyanimedb_backend" + out: "sqlc" + sql_package: "pgx/v5" + sql_driver: "github.com/lib/pq" + emit_db_tags: true + emit_exact_table_names: true + emit_json_tags: true \ No newline at end of file diff --git a/modules/sql/sqlc/db.go b/modules/sql/sqlc/db.go new file mode 100644 index 0000000..cdbc865 --- /dev/null +++ b/modules/sql/sqlc/db.go @@ -0,0 +1,32 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 + +package nyanimedb_backend + +import ( + "context" + + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" +) + +type DBTX interface { + Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error) + Query(context.Context, string, ...interface{}) (pgx.Rows, error) + QueryRow(context.Context, string, ...interface{}) pgx.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx pgx.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/modules/sql/sqlc/models.go b/modules/sql/sqlc/models.go new file mode 100644 index 0000000..e7aeb3c --- /dev/null +++ b/modules/sql/sqlc/models.go @@ -0,0 +1,266 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 + +package nyanimedb_backend + +import ( + "database/sql/driver" + "fmt" + + "github.com/jackc/pgx/v5/pgtype" +) + +type ReleaseSeasonT string + +const ( + ReleaseSeasonTWinter ReleaseSeasonT = "winter" + ReleaseSeasonTSpring ReleaseSeasonT = "spring" + ReleaseSeasonTSummer ReleaseSeasonT = "summer" + ReleaseSeasonTFall ReleaseSeasonT = "fall" +) + +func (e *ReleaseSeasonT) Scan(src interface{}) error { + switch s := src.(type) { + case []byte: + *e = ReleaseSeasonT(s) + case string: + *e = ReleaseSeasonT(s) + default: + return fmt.Errorf("unsupported scan type for ReleaseSeasonT: %T", src) + } + return nil +} + +type NullReleaseSeasonT struct { + ReleaseSeasonT ReleaseSeasonT `json:"release_season_t"` + Valid bool `json:"valid"` // Valid is true if ReleaseSeasonT is not NULL +} + +// Scan implements the Scanner interface. +func (ns *NullReleaseSeasonT) Scan(value interface{}) error { + if value == nil { + ns.ReleaseSeasonT, ns.Valid = "", false + return nil + } + ns.Valid = true + return ns.ReleaseSeasonT.Scan(value) +} + +// Value implements the driver Valuer interface. +func (ns NullReleaseSeasonT) Value() (driver.Value, error) { + if !ns.Valid { + return nil, nil + } + return string(ns.ReleaseSeasonT), nil +} + +type StorageTypeT string + +const ( + StorageTypeTLocal StorageTypeT = "local" + StorageTypeTS3 StorageTypeT = "s3" +) + +func (e *StorageTypeT) Scan(src interface{}) error { + switch s := src.(type) { + case []byte: + *e = StorageTypeT(s) + case string: + *e = StorageTypeT(s) + default: + return fmt.Errorf("unsupported scan type for StorageTypeT: %T", src) + } + return nil +} + +type NullStorageTypeT struct { + StorageTypeT StorageTypeT `json:"storage_type_t"` + Valid bool `json:"valid"` // Valid is true if StorageTypeT is not NULL +} + +// Scan implements the Scanner interface. +func (ns *NullStorageTypeT) Scan(value interface{}) error { + if value == nil { + ns.StorageTypeT, ns.Valid = "", false + return nil + } + ns.Valid = true + return ns.StorageTypeT.Scan(value) +} + +// Value implements the driver Valuer interface. +func (ns NullStorageTypeT) Value() (driver.Value, error) { + if !ns.Valid { + return nil, nil + } + return string(ns.StorageTypeT), nil +} + +type TitleStatusT string + +const ( + TitleStatusTFinished TitleStatusT = "finished" + TitleStatusTOngoing TitleStatusT = "ongoing" + TitleStatusTPlanned TitleStatusT = "planned" +) + +func (e *TitleStatusT) Scan(src interface{}) error { + switch s := src.(type) { + case []byte: + *e = TitleStatusT(s) + case string: + *e = TitleStatusT(s) + default: + return fmt.Errorf("unsupported scan type for TitleStatusT: %T", src) + } + return nil +} + +type NullTitleStatusT struct { + TitleStatusT TitleStatusT `json:"title_status_t"` + Valid bool `json:"valid"` // Valid is true if TitleStatusT is not NULL +} + +// Scan implements the Scanner interface. +func (ns *NullTitleStatusT) Scan(value interface{}) error { + if value == nil { + ns.TitleStatusT, ns.Valid = "", false + return nil + } + ns.Valid = true + return ns.TitleStatusT.Scan(value) +} + +// Value implements the driver Valuer interface. +func (ns NullTitleStatusT) Value() (driver.Value, error) { + if !ns.Valid { + return nil, nil + } + return string(ns.TitleStatusT), nil +} + +type UsertitleStatusT string + +const ( + UsertitleStatusTFinished UsertitleStatusT = "finished" + UsertitleStatusTPlanned UsertitleStatusT = "planned" + UsertitleStatusTDropped UsertitleStatusT = "dropped" + UsertitleStatusTInProgress UsertitleStatusT = "in-progress" +) + +func (e *UsertitleStatusT) Scan(src interface{}) error { + switch s := src.(type) { + case []byte: + *e = UsertitleStatusT(s) + case string: + *e = UsertitleStatusT(s) + default: + return fmt.Errorf("unsupported scan type for UsertitleStatusT: %T", src) + } + return nil +} + +type NullUsertitleStatusT struct { + UsertitleStatusT UsertitleStatusT `json:"usertitle_status_t"` + Valid bool `json:"valid"` // Valid is true if UsertitleStatusT is not NULL +} + +// Scan implements the Scanner interface. +func (ns *NullUsertitleStatusT) Scan(value interface{}) error { + if value == nil { + ns.UsertitleStatusT, ns.Valid = "", false + return nil + } + ns.Valid = true + return ns.UsertitleStatusT.Scan(value) +} + +// Value implements the driver Valuer interface. +func (ns NullUsertitleStatusT) Value() (driver.Value, error) { + if !ns.Valid { + return nil, nil + } + return string(ns.UsertitleStatusT), nil +} + +type Images struct { + ImageID int32 `db:"image_id" json:"image_id"` + StorageType StorageTypeT `db:"storage_type" json:"storage_type"` + ImagePath string `db:"image_path" json:"image_path"` +} + +type Providers struct { + ProviderID int32 `db:"provider_id" json:"provider_id"` + ProviderName string `db:"provider_name" json:"provider_name"` +} + +type Reviews struct { + ReviewID int32 `db:"review_id" json:"review_id"` + UserID int32 `db:"user_id" json:"user_id"` + TitleID int32 `db:"title_id" json:"title_id"` + ImageIds []int32 `db:"image_ids" json:"image_ids"` + ReviewText string `db:"review_text" json:"review_text"` + CreationDate pgtype.Timestamp `db:"creation_date" json:"creation_date"` +} + +type Signals struct { + SignalID int32 `db:"signal_id" json:"signal_id"` + RawData []byte `db:"raw_data" json:"raw_data"` + ProviderID int32 `db:"provider_id" json:"provider_id"` + Dirty bool `db:"dirty" json:"dirty"` +} + +type Studios struct { + StudioID int32 `db:"studio_id" json:"studio_id"` + StudioName pgtype.Text `db:"studio_name" json:"studio_name"` + IllustID pgtype.Int4 `db:"illust_id" json:"illust_id"` + StudioDesc pgtype.Text `db:"studio_desc" json:"studio_desc"` +} + +type Tags struct { + TagID int32 `db:"tag_id" json:"tag_id"` + TagNames []byte `db:"tag_names" json:"tag_names"` +} + +type TitleTags struct { + TitleID int32 `db:"title_id" json:"title_id"` + TagID int32 `db:"tag_id" json:"tag_id"` +} + +type Titles struct { + TitleID int32 `db:"title_id" json:"title_id"` + TitleNames []byte `db:"title_names" json:"title_names"` + StudioID int32 `db:"studio_id" json:"studio_id"` + PosterID pgtype.Int4 `db:"poster_id" json:"poster_id"` + SignalIds []int32 `db:"signal_ids" json:"signal_ids"` + TitleStatus TitleStatusT `db:"title_status" json:"title_status"` + Rating pgtype.Float8 `db:"rating" json:"rating"` + RatingCount pgtype.Int4 `db:"rating_count" json:"rating_count"` + ReleaseYear pgtype.Int4 `db:"release_year" json:"release_year"` + ReleaseSeason NullReleaseSeasonT `db:"release_season" json:"release_season"` + Season pgtype.Int4 `db:"season" json:"season"` + EpisodesAired pgtype.Int4 `db:"episodes_aired" json:"episodes_aired"` + EpisodesAll pgtype.Int4 `db:"episodes_all" json:"episodes_all"` + EpisodesLen []byte `db:"episodes_len" json:"episodes_len"` +} + +type Users struct { + UserID int32 `db:"user_id" json:"user_id"` + AvatarID pgtype.Int4 `db:"avatar_id" json:"avatar_id"` + Passhash string `db:"passhash" json:"passhash"` + Mail pgtype.Text `db:"mail" json:"mail"` + Nickname string `db:"nickname" json:"nickname"` + DispName pgtype.Text `db:"disp_name" json:"disp_name"` + UserDesc pgtype.Text `db:"user_desc" json:"user_desc"` + CreationDate pgtype.Timestamp `db:"creation_date" json:"creation_date"` +} + +type Usertitles struct { + UsertitleID int32 `db:"usertitle_id" json:"usertitle_id"` + UserID int32 `db:"user_id" json:"user_id"` + TitleID int32 `db:"title_id" json:"title_id"` + Status UsertitleStatusT `db:"status" json:"status"` + Rate pgtype.Int4 `db:"rate" json:"rate"` + ReviewID pgtype.Int4 `db:"review_id" json:"review_id"` +} diff --git a/modules/sql/sqlc/query.sql.go b/modules/sql/sqlc/query.sql.go new file mode 100644 index 0000000..1fc06ac --- /dev/null +++ b/modules/sql/sqlc/query.sql.go @@ -0,0 +1,712 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 +// source: query.sql + +package db + +import ( + "context" + + "github.com/jackc/pgx/v5/pgtype" +) + +const createImage = `-- name: CreateImage :one +INSERT INTO images (storage_type, image_path) +VALUES ($1, $2) +RETURNING image_id, storage_type, image_path +` + +type CreateImageParams struct { + StorageType StorageTypeT `db:"storage_type" json:"storage_type"` + ImagePath string `db:"image_path" json:"image_path"` +} + +func (q *Queries) CreateImage(ctx context.Context, arg CreateImageParams) (Images, error) { + row := q.db.QueryRow(ctx, createImage, arg.StorageType, arg.ImagePath) + var i Images + err := row.Scan(&i.ImageID, &i.StorageType, &i.ImagePath) + return i, err +} + +const createReview = `-- name: CreateReview :one +INSERT INTO reviews (user_id, title_id, image_ids, review_text, creation_date) +VALUES ($1, $2, $3, $4, $5) +RETURNING review_id, user_id, title_id, image_ids, review_text, creation_date +` + +type CreateReviewParams struct { + UserID int32 `db:"user_id" json:"user_id"` + TitleID int32 `db:"title_id" json:"title_id"` + ImageIds []int32 `db:"image_ids" json:"image_ids"` + ReviewText string `db:"review_text" json:"review_text"` + CreationDate pgtype.Timestamp `db:"creation_date" json:"creation_date"` +} + +func (q *Queries) CreateReview(ctx context.Context, arg CreateReviewParams) (Reviews, error) { + row := q.db.QueryRow(ctx, createReview, + arg.UserID, + arg.TitleID, + arg.ImageIds, + arg.ReviewText, + arg.CreationDate, + ) + var i Reviews + err := row.Scan( + &i.ReviewID, + &i.UserID, + &i.TitleID, + &i.ImageIds, + &i.ReviewText, + &i.CreationDate, + ) + return i, err +} + +const createUser = `-- name: CreateUser :one +INSERT INTO users (avatar_id, passhash, mail, nickname, disp_name, user_desc, creation_date) +VALUES ($1, $2, $3, $4, $5, $6, $7) +RETURNING user_id, avatar_id, nickname, disp_name, user_desc, creation_date +` + +type CreateUserParams struct { + AvatarID pgtype.Int4 `db:"avatar_id" json:"avatar_id"` + Passhash string `db:"passhash" json:"passhash"` + Mail pgtype.Text `db:"mail" json:"mail"` + Nickname string `db:"nickname" json:"nickname"` + DispName pgtype.Text `db:"disp_name" json:"disp_name"` + UserDesc pgtype.Text `db:"user_desc" json:"user_desc"` + CreationDate pgtype.Timestamp `db:"creation_date" json:"creation_date"` +} + +type CreateUserRow struct { + UserID int32 `db:"user_id" json:"user_id"` + AvatarID pgtype.Int4 `db:"avatar_id" json:"avatar_id"` + Nickname string `db:"nickname" json:"nickname"` + DispName pgtype.Text `db:"disp_name" json:"disp_name"` + UserDesc pgtype.Text `db:"user_desc" json:"user_desc"` + CreationDate pgtype.Timestamp `db:"creation_date" json:"creation_date"` +} + +func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (CreateUserRow, error) { + row := q.db.QueryRow(ctx, createUser, + arg.AvatarID, + arg.Passhash, + arg.Mail, + arg.Nickname, + arg.DispName, + arg.UserDesc, + arg.CreationDate, + ) + var i CreateUserRow + err := row.Scan( + &i.UserID, + &i.AvatarID, + &i.Nickname, + &i.DispName, + &i.UserDesc, + &i.CreationDate, + ) + return i, err +} + +const createUserTitle = `-- name: CreateUserTitle :one +INSERT INTO usertitles (user_id, title_id, status, rate, review_id) +VALUES ($1, $2, $3, $4, $5) +RETURNING usertitle_id, user_id, title_id, status, rate, review_id +` + +type CreateUserTitleParams struct { + UserID int32 `db:"user_id" json:"user_id"` + TitleID int32 `db:"title_id" json:"title_id"` + Status UsertitleStatusT `db:"status" json:"status"` + Rate pgtype.Int4 `db:"rate" json:"rate"` + ReviewID pgtype.Int4 `db:"review_id" json:"review_id"` +} + +func (q *Queries) CreateUserTitle(ctx context.Context, arg CreateUserTitleParams) (Usertitles, error) { + row := q.db.QueryRow(ctx, createUserTitle, + arg.UserID, + arg.TitleID, + arg.Status, + arg.Rate, + arg.ReviewID, + ) + var i Usertitles + err := row.Scan( + &i.UsertitleID, + &i.UserID, + &i.TitleID, + &i.Status, + &i.Rate, + &i.ReviewID, + ) + return i, err +} + +const deleteReview = `-- name: DeleteReview :exec +DELETE FROM reviews +WHERE review_id = $1 +` + +func (q *Queries) DeleteReview(ctx context.Context, reviewID int32) error { + _, err := q.db.Exec(ctx, deleteReview, reviewID) + return err +} + +const deleteUser = `-- name: DeleteUser :exec +DELETE FROM users +WHERE user_id = $1 +` + +func (q *Queries) DeleteUser(ctx context.Context, userID int32) error { + _, err := q.db.Exec(ctx, deleteUser, userID) + return err +} + +const deleteUserTitle = `-- name: DeleteUserTitle :exec +DELETE FROM usertitles +WHERE user_id = $1 AND ($2::int IS NULL OR title_id = $2) +` + +type DeleteUserTitleParams struct { + UserID int32 `db:"user_id" json:"user_id"` + Column2 int32 `db:"column_2" json:"column_2"` +} + +func (q *Queries) DeleteUserTitle(ctx context.Context, arg DeleteUserTitleParams) error { + _, err := q.db.Exec(ctx, deleteUserTitle, arg.UserID, arg.Column2) + return err +} + +const getImageByID = `-- name: GetImageByID :one +SELECT image_id, storage_type, image_path +FROM images +WHERE image_id = $1 +` + +func (q *Queries) GetImageByID(ctx context.Context, imageID int32) (Images, error) { + row := q.db.QueryRow(ctx, getImageByID, imageID) + var i Images + err := row.Scan(&i.ImageID, &i.StorageType, &i.ImagePath) + return i, err +} + +const getReviewByID = `-- name: GetReviewByID :one +SELECT review_id, user_id, title_id, image_ids, review_text, creation_date +FROM reviews +WHERE review_id = $1 +` + +func (q *Queries) GetReviewByID(ctx context.Context, reviewID int32) (Reviews, error) { + row := q.db.QueryRow(ctx, getReviewByID, reviewID) + var i Reviews + err := row.Scan( + &i.ReviewID, + &i.UserID, + &i.TitleID, + &i.ImageIds, + &i.ReviewText, + &i.CreationDate, + ) + return i, err +} + +const getTitleByID = `-- name: GetTitleByID :one +SELECT title_id, title_names, studio_id, poster_id, signal_ids, + title_status, rating, rating_count, release_year, release_season, + season, episodes_aired, episodes_all, episodes_len +FROM titles +WHERE title_id = $1 +` + +func (q *Queries) GetTitleByID(ctx context.Context, titleID int32) (Titles, error) { + row := q.db.QueryRow(ctx, getTitleByID, titleID) + var i Titles + err := row.Scan( + &i.TitleID, + &i.TitleNames, + &i.StudioID, + &i.PosterID, + &i.SignalIds, + &i.TitleStatus, + &i.Rating, + &i.RatingCount, + &i.ReleaseYear, + &i.ReleaseSeason, + &i.Season, + &i.EpisodesAired, + &i.EpisodesAll, + &i.EpisodesLen, + ) + return i, err +} + +const getUserByID = `-- name: GetUserByID :one +SELECT user_id, avatar_id, passhash, mail, nickname, disp_name, user_desc, creation_date +FROM users +WHERE user_id = $1 +` + +func (q *Queries) GetUserByID(ctx context.Context, userID int32) (Users, error) { + row := q.db.QueryRow(ctx, getUserByID, userID) + var i Users + err := row.Scan( + &i.UserID, + &i.AvatarID, + &i.Passhash, + &i.Mail, + &i.Nickname, + &i.DispName, + &i.UserDesc, + &i.CreationDate, + ) + return i, err +} + +const getUserTitle = `-- name: GetUserTitle :one +SELECT usertitle_id, user_id, title_id, status, rate, review_id +FROM usertitles +WHERE user_id = $1 AND title_id = $2 +` + +type GetUserTitleParams struct { + UserID int32 `db:"user_id" json:"user_id"` + TitleID int32 `db:"title_id" json:"title_id"` +} + +func (q *Queries) GetUserTitle(ctx context.Context, arg GetUserTitleParams) (Usertitles, error) { + row := q.db.QueryRow(ctx, getUserTitle, arg.UserID, arg.TitleID) + var i Usertitles + err := row.Scan( + &i.UsertitleID, + &i.UserID, + &i.TitleID, + &i.Status, + &i.Rate, + &i.ReviewID, + ) + return i, err +} + +const listReviewsByTitle = `-- name: ListReviewsByTitle :many +SELECT review_id, user_id, title_id, image_ids, review_text, creation_date +FROM reviews +WHERE title_id = $1 +ORDER BY creation_date DESC +LIMIT $2 OFFSET $3 +` + +type ListReviewsByTitleParams struct { + TitleID int32 `db:"title_id" json:"title_id"` + Limit int32 `db:"limit" json:"limit"` + Offset int32 `db:"offset" json:"offset"` +} + +func (q *Queries) ListReviewsByTitle(ctx context.Context, arg ListReviewsByTitleParams) ([]Reviews, error) { + rows, err := q.db.Query(ctx, listReviewsByTitle, arg.TitleID, arg.Limit, arg.Offset) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Reviews + for rows.Next() { + var i Reviews + if err := rows.Scan( + &i.ReviewID, + &i.UserID, + &i.TitleID, + &i.ImageIds, + &i.ReviewText, + &i.CreationDate, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listReviewsByUser = `-- name: ListReviewsByUser :many +SELECT review_id, user_id, title_id, image_ids, review_text, creation_date +FROM reviews +WHERE user_id = $1 +ORDER BY creation_date DESC +LIMIT $2 OFFSET $3 +` + +type ListReviewsByUserParams struct { + UserID int32 `db:"user_id" json:"user_id"` + Limit int32 `db:"limit" json:"limit"` + Offset int32 `db:"offset" json:"offset"` +} + +func (q *Queries) ListReviewsByUser(ctx context.Context, arg ListReviewsByUserParams) ([]Reviews, error) { + rows, err := q.db.Query(ctx, listReviewsByUser, arg.UserID, arg.Limit, arg.Offset) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Reviews + for rows.Next() { + var i Reviews + if err := rows.Scan( + &i.ReviewID, + &i.UserID, + &i.TitleID, + &i.ImageIds, + &i.ReviewText, + &i.CreationDate, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listTags = `-- name: ListTags :many +SELECT tag_id, tag_names +FROM tags +ORDER BY tag_id +LIMIT $1 OFFSET $2 +` + +type ListTagsParams struct { + Limit int32 `db:"limit" json:"limit"` + Offset int32 `db:"offset" json:"offset"` +} + +func (q *Queries) ListTags(ctx context.Context, arg ListTagsParams) ([]Tags, error) { + rows, err := q.db.Query(ctx, listTags, arg.Limit, arg.Offset) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Tags + for rows.Next() { + var i Tags + if err := rows.Scan(&i.TagID, &i.TagNames); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listTitles = `-- name: ListTitles :many +SELECT title_id, title_names, studio_id, poster_id, signal_ids, + title_status, rating, rating_count, release_year, release_season, + season, episodes_aired, episodes_all, episodes_len +FROM titles +ORDER BY title_id +LIMIT $1 OFFSET $2 +` + +type ListTitlesParams struct { + Limit int32 `db:"limit" json:"limit"` + Offset int32 `db:"offset" json:"offset"` +} + +func (q *Queries) ListTitles(ctx context.Context, arg ListTitlesParams) ([]Titles, error) { + rows, err := q.db.Query(ctx, listTitles, arg.Limit, arg.Offset) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Titles + for rows.Next() { + var i Titles + if err := rows.Scan( + &i.TitleID, + &i.TitleNames, + &i.StudioID, + &i.PosterID, + &i.SignalIds, + &i.TitleStatus, + &i.Rating, + &i.RatingCount, + &i.ReleaseYear, + &i.ReleaseSeason, + &i.Season, + &i.EpisodesAired, + &i.EpisodesAll, + &i.EpisodesLen, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listUserTitles = `-- name: ListUserTitles :many +SELECT usertitle_id, user_id, title_id, status, rate, review_id +FROM usertitles +WHERE user_id = $1 +ORDER BY usertitle_id +LIMIT $2 OFFSET $3 +` + +type ListUserTitlesParams struct { + UserID int32 `db:"user_id" json:"user_id"` + Limit int32 `db:"limit" json:"limit"` + Offset int32 `db:"offset" json:"offset"` +} + +func (q *Queries) ListUserTitles(ctx context.Context, arg ListUserTitlesParams) ([]Usertitles, error) { + rows, err := q.db.Query(ctx, listUserTitles, arg.UserID, arg.Limit, arg.Offset) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Usertitles + for rows.Next() { + var i Usertitles + if err := rows.Scan( + &i.UsertitleID, + &i.UserID, + &i.TitleID, + &i.Status, + &i.Rate, + &i.ReviewID, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listUsers = `-- name: ListUsers :many +SELECT user_id, avatar_id, passhash, mail, nickname, disp_name, user_desc, creation_date +FROM users +ORDER BY user_id +LIMIT $1 OFFSET $2 +` + +type ListUsersParams struct { + Limit int32 `db:"limit" json:"limit"` + Offset int32 `db:"offset" json:"offset"` +} + +func (q *Queries) ListUsers(ctx context.Context, arg ListUsersParams) ([]Users, error) { + rows, err := q.db.Query(ctx, listUsers, arg.Limit, arg.Offset) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Users + for rows.Next() { + var i Users + if err := rows.Scan( + &i.UserID, + &i.AvatarID, + &i.Passhash, + &i.Mail, + &i.Nickname, + &i.DispName, + &i.UserDesc, + &i.CreationDate, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const updateReview = `-- name: UpdateReview :one +UPDATE reviews +SET + image_ids = COALESCE($1, image_ids), + review_text = COALESCE($2, review_text) +WHERE review_id = $3 +RETURNING review_id, user_id, title_id, image_ids, review_text, creation_date +` + +type UpdateReviewParams struct { + ImageIds []int32 `db:"image_ids" json:"image_ids"` + ReviewText pgtype.Text `db:"review_text" json:"review_text"` + ReviewID int32 `db:"review_id" json:"review_id"` +} + +func (q *Queries) UpdateReview(ctx context.Context, arg UpdateReviewParams) (Reviews, error) { + row := q.db.QueryRow(ctx, updateReview, arg.ImageIds, arg.ReviewText, arg.ReviewID) + var i Reviews + err := row.Scan( + &i.ReviewID, + &i.UserID, + &i.TitleID, + &i.ImageIds, + &i.ReviewText, + &i.CreationDate, + ) + return i, err +} + +const updateTitle = `-- name: UpdateTitle :one +UPDATE titles +SET + title_names = COALESCE($1, title_names), + studio_id = COALESCE($2, studio_id), + poster_id = COALESCE($3, poster_id), + signal_ids = COALESCE($4, signal_ids), + title_status = COALESCE($5, title_status), + release_year = COALESCE($6, release_year), + release_season = COALESCE($7, release_season), + episodes_aired = COALESCE($8, episodes_aired), + episodes_all = COALESCE($9, episodes_all), + episodes_len = COALESCE($10, episodes_len) +WHERE title_id = $11 +RETURNING title_id, title_names, studio_id, poster_id, signal_ids, title_status, rating, rating_count, release_year, release_season, season, episodes_aired, episodes_all, episodes_len +` + +type UpdateTitleParams struct { + TitleNames []byte `db:"title_names" json:"title_names"` + StudioID pgtype.Int4 `db:"studio_id" json:"studio_id"` + PosterID pgtype.Int4 `db:"poster_id" json:"poster_id"` + SignalIds []int32 `db:"signal_ids" json:"signal_ids"` + TitleStatus NullTitleStatusT `db:"title_status" json:"title_status"` + ReleaseYear pgtype.Int4 `db:"release_year" json:"release_year"` + ReleaseSeason NullReleaseSeasonT `db:"release_season" json:"release_season"` + EpisodesAired pgtype.Int4 `db:"episodes_aired" json:"episodes_aired"` + EpisodesAll pgtype.Int4 `db:"episodes_all" json:"episodes_all"` + EpisodesLen []byte `db:"episodes_len" json:"episodes_len"` + TitleID int32 `db:"title_id" json:"title_id"` +} + +func (q *Queries) UpdateTitle(ctx context.Context, arg UpdateTitleParams) (Titles, error) { + row := q.db.QueryRow(ctx, updateTitle, + arg.TitleNames, + arg.StudioID, + arg.PosterID, + arg.SignalIds, + arg.TitleStatus, + arg.ReleaseYear, + arg.ReleaseSeason, + arg.EpisodesAired, + arg.EpisodesAll, + arg.EpisodesLen, + arg.TitleID, + ) + var i Titles + err := row.Scan( + &i.TitleID, + &i.TitleNames, + &i.StudioID, + &i.PosterID, + &i.SignalIds, + &i.TitleStatus, + &i.Rating, + &i.RatingCount, + &i.ReleaseYear, + &i.ReleaseSeason, + &i.Season, + &i.EpisodesAired, + &i.EpisodesAll, + &i.EpisodesLen, + ) + return i, err +} + +const updateUser = `-- name: UpdateUser :one +UPDATE users +SET + avatar_id = COALESCE($1, avatar_id), + disp_name = COALESCE($2, disp_name), + user_desc = COALESCE($3, user_desc) +WHERE user_id = $4 +RETURNING user_id, avatar_id, nickname, disp_name, user_desc, creation_date +` + +type UpdateUserParams struct { + AvatarID pgtype.Int4 `db:"avatar_id" json:"avatar_id"` + DispName pgtype.Text `db:"disp_name" json:"disp_name"` + UserDesc pgtype.Text `db:"user_desc" json:"user_desc"` + UserID int32 `db:"user_id" json:"user_id"` +} + +type UpdateUserRow struct { + UserID int32 `db:"user_id" json:"user_id"` + AvatarID pgtype.Int4 `db:"avatar_id" json:"avatar_id"` + Nickname string `db:"nickname" json:"nickname"` + DispName pgtype.Text `db:"disp_name" json:"disp_name"` + UserDesc pgtype.Text `db:"user_desc" json:"user_desc"` + CreationDate pgtype.Timestamp `db:"creation_date" json:"creation_date"` +} + +func (q *Queries) UpdateUser(ctx context.Context, arg UpdateUserParams) (UpdateUserRow, error) { + row := q.db.QueryRow(ctx, updateUser, + arg.AvatarID, + arg.DispName, + arg.UserDesc, + arg.UserID, + ) + var i UpdateUserRow + err := row.Scan( + &i.UserID, + &i.AvatarID, + &i.Nickname, + &i.DispName, + &i.UserDesc, + &i.CreationDate, + ) + return i, err +} + +const updateUserTitle = `-- name: UpdateUserTitle :one +UPDATE usertitles +SET + status = COALESCE($3, status), + rate = COALESCE($4, rate), + review_id = COALESCE($5, review_id) +WHERE user_id = $1 AND title_id = $2 +RETURNING usertitle_id, user_id, title_id, status, rate, review_id +` + +type UpdateUserTitleParams struct { + UserID int32 `db:"user_id" json:"user_id"` + TitleID int32 `db:"title_id" json:"title_id"` + Status NullUsertitleStatusT `db:"status" json:"status"` + Rate pgtype.Int4 `db:"rate" json:"rate"` + ReviewID pgtype.Int4 `db:"review_id" json:"review_id"` +} + +func (q *Queries) UpdateUserTitle(ctx context.Context, arg UpdateUserTitleParams) (Usertitles, error) { + row := q.db.QueryRow(ctx, updateUserTitle, + arg.UserID, + arg.TitleID, + arg.Status, + arg.Rate, + arg.ReviewID, + ) + var i Usertitles + err := row.Scan( + &i.UsertitleID, + &i.UserID, + &i.TitleID, + &i.Status, + &i.Rate, + &i.ReviewID, + ) + return i, err +} diff --git a/sql/migrations/000001_init.down.sql b/sql/migrations/000001_init.down.sql deleted file mode 100644 index dc52d23..0000000 --- a/sql/migrations/000001_init.down.sql +++ /dev/null @@ -1,20 +0,0 @@ -DROP TRIGGER IF EXISTS trg_update_title_rating ON usertitles; -DROP TRIGGER IF EXISTS trg_notify_new_signal ON signals; - -DROP FUNCTION IF EXISTS update_title_rating(); -DROP FUNCTION IF EXISTS notify_new_signal(); - -DROP TABLE IF EXISTS signals; -DROP TABLE IF EXISTS title_tags; -DROP TABLE IF EXISTS usertitles; -DROP TABLE IF EXISTS titles; -DROP TABLE IF EXISTS studios; -DROP TABLE IF EXISTS users; -DROP TABLE IF EXISTS images; -DROP TABLE IF EXISTS tags; -DROP TABLE IF EXISTS providers; - -DROP TYPE IF EXISTS release_season_t; -DROP TYPE IF EXISTS title_status_t; -DROP TYPE IF EXISTS storage_type_t; -DROP TYPE IF EXISTS usertitle_status_t; \ No newline at end of file diff --git a/sql/migrations/000001_init.up.sql b/sql/migrations/000001_init.up.sql deleted file mode 100644 index 0b7fa33..0000000 --- a/sql/migrations/000001_init.up.sql +++ /dev/null @@ -1,141 +0,0 @@ --- TODO: --- maybe jsonb constraints --- clean unused images -CREATE TYPE usertitle_status_t AS ENUM ('finished', 'planned', 'dropped', 'in-progress'); -CREATE TYPE storage_type_t AS ENUM ('local', 's3'); -CREATE TYPE title_status_t AS ENUM ('finished', 'ongoing', 'planned'); -CREATE TYPE release_season_t AS ENUM ('winter', 'spring', 'summer', 'fall'); - -CREATE TABLE providers ( - id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - provider_name text NOT NULL, - credentials jsonb -); - -CREATE TABLE tags ( - id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - tag_names jsonb NOT NULL -); - -CREATE TABLE images ( - id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - storage_type storage_type_t NOT NULL, - image_path text UNIQUE NOT NULL -); - -CREATE TABLE users ( - id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - avatar_id bigint REFERENCES images (id), - passhash text NOT NULL, - mail text CHECK (mail ~ '[a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+'), - nickname text NOT NULL CHECK (nickname ~ '^[a-zA-Z0-9_-]+$'), - disp_name text, - user_desc text, - creation_date timestamptz NOT NULL, - last_login timestamptz -); - -CREATE TABLE studios ( - id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - studio_name text UNIQUE, - illust_id bigint REFERENCES images (id), - studio_desc text -); - -CREATE TABLE titles ( - id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - title_names jsonb NOT NULL, - studio_id bigint NOT NULL REFERENCES studios (id), - poster_id bigint REFERENCES images (id), - title_status title_status_t NOT NULL, - rating float CHECK (rating >= 0 AND rating <= 10), - rating_count int CHECK (rating_count >= 0), - release_year int CHECK (release_year >= 1900), - release_season release_season_t, - season int CHECK (season >= 0), - episodes_aired int CHECK (episodes_aired >= 0), - episodes_all int CHECK (episodes_all >= 0), - episodes_len jsonb, - CHECK ((episodes_aired IS NULL AND episodes_all IS NULL) - OR (episodes_aired IS NOT NULL AND episodes_all IS NOT NULL - AND episodes_aired <= episodes_all)) -); - -CREATE TABLE usertitles ( - PRIMARY KEY (user_id, title_id), - user_id bigint NOT NULL REFERENCES users (id), - title_id bigint NOT NULL REFERENCES titles (id), - status usertitle_status_t NOT NULL, - rate int CHECK (rate > 0 AND rate <= 10), - review_text text, - review_date timestamptz -); - -CREATE TABLE title_tags ( - PRIMARY KEY (title_id, tag_id), - title_id bigint NOT NULL REFERENCES titles (id), - tag_id bigint NOT NULL REFERENCES tags (id) -); - -CREATE TABLE signals ( - id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - title_id bigint REFERENCES titles (id), - raw_data jsonb NOT NULL, - provider_id bigint NOT NULL REFERENCES providers (id), - pending boolean NOT NULL -); - --- Functions -CREATE OR REPLACE FUNCTION update_title_rating() -RETURNS TRIGGER AS $$ -BEGIN - IF (TG_OP = 'INSERT') OR (TG_OP = 'UPDATE' AND NEW.rate IS DISTINCT FROM OLD.rate) THEN - UPDATE titles - SET - rating = sub.avg_rating, - rating_count = sub.rating_count - FROM ( - SELECT - title_id, - AVG(rate)::float AS avg_rating, - COUNT(rate) AS rating_count - FROM usertitles - WHERE title_id = NEW.title_id AND rate IS NOT NULL - GROUP BY title_id - ) AS sub - WHERE titles.id = sub.title_id; - END IF; - - RETURN NEW; -END; -$$ LANGUAGE plpgsql; - -CREATE OR REPLACE FUNCTION notify_new_signal() -RETURNS TRIGGER AS $$ -DECLARE - payload JSON; -BEGIN - payload := json_build_object( - 'signal_id', NEW.id, - 'title_id', NEW.title_id, - 'provider_id', NEW.provider_id, - 'pending', NEW.pending, - 'timestamp', NOW() - ); - PERFORM pg_notify('new_signal', payload::text); - - RETURN NEW; -END; -$$ LANGUAGE plpgsql; - --- Triggers - -CREATE TRIGGER trg_update_title_rating -AFTER INSERT OR UPDATE OF rate ON usertitles -FOR EACH ROW -EXECUTE FUNCTION update_title_rating(); - -CREATE TRIGGER trg_notify_new_signal -AFTER INSERT ON signals -FOR EACH ROW -EXECUTE FUNCTION notify_new_signal(); \ No newline at end of file diff --git a/sql/queries.sql.go b/sql/queries.sql.go deleted file mode 100644 index 8f92c2a..0000000 --- a/sql/queries.sql.go +++ /dev/null @@ -1,73 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.30.0 -// source: queries.sql - -package sqlc - -import ( - "context" - "time" -) - -const createImage = `-- name: CreateImage :one -INSERT INTO images (storage_type, image_path) -VALUES ($1, $2) -RETURNING id, storage_type, image_path -` - -type CreateImageParams struct { - StorageType StorageTypeT `json:"storage_type"` - ImagePath string `json:"image_path"` -} - -func (q *Queries) CreateImage(ctx context.Context, arg CreateImageParams) (Image, error) { - row := q.db.QueryRow(ctx, createImage, arg.StorageType, arg.ImagePath) - var i Image - err := row.Scan(&i.ID, &i.StorageType, &i.ImagePath) - return i, err -} - -const getImageByID = `-- name: GetImageByID :one -SELECT id, storage_type, image_path -FROM images -WHERE id = $1 -` - -func (q *Queries) GetImageByID(ctx context.Context, id int64) (Image, error) { - row := q.db.QueryRow(ctx, getImageByID, id) - var i Image - err := row.Scan(&i.ID, &i.StorageType, &i.ImagePath) - return i, err -} - -const getUserByID = `-- name: GetUserByID :one -SELECT id, avatar_id, mail, nickname, disp_name, user_desc, creation_date -FROM users -WHERE id = $1 -` - -type GetUserByIDRow struct { - ID int64 `json:"id"` - AvatarID *int64 `json:"avatar_id"` - Mail *string `json:"mail"` - Nickname string `json:"nickname"` - DispName *string `json:"disp_name"` - UserDesc *string `json:"user_desc"` - CreationDate time.Time `json:"creation_date"` -} - -func (q *Queries) GetUserByID(ctx context.Context, id int64) (GetUserByIDRow, error) { - row := q.db.QueryRow(ctx, getUserByID, id) - var i GetUserByIDRow - err := row.Scan( - &i.ID, - &i.AvatarID, - &i.Mail, - &i.Nickname, - &i.DispName, - &i.UserDesc, - &i.CreationDate, - ) - return i, err -} diff --git a/sql/sqlc.yaml b/sql/sqlc.yaml deleted file mode 100644 index f44761e..0000000 --- a/sql/sqlc.yaml +++ /dev/null @@ -1,27 +0,0 @@ -version: "2" -sql: - - engine: "postgresql" - queries: - - "../modules/backend/queries.sql" - schema: "migrations" - gen: - go: - package: "sqlc" - out: "." - sql_package: "pgx/v5" - sql_driver: "github.com/jackc/pgx/v5" - emit_json_tags: true - emit_pointers_for_null_types: true - overrides: - - db_type: "uuid" - nullable: false - go_type: - import: "github.com/gofrs/uuid" - package: "gofrsuuid" - type: UUID - pointer: true - - db_type: "timestamptz" - nullable: false - go_type: - import: "time" - type: "Time" \ No newline at end of file