From a1d7f2b9ee20f84a11f2b8febe0739a2076eec0b Mon Sep 17 00:00:00 2001 From: AzenKain Date: Tue, 7 Apr 2026 17:59:15 +0700 Subject: [PATCH] UPDATE: logout --- internal/controllers/authController.go | 37 +++++++++++++++ internal/routes/authRoute.go | 1 + internal/services/authService.go | 33 ++++++++++++++ internal/services/userService.go | 62 +++++++++++++------------- 4 files changed, 102 insertions(+), 31 deletions(-) diff --git a/internal/controllers/authController.go b/internal/controllers/authController.go index 508f44d..57d6ef6 100644 --- a/internal/controllers/authController.go +++ b/internal/controllers/authController.go @@ -432,3 +432,40 @@ func (h *AuthController) GoogleCallback(c fiber.Ctx) error { return c.Redirect().To(redirectURL) } + +func (h *AuthController) Logout(c fiber.Ctx) error { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + userId := c.Locals("uid").(string) + + err := h.service.Logout(ctx, userId) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(response.CommonResponse{ + Status: false, + Message: err.Error(), + }) + } + + c.Cookie(&fiber.Cookie{ + Name: "access_token", + Value: "", + Expires: time.Now().Add(-time.Hour), + HTTPOnly: true, + Secure: true, + Path: "/", + }) + + c.Cookie(&fiber.Cookie{ + Name: "refresh_token", + Value: "", + Expires: time.Now().Add(-time.Hour), + HTTPOnly: true, + Secure: true, + Path: "/", + }) + + return c.Status(fiber.StatusOK).JSON(response.CommonResponse{ + Status: true, + Message: "Logged out successfully", + }) +} diff --git a/internal/routes/authRoute.go b/internal/routes/authRoute.go index 1e9b518..ea2d367 100644 --- a/internal/routes/authRoute.go +++ b/internal/routes/authRoute.go @@ -18,4 +18,5 @@ func AuthRoutes(app *fiber.App, controller *controllers.AuthController, userRepo route.Post("/forgot-password", controller.ForgotPassword) route.Get("/google/login", controller.GoogleLogin) route.Get("/google/callback", controller.GoogleCallback) + route.Post("/logout", middlewares.JwtAccess(userRepo), controller.Logout) } diff --git a/internal/services/authService.go b/internal/services/authService.go index c9be612..df11522 100644 --- a/internal/services/authService.go +++ b/internal/services/authService.go @@ -33,6 +33,7 @@ import ( type AuthService interface { Signin(ctx context.Context, dto *request.SignInDto) (*response.AuthResponse, error) Signup(ctx context.Context, dto *request.SignUpDto) (*response.AuthResponse, error) + Logout(ctx context.Context, userId string) error ForgotPassword(ctx context.Context, dto *request.ForgotPasswordDto) error VerifyToken(ctx context.Context, dto *request.VerifyTokenDto) (*response.VerifyTokenResponse, error) CreateToken(ctx context.Context, dto *request.CreateTokenDto) error @@ -170,6 +171,38 @@ func (a *authService) Signin(ctx context.Context, dto *request.SignInDto) (*resp } + +func (a *authService) Logout(ctx context.Context, userId string) error { + pgID, err := convert.StringToUUID(userId) + if err != nil { + return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + } + user , err := a.userRepo.GetByID(ctx, pgID) + if err != nil || user == nil { + return fiber.NewError(fiber.StatusInternalServerError, "Invalid user data") + } + + err = a.userRepo.UpdateTokenVersion(ctx, sqlc.UpdateTokenVersionParams{ + ID: pgID, + TokenVersion: user.TokenVersion + 1, + }) + if err != nil { + return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + } + + err = a.userRepo.UpdateRefreshToken(ctx, sqlc.UpdateUserRefreshTokenParams{ + ID: pgID, + RefreshToken: pgtype.Text{ + String: "", + Valid: false, + }, + }) + if err != nil { + return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + } + return nil +} + func (a *authService) RefreshToken(ctx context.Context, id string) (*response.AuthResponse, error) { var pgID pgtype.UUID err := pgID.Scan(id) diff --git a/internal/services/userService.go b/internal/services/userService.go index 53f190b..dc67bb4 100644 --- a/internal/services/userService.go +++ b/internal/services/userService.go @@ -210,8 +210,8 @@ func (u *userService) RestoreUser(ctx context.Context, userId string) (*response func (u *userService) SearchUser(ctx context.Context, dto *request.SearchUserDto) (*response.PaginatedResponse, error) { arg := sqlc.SearchUsersParams{ - Limit: int32(dto.Limit + 1), - } + Limit: int32(dto.Limit + 1), + } if dto.Sort != "" { arg.Sort = pgtype.Text{String: dto.Sort, Valid: true} @@ -225,64 +225,64 @@ func (u *userService) SearchUser(ctx context.Context, dto *request.SearchUserDto arg.Order = pgtype.Text{String: "asc", Valid: true} } - if dto.Cursor != "" { + if dto.Cursor != "" { pgID, err := convert.StringToUUID(dto.Cursor) if err != nil { return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid cursor format") } - arg.Cursor = pgID - } + arg.Cursor = pgID + } - if dto.Search != "" { + if dto.Search != "" { pgID, err := convert.StringToUUID(dto.Search) if err == nil { arg.SearchID = pgID } else { arg.SearchText = pgtype.Text{String: dto.Search, Valid: true} } - } + } if dto.IsDeleted != nil { - arg.IsDeleted = pgtype.Bool{Bool: *dto.IsDeleted, Valid: true} - } + arg.IsDeleted = pgtype.Bool{Bool: *dto.IsDeleted, Valid: true} + } if len(dto.RoleIDs) > 0 { - var pgRoleIDs []pgtype.UUID - for _, idStr := range dto.RoleIDs { + var pgRoleIDs []pgtype.UUID + for _, idStr := range dto.RoleIDs { pgID, err := convert.StringToUUID(idStr) if err != nil { continue } pgRoleIDs = append(pgRoleIDs, pgID) - } - arg.RoleIds = pgRoleIDs - } + } + arg.RoleIds = pgRoleIDs + } rows, err := u.userRepo.Search(ctx, arg) - if err != nil { - return nil, err - } + if err != nil { + return nil, err + } hasMore := false - var nextCursor string + var nextCursor string - if len(rows) > dto.Limit { - hasMore = true - nextCursor = rows[dto.Limit-1].ID - rows = rows[:dto.Limit] - } + if len(rows) > dto.Limit { + hasMore = true + nextCursor = rows[dto.Limit-1].ID + rows = rows[:dto.Limit] + } users := models.UsersEntityToResponse(rows) res := &response.PaginatedResponse{ - Data: users, - Status: true, - Message: "", - } + Data: users, + Status: true, + Message: "", + } - res.Pagination.HasMore = hasMore - res.Pagination.NextCursor = nextCursor + res.Pagination.HasMore = hasMore + res.Pagination.NextCursor = nextCursor - return res, nil + return res, nil } func (u *userService) GetUserByID(ctx context.Context, userId string) (*response.UserResponse, error) { @@ -295,4 +295,4 @@ func (u *userService) GetUserByID(ctx context.Context, userId string) (*response return nil, fiber.NewError(fiber.StatusNotFound, err.Error()) } return user.ToResponse(), nil -} \ No newline at end of file +}