Merge branch 'dev' of ssh://meowgit.nekoea.red:22222/nihonium/nyanimedb into dev
This commit is contained in:
commit
b0a8f4a02e
16 changed files with 210 additions and 86 deletions
|
|
@ -150,6 +150,8 @@ paths:
|
|||
description: User not found
|
||||
'500':
|
||||
description: Unknown server error
|
||||
security:
|
||||
- JwtAuthCookies: []
|
||||
patch:
|
||||
operationId: updateUser
|
||||
summary: Partially update a user account
|
||||
|
|
@ -158,8 +160,7 @@ paths:
|
|||
Password updates must be done via the dedicated auth-service (`/auth/`).
|
||||
Fields not provided in the request body remain unchanged.
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/accessToken'
|
||||
- $ref: '#/components/parameters/csrfToken'
|
||||
- $ref: '#/components/parameters/csrfTokenHeader'
|
||||
- name: user_id
|
||||
in: path
|
||||
description: User ID (primary key)
|
||||
|
|
@ -404,11 +405,14 @@ paths:
|
|||
description: User or title not found
|
||||
'500':
|
||||
description: Unknown server error
|
||||
security:
|
||||
- JwtAuthCookies: []
|
||||
patch:
|
||||
operationId: updateUserTitle
|
||||
summary: Update a usertitle
|
||||
description: User updating title list of watched
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/csrfTokenHeader'
|
||||
- name: user_id
|
||||
in: path
|
||||
required: true
|
||||
|
|
@ -450,11 +454,14 @@ paths:
|
|||
description: User or Title not found
|
||||
'500':
|
||||
description: Internal server error
|
||||
security:
|
||||
- JwtAuthCookies: []
|
||||
delete:
|
||||
operationId: deleteUserTitle
|
||||
summary: Delete a usertitle
|
||||
description: User deleting title from list of watched
|
||||
parameters:
|
||||
- $ref: '#/components/parameters/csrfTokenHeader'
|
||||
- name: user_id
|
||||
in: path
|
||||
required: true
|
||||
|
|
@ -478,6 +485,8 @@ paths:
|
|||
description: User or Title not found
|
||||
'500':
|
||||
description: Internal server error
|
||||
security:
|
||||
- JwtAuthCookies: []
|
||||
components:
|
||||
parameters:
|
||||
accessToken:
|
||||
|
|
@ -491,7 +500,7 @@ components:
|
|||
description: |
|
||||
JWT access token.
|
||||
csrfToken:
|
||||
name: XSRF-TOKEN
|
||||
name: xsrf_token
|
||||
in: cookie
|
||||
required: true
|
||||
schema:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
get:
|
||||
summary: Get user title
|
||||
operationId: getUserTitle
|
||||
security:
|
||||
- JwtAuthCookies: []
|
||||
parameters:
|
||||
- in: path
|
||||
name: user_id
|
||||
|
|
@ -34,7 +36,10 @@ patch:
|
|||
summary: Update a usertitle
|
||||
description: User updating title list of watched
|
||||
operationId: updateUserTitle
|
||||
security:
|
||||
- JwtAuthCookies: []
|
||||
parameters:
|
||||
- $ref: '../parameters/xsrf_token_header.yaml'
|
||||
- in: path
|
||||
name: user_id
|
||||
required: true
|
||||
|
|
@ -81,7 +86,10 @@ delete:
|
|||
summary: Delete a usertitle
|
||||
description: User deleting title from list of watched
|
||||
operationId: deleteUserTitle
|
||||
security:
|
||||
- JwtAuthCookies: []
|
||||
parameters:
|
||||
- $ref: '../parameters/xsrf_token_header.yaml'
|
||||
- in: path
|
||||
name: user_id
|
||||
required: true
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
get:
|
||||
summary: Get user info
|
||||
operationId: getUsersId
|
||||
security:
|
||||
- JwtAuthCookies: []
|
||||
parameters:
|
||||
- in: path
|
||||
name: user_id
|
||||
|
|
@ -36,8 +38,7 @@ patch:
|
|||
Fields not provided in the request body remain unchanged.
|
||||
operationId: updateUser
|
||||
parameters:
|
||||
- $ref: '../parameters/access_token.yaml' # ← для поля в UI и GoDoc
|
||||
- $ref: '../parameters/xsrf_token_cookie.yaml' # ← для CSRF
|
||||
- $ref: '../parameters/xsrf_token_header.yaml'
|
||||
- name: user_id
|
||||
in: path
|
||||
required: true
|
||||
|
|
|
|||
108
auth/auth.gen.go
108
auth/auth.gen.go
|
|
@ -13,32 +13,32 @@ import (
|
|||
strictgin "github.com/oapi-codegen/runtime/strictmiddleware/gin"
|
||||
)
|
||||
|
||||
// PostAuthSignInJSONBody defines parameters for PostAuthSignIn.
|
||||
type PostAuthSignInJSONBody struct {
|
||||
// PostSignInJSONBody defines parameters for PostSignIn.
|
||||
type PostSignInJSONBody struct {
|
||||
Nickname string `json:"nickname"`
|
||||
Pass string `json:"pass"`
|
||||
}
|
||||
|
||||
// PostAuthSignUpJSONBody defines parameters for PostAuthSignUp.
|
||||
type PostAuthSignUpJSONBody struct {
|
||||
// PostSignUpJSONBody defines parameters for PostSignUp.
|
||||
type PostSignUpJSONBody struct {
|
||||
Nickname string `json:"nickname"`
|
||||
Pass string `json:"pass"`
|
||||
}
|
||||
|
||||
// PostAuthSignInJSONRequestBody defines body for PostAuthSignIn for application/json ContentType.
|
||||
type PostAuthSignInJSONRequestBody PostAuthSignInJSONBody
|
||||
// PostSignInJSONRequestBody defines body for PostSignIn for application/json ContentType.
|
||||
type PostSignInJSONRequestBody PostSignInJSONBody
|
||||
|
||||
// PostAuthSignUpJSONRequestBody defines body for PostAuthSignUp for application/json ContentType.
|
||||
type PostAuthSignUpJSONRequestBody PostAuthSignUpJSONBody
|
||||
// PostSignUpJSONRequestBody defines body for PostSignUp for application/json ContentType.
|
||||
type PostSignUpJSONRequestBody PostSignUpJSONBody
|
||||
|
||||
// ServerInterface represents all server handlers.
|
||||
type ServerInterface interface {
|
||||
// Sign in a user and return JWT
|
||||
// (POST /auth/sign-in)
|
||||
PostAuthSignIn(c *gin.Context)
|
||||
// (POST /sign-in)
|
||||
PostSignIn(c *gin.Context)
|
||||
// Sign up a new user
|
||||
// (POST /auth/sign-up)
|
||||
PostAuthSignUp(c *gin.Context)
|
||||
// (POST /sign-up)
|
||||
PostSignUp(c *gin.Context)
|
||||
}
|
||||
|
||||
// ServerInterfaceWrapper converts contexts to parameters.
|
||||
|
|
@ -50,8 +50,8 @@ type ServerInterfaceWrapper struct {
|
|||
|
||||
type MiddlewareFunc func(c *gin.Context)
|
||||
|
||||
// PostAuthSignIn operation middleware
|
||||
func (siw *ServerInterfaceWrapper) PostAuthSignIn(c *gin.Context) {
|
||||
// PostSignIn operation middleware
|
||||
func (siw *ServerInterfaceWrapper) PostSignIn(c *gin.Context) {
|
||||
|
||||
for _, middleware := range siw.HandlerMiddlewares {
|
||||
middleware(c)
|
||||
|
|
@ -60,11 +60,11 @@ func (siw *ServerInterfaceWrapper) PostAuthSignIn(c *gin.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
siw.Handler.PostAuthSignIn(c)
|
||||
siw.Handler.PostSignIn(c)
|
||||
}
|
||||
|
||||
// PostAuthSignUp operation middleware
|
||||
func (siw *ServerInterfaceWrapper) PostAuthSignUp(c *gin.Context) {
|
||||
// PostSignUp operation middleware
|
||||
func (siw *ServerInterfaceWrapper) PostSignUp(c *gin.Context) {
|
||||
|
||||
for _, middleware := range siw.HandlerMiddlewares {
|
||||
middleware(c)
|
||||
|
|
@ -73,7 +73,7 @@ func (siw *ServerInterfaceWrapper) PostAuthSignUp(c *gin.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
siw.Handler.PostAuthSignUp(c)
|
||||
siw.Handler.PostSignUp(c)
|
||||
}
|
||||
|
||||
// GinServerOptions provides options for the Gin server.
|
||||
|
|
@ -103,54 +103,54 @@ func RegisterHandlersWithOptions(router gin.IRouter, si ServerInterface, options
|
|||
ErrorHandler: errorHandler,
|
||||
}
|
||||
|
||||
router.POST(options.BaseURL+"/auth/sign-in", wrapper.PostAuthSignIn)
|
||||
router.POST(options.BaseURL+"/auth/sign-up", wrapper.PostAuthSignUp)
|
||||
router.POST(options.BaseURL+"/sign-in", wrapper.PostSignIn)
|
||||
router.POST(options.BaseURL+"/sign-up", wrapper.PostSignUp)
|
||||
}
|
||||
|
||||
type PostAuthSignInRequestObject struct {
|
||||
Body *PostAuthSignInJSONRequestBody
|
||||
type PostSignInRequestObject struct {
|
||||
Body *PostSignInJSONRequestBody
|
||||
}
|
||||
|
||||
type PostAuthSignInResponseObject interface {
|
||||
VisitPostAuthSignInResponse(w http.ResponseWriter) error
|
||||
type PostSignInResponseObject interface {
|
||||
VisitPostSignInResponse(w http.ResponseWriter) error
|
||||
}
|
||||
|
||||
type PostAuthSignIn200JSONResponse struct {
|
||||
type PostSignIn200JSONResponse struct {
|
||||
UserId int64 `json:"user_id"`
|
||||
UserName string `json:"user_name"`
|
||||
}
|
||||
|
||||
func (response PostAuthSignIn200JSONResponse) VisitPostAuthSignInResponse(w http.ResponseWriter) error {
|
||||
func (response PostSignIn200JSONResponse) VisitPostSignInResponse(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(200)
|
||||
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type PostAuthSignIn401JSONResponse struct {
|
||||
type PostSignIn401JSONResponse struct {
|
||||
Error *string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
func (response PostAuthSignIn401JSONResponse) VisitPostAuthSignInResponse(w http.ResponseWriter) error {
|
||||
func (response PostSignIn401JSONResponse) VisitPostSignInResponse(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(401)
|
||||
|
||||
return json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
|
||||
type PostAuthSignUpRequestObject struct {
|
||||
Body *PostAuthSignUpJSONRequestBody
|
||||
type PostSignUpRequestObject struct {
|
||||
Body *PostSignUpJSONRequestBody
|
||||
}
|
||||
|
||||
type PostAuthSignUpResponseObject interface {
|
||||
VisitPostAuthSignUpResponse(w http.ResponseWriter) error
|
||||
type PostSignUpResponseObject interface {
|
||||
VisitPostSignUpResponse(w http.ResponseWriter) error
|
||||
}
|
||||
|
||||
type PostAuthSignUp200JSONResponse struct {
|
||||
type PostSignUp200JSONResponse struct {
|
||||
UserId int64 `json:"user_id"`
|
||||
}
|
||||
|
||||
func (response PostAuthSignUp200JSONResponse) VisitPostAuthSignUpResponse(w http.ResponseWriter) error {
|
||||
func (response PostSignUp200JSONResponse) VisitPostSignUpResponse(w http.ResponseWriter) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(200)
|
||||
|
||||
|
|
@ -160,11 +160,11 @@ func (response PostAuthSignUp200JSONResponse) VisitPostAuthSignUpResponse(w http
|
|||
// StrictServerInterface represents all server handlers.
|
||||
type StrictServerInterface interface {
|
||||
// Sign in a user and return JWT
|
||||
// (POST /auth/sign-in)
|
||||
PostAuthSignIn(ctx context.Context, request PostAuthSignInRequestObject) (PostAuthSignInResponseObject, error)
|
||||
// (POST /sign-in)
|
||||
PostSignIn(ctx context.Context, request PostSignInRequestObject) (PostSignInResponseObject, error)
|
||||
// Sign up a new user
|
||||
// (POST /auth/sign-up)
|
||||
PostAuthSignUp(ctx context.Context, request PostAuthSignUpRequestObject) (PostAuthSignUpResponseObject, error)
|
||||
// (POST /sign-up)
|
||||
PostSignUp(ctx context.Context, request PostSignUpRequestObject) (PostSignUpResponseObject, error)
|
||||
}
|
||||
|
||||
type StrictHandlerFunc = strictgin.StrictGinHandlerFunc
|
||||
|
|
@ -179,11 +179,11 @@ type strictHandler struct {
|
|||
middlewares []StrictMiddlewareFunc
|
||||
}
|
||||
|
||||
// PostAuthSignIn operation middleware
|
||||
func (sh *strictHandler) PostAuthSignIn(ctx *gin.Context) {
|
||||
var request PostAuthSignInRequestObject
|
||||
// PostSignIn operation middleware
|
||||
func (sh *strictHandler) PostSignIn(ctx *gin.Context) {
|
||||
var request PostSignInRequestObject
|
||||
|
||||
var body PostAuthSignInJSONRequestBody
|
||||
var body PostSignInJSONRequestBody
|
||||
if err := ctx.ShouldBindJSON(&body); err != nil {
|
||||
ctx.Status(http.StatusBadRequest)
|
||||
ctx.Error(err)
|
||||
|
|
@ -192,10 +192,10 @@ func (sh *strictHandler) PostAuthSignIn(ctx *gin.Context) {
|
|||
request.Body = &body
|
||||
|
||||
handler := func(ctx *gin.Context, request interface{}) (interface{}, error) {
|
||||
return sh.ssi.PostAuthSignIn(ctx, request.(PostAuthSignInRequestObject))
|
||||
return sh.ssi.PostSignIn(ctx, request.(PostSignInRequestObject))
|
||||
}
|
||||
for _, middleware := range sh.middlewares {
|
||||
handler = middleware(handler, "PostAuthSignIn")
|
||||
handler = middleware(handler, "PostSignIn")
|
||||
}
|
||||
|
||||
response, err := handler(ctx, request)
|
||||
|
|
@ -203,8 +203,8 @@ func (sh *strictHandler) PostAuthSignIn(ctx *gin.Context) {
|
|||
if err != nil {
|
||||
ctx.Error(err)
|
||||
ctx.Status(http.StatusInternalServerError)
|
||||
} else if validResponse, ok := response.(PostAuthSignInResponseObject); ok {
|
||||
if err := validResponse.VisitPostAuthSignInResponse(ctx.Writer); err != nil {
|
||||
} else if validResponse, ok := response.(PostSignInResponseObject); ok {
|
||||
if err := validResponse.VisitPostSignInResponse(ctx.Writer); err != nil {
|
||||
ctx.Error(err)
|
||||
}
|
||||
} else if response != nil {
|
||||
|
|
@ -212,11 +212,11 @@ func (sh *strictHandler) PostAuthSignIn(ctx *gin.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
// PostAuthSignUp operation middleware
|
||||
func (sh *strictHandler) PostAuthSignUp(ctx *gin.Context) {
|
||||
var request PostAuthSignUpRequestObject
|
||||
// PostSignUp operation middleware
|
||||
func (sh *strictHandler) PostSignUp(ctx *gin.Context) {
|
||||
var request PostSignUpRequestObject
|
||||
|
||||
var body PostAuthSignUpJSONRequestBody
|
||||
var body PostSignUpJSONRequestBody
|
||||
if err := ctx.ShouldBindJSON(&body); err != nil {
|
||||
ctx.Status(http.StatusBadRequest)
|
||||
ctx.Error(err)
|
||||
|
|
@ -225,10 +225,10 @@ func (sh *strictHandler) PostAuthSignUp(ctx *gin.Context) {
|
|||
request.Body = &body
|
||||
|
||||
handler := func(ctx *gin.Context, request interface{}) (interface{}, error) {
|
||||
return sh.ssi.PostAuthSignUp(ctx, request.(PostAuthSignUpRequestObject))
|
||||
return sh.ssi.PostSignUp(ctx, request.(PostSignUpRequestObject))
|
||||
}
|
||||
for _, middleware := range sh.middlewares {
|
||||
handler = middleware(handler, "PostAuthSignUp")
|
||||
handler = middleware(handler, "PostSignUp")
|
||||
}
|
||||
|
||||
response, err := handler(ctx, request)
|
||||
|
|
@ -236,8 +236,8 @@ func (sh *strictHandler) PostAuthSignUp(ctx *gin.Context) {
|
|||
if err != nil {
|
||||
ctx.Error(err)
|
||||
ctx.Status(http.StatusInternalServerError)
|
||||
} else if validResponse, ok := response.(PostAuthSignUpResponseObject); ok {
|
||||
if err := validResponse.VisitPostAuthSignUpResponse(ctx.Writer); err != nil {
|
||||
} else if validResponse, ok := response.(PostSignUpResponseObject); ok {
|
||||
if err := validResponse.VisitPostSignUpResponse(ctx.Writer); err != nil {
|
||||
ctx.Error(err)
|
||||
}
|
||||
} else if response != nil {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ servers:
|
|||
- url: /auth
|
||||
|
||||
paths:
|
||||
/auth/sign-up:
|
||||
/sign-up:
|
||||
post:
|
||||
summary: Sign up a new user
|
||||
tags: [Auth]
|
||||
|
|
@ -38,7 +38,7 @@ paths:
|
|||
type: integer
|
||||
format: int64
|
||||
|
||||
/auth/sign-in:
|
||||
/sign-in:
|
||||
post:
|
||||
summary: Sign in a user and return JWT
|
||||
tags: [Auth]
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ func (s Server) generateTokens(userID string) (accessToken string, refreshToken
|
|||
return accessToken, refreshToken, csrfToken, nil
|
||||
}
|
||||
|
||||
func (s Server) PostAuthSignUp(ctx context.Context, req auth.PostAuthSignUpRequestObject) (auth.PostAuthSignUpResponseObject, error) {
|
||||
func (s Server) PostSignUp(ctx context.Context, req auth.PostSignUpRequestObject) (auth.PostSignUpResponseObject, error) {
|
||||
passhash, err := HashPassword(req.Body.Pass)
|
||||
if err != nil {
|
||||
log.Errorf("failed to hash password: %v", err)
|
||||
|
|
@ -94,17 +94,17 @@ func (s Server) PostAuthSignUp(ctx context.Context, req auth.PostAuthSignUpReque
|
|||
// TODO: check err and retyrn 400/500
|
||||
}
|
||||
|
||||
return auth.PostAuthSignUp200JSONResponse{
|
||||
return auth.PostSignUp200JSONResponse{
|
||||
UserId: user_id,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s Server) PostAuthSignIn(ctx context.Context, req auth.PostAuthSignInRequestObject) (auth.PostAuthSignInResponseObject, error) {
|
||||
func (s Server) PostSignIn(ctx context.Context, req auth.PostSignInRequestObject) (auth.PostSignInResponseObject, error) {
|
||||
ginCtx, ok := ctx.Value(gin.ContextKey).(*gin.Context)
|
||||
if !ok {
|
||||
log.Print("failed to get gin context")
|
||||
// TODO: change to 500
|
||||
return auth.PostAuthSignIn200JSONResponse{}, fmt.Errorf("failed to get gin.Context from context.Context")
|
||||
return auth.PostSignIn200JSONResponse{}, fmt.Errorf("failed to get gin.Context from context.Context")
|
||||
}
|
||||
|
||||
user, err := s.db.GetUserByNickname(context.Background(), req.Body.Nickname)
|
||||
|
|
@ -120,7 +120,7 @@ func (s Server) PostAuthSignIn(ctx context.Context, req auth.PostAuthSignInReque
|
|||
}
|
||||
if !ok {
|
||||
err_msg := "invalid credentials"
|
||||
return auth.PostAuthSignIn401JSONResponse{
|
||||
return auth.PostSignIn401JSONResponse{
|
||||
Error: &err_msg,
|
||||
}, nil
|
||||
}
|
||||
|
|
@ -137,7 +137,7 @@ func (s Server) PostAuthSignIn(ctx context.Context, req auth.PostAuthSignInReque
|
|||
ginCtx.SetCookie("refresh_token", refreshToken, 1209600, "/auth", "", false, true)
|
||||
ginCtx.SetCookie("xsrf_token", csrfToken, 1209600, "/api", "", false, false)
|
||||
|
||||
result := auth.PostAuthSignIn200JSONResponse{
|
||||
result := auth.PostSignIn200JSONResponse{
|
||||
UserId: user.ID,
|
||||
UserName: user.Nickname,
|
||||
}
|
||||
|
|
|
|||
53
modules/frontend/package-lock.json
generated
53
modules/frontend/package-lock.json
generated
|
|
@ -13,6 +13,7 @@
|
|||
"@tailwindcss/vite": "^4.1.17",
|
||||
"axios": "^1.12.2",
|
||||
"react": "^19.1.1",
|
||||
"react-cookie": "^8.0.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"react-router-dom": "^7.9.4",
|
||||
"tailwindcss": "^4.1.17"
|
||||
|
|
@ -1868,6 +1869,18 @@
|
|||
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/hoist-non-react-statics": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.7.tgz",
|
||||
"integrity": "sha512-PQTyIulDkIDro8P+IHbKCsw7U2xxBYflVzW/FgWdCAePD9xGSidgA76/GeJ6lBKoblyhf9pBY763gbrN+1dI8g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"hoist-non-react-statics": "^3.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/json-schema": {
|
||||
"version": "7.0.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
|
||||
|
|
@ -1890,7 +1903,6 @@
|
|||
"version": "19.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz",
|
||||
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
|
|
@ -2524,7 +2536,6 @@
|
|||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/debug": {
|
||||
|
|
@ -3260,6 +3271,15 @@
|
|||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/hoist-non-react-statics": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
|
||||
"integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"react-is": "^16.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
|
||||
|
|
@ -4068,6 +4088,20 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-cookie": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-8.0.1.tgz",
|
||||
"integrity": "sha512-QNdAd0MLuAiDiLcDU/2s/eyKmmfMHtjPUKJ2dZ/5CcQ9QKUium4B3o61/haq6PQl/YWFqC5PO8GvxeHKhy3GFA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/hoist-non-react-statics": "^3.3.6",
|
||||
"hoist-non-react-statics": "^3.3.2",
|
||||
"universal-cookie": "^8.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">= 16.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "19.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
|
||||
|
|
@ -4081,6 +4115,12 @@
|
|||
"react": "^19.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react-refresh": {
|
||||
"version": "0.17.0",
|
||||
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
|
||||
|
|
@ -4481,6 +4521,15 @@
|
|||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/universal-cookie": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-8.0.1.tgz",
|
||||
"integrity": "sha512-B6ks9FLLnP1UbPPcveOidfvB9pHjP+wekP2uRYB9YDfKVpvcjKgy1W5Zj+cEXJ9KTPnqOKGfVDQBmn8/YCQfRg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cookie": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/universalify": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
"@tailwindcss/vite": "^4.1.17",
|
||||
"axios": "^1.12.2",
|
||||
"react": "^19.1.1",
|
||||
"react-cookie": "^8.0.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"react-router-dom": "^7.9.4",
|
||||
"tailwindcss": "^4.1.17"
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ export { CancelablePromise, CancelError } from './core/CancelablePromise';
|
|||
export { OpenAPI } from './core/OpenAPI';
|
||||
export type { OpenAPIConfig } from './core/OpenAPI';
|
||||
|
||||
export type { accessToken } from './models/accessToken';
|
||||
export type { csrfToken } from './models/csrfToken';
|
||||
export type { csrfTokenHeader } from './models/csrfTokenHeader';
|
||||
export type { cursor } from './models/cursor';
|
||||
export type { CursorObj } from './models/CursorObj';
|
||||
export type { Image } from './models/Image';
|
||||
|
|
|
|||
9
modules/frontend/src/api/models/accessToken.ts
Normal file
9
modules/frontend/src/api/models/accessToken.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* JWT access token.
|
||||
*
|
||||
*/
|
||||
export type accessToken = string;
|
||||
11
modules/frontend/src/api/models/csrfToken.ts
Normal file
11
modules/frontend/src/api/models/csrfToken.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Anti-CSRF token (Double Submit Cookie pattern).
|
||||
* Stored in non-HttpOnly cookie, readable by JavaScript.
|
||||
* Must be echoed in `X-XSRF-TOKEN` header for state-changing requests (POST/PUT/PATCH/DELETE).
|
||||
*
|
||||
*/
|
||||
export type csrfToken = string;
|
||||
10
modules/frontend/src/api/models/csrfTokenHeader.ts
Normal file
10
modules/frontend/src/api/models/csrfTokenHeader.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
/* generated using openapi-typescript-codegen -- do not edit */
|
||||
/* istanbul ignore file */
|
||||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* Anti-CSRF token. Must match the `XSRF-TOKEN` cookie.
|
||||
* Required for all state-changing requests (POST/PUT/PATCH/DELETE).
|
||||
*
|
||||
*/
|
||||
export type csrfTokenHeader = string;
|
||||
|
|
@ -135,12 +135,16 @@ export class DefaultService {
|
|||
* Password updates must be done via the dedicated auth-service (`/auth/`).
|
||||
* Fields not provided in the request body remain unchanged.
|
||||
*
|
||||
* @param xXsrfToken Anti-CSRF token. Must match the `XSRF-TOKEN` cookie.
|
||||
* Required for all state-changing requests (POST/PUT/PATCH/DELETE).
|
||||
*
|
||||
* @param userId User ID (primary key)
|
||||
* @param requestBody
|
||||
* @returns User User updated successfully. Returns updated user representation (excluding sensitive fields).
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static updateUser(
|
||||
xXsrfToken: string,
|
||||
userId: number,
|
||||
requestBody: {
|
||||
/**
|
||||
|
|
@ -171,6 +175,9 @@ export class DefaultService {
|
|||
path: {
|
||||
'user_id': userId,
|
||||
},
|
||||
headers: {
|
||||
'X-XSRF-TOKEN': xXsrfToken,
|
||||
},
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
errors: {
|
||||
|
|
@ -309,6 +316,9 @@ export class DefaultService {
|
|||
/**
|
||||
* Update a usertitle
|
||||
* User updating title list of watched
|
||||
* @param xXsrfToken Anti-CSRF token. Must match the `XSRF-TOKEN` cookie.
|
||||
* Required for all state-changing requests (POST/PUT/PATCH/DELETE).
|
||||
*
|
||||
* @param userId
|
||||
* @param titleId
|
||||
* @param requestBody
|
||||
|
|
@ -316,6 +326,7 @@ export class DefaultService {
|
|||
* @throws ApiError
|
||||
*/
|
||||
public static updateUserTitle(
|
||||
xXsrfToken: string,
|
||||
userId: number,
|
||||
titleId: number,
|
||||
requestBody: {
|
||||
|
|
@ -330,6 +341,9 @@ export class DefaultService {
|
|||
'user_id': userId,
|
||||
'title_id': titleId,
|
||||
},
|
||||
headers: {
|
||||
'X-XSRF-TOKEN': xXsrfToken,
|
||||
},
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
errors: {
|
||||
|
|
@ -344,12 +358,16 @@ export class DefaultService {
|
|||
/**
|
||||
* Delete a usertitle
|
||||
* User deleting title from list of watched
|
||||
* @param xXsrfToken Anti-CSRF token. Must match the `XSRF-TOKEN` cookie.
|
||||
* Required for all state-changing requests (POST/PUT/PATCH/DELETE).
|
||||
*
|
||||
* @param userId
|
||||
* @param titleId
|
||||
* @returns any Title successfully deleted
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static deleteUserTitle(
|
||||
xXsrfToken: string,
|
||||
userId: number,
|
||||
titleId: number,
|
||||
): CancelablePromise<any> {
|
||||
|
|
@ -360,6 +378,9 @@ export class DefaultService {
|
|||
'user_id': userId,
|
||||
'title_id': titleId,
|
||||
},
|
||||
headers: {
|
||||
'X-XSRF-TOKEN': xXsrfToken,
|
||||
},
|
||||
errors: {
|
||||
401: `Unauthorized — missing or invalid auth token`,
|
||||
403: `Forbidden — user not allowed to delete title`,
|
||||
|
|
|
|||
|
|
@ -12,19 +12,17 @@ export class AuthService {
|
|||
* @returns any Sign-up result
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static postAuthSignUp(
|
||||
public static postSignUp(
|
||||
requestBody: {
|
||||
nickname: string;
|
||||
pass: string;
|
||||
},
|
||||
): CancelablePromise<{
|
||||
success?: boolean;
|
||||
error?: string | null;
|
||||
user_id?: string | null;
|
||||
user_id: number;
|
||||
}> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'POST',
|
||||
url: '/auth/sign-up',
|
||||
url: '/sign-up',
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
});
|
||||
|
|
@ -35,19 +33,18 @@ export class AuthService {
|
|||
* @returns any Sign-in result with JWT
|
||||
* @throws ApiError
|
||||
*/
|
||||
public static postAuthSignIn(
|
||||
public static postSignIn(
|
||||
requestBody: {
|
||||
nickname: string;
|
||||
pass: string;
|
||||
},
|
||||
): CancelablePromise<{
|
||||
error?: string | null;
|
||||
user_id?: string | null;
|
||||
user_name?: string | null;
|
||||
user_id: number;
|
||||
user_name: string;
|
||||
}> {
|
||||
return __request(OpenAPI, {
|
||||
method: 'POST',
|
||||
url: '/auth/sign-in',
|
||||
url: '/sign-in',
|
||||
body: requestBody,
|
||||
mediaType: 'application/json',
|
||||
errors: {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { DefaultService } from "../../api";
|
||||
import type { UserTitleStatus } from "../../api";
|
||||
import { useCookies } from 'react-cookie';
|
||||
|
||||
import {
|
||||
ClockIcon,
|
||||
CheckCircleIcon,
|
||||
|
|
@ -17,6 +19,9 @@ const STATUS_BUTTONS: { status: UserTitleStatus; icon: React.ReactNode; label: s
|
|||
];
|
||||
|
||||
export function TitleStatusControls({ titleId }: { titleId: number }) {
|
||||
const [cookies] = useCookies(['xsrf_token']);
|
||||
const xsrfToken = cookies['xsrf_token'] || null;
|
||||
|
||||
const [currentStatus, setCurrentStatus] = useState<UserTitleStatus | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
|
|
@ -41,7 +46,7 @@ export function TitleStatusControls({ titleId }: { titleId: number }) {
|
|||
try {
|
||||
// 1) Если кликнули на текущий статус — DELETE
|
||||
if (currentStatus === status) {
|
||||
await DefaultService.deleteUserTitle(userId, titleId);
|
||||
await DefaultService.deleteUserTitle(xsrfToken, userId, titleId);
|
||||
setCurrentStatus(null);
|
||||
return;
|
||||
}
|
||||
|
|
@ -56,7 +61,7 @@ export function TitleStatusControls({ titleId }: { titleId: number }) {
|
|||
setCurrentStatus(added.status);
|
||||
} else {
|
||||
// уже есть запись — PATCH
|
||||
const updated = await DefaultService.updateUserTitle(userId, titleId, { status });
|
||||
const updated = await DefaultService.updateUserTitle(xsrfToken, userId, titleId, { status });
|
||||
setCurrentStatus(updated.status);
|
||||
}
|
||||
} finally {
|
||||
|
|
|
|||
|
|
@ -17,23 +17,23 @@ export const LoginPage: React.FC = () => {
|
|||
|
||||
try {
|
||||
if (isLogin) {
|
||||
const res = await AuthService.postAuthSignIn({ nickname, pass: password });
|
||||
const res = await AuthService.postSignIn({ nickname, pass: password });
|
||||
if (res.user_id && res.user_name) {
|
||||
// Сохраняем user_id и username в localStorage
|
||||
localStorage.setItem("userId", res.user_id);
|
||||
localStorage.setItem("userId", res.user_id.toString());
|
||||
localStorage.setItem("username", res.user_name);
|
||||
|
||||
navigate("/profile"); // редирект на профиль
|
||||
} else {
|
||||
setError(res.error || "Login failed");
|
||||
setError("Login failed");
|
||||
}
|
||||
} else {
|
||||
// SignUp оставляем без сохранения данных
|
||||
const res = await AuthService.postAuthSignUp({ nickname, pass: password });
|
||||
const res = await AuthService.postSignUp({ nickname, pass: password });
|
||||
if (res.user_id) {
|
||||
setIsLogin(true); // переключаемся на login после регистрации
|
||||
} else {
|
||||
setError(res.error || "Sign up failed");
|
||||
setError("Sign up failed");
|
||||
}
|
||||
}
|
||||
} catch (err: any) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue