From 267069f151ff78d4558fb467d97b52c86438f14a Mon Sep 17 00:00:00 2001 From: ZoanhLuong <146954072+LuongDangDoanh@users.noreply.github.com> Date: Fri, 5 Jul 2024 12:28:55 +0700 Subject: [PATCH] feat:block-unblock-account (#194) Co-authored-by: CB.Bang <112295151+Buicongbang04@users.noreply.github.com> --- src/modules/user/user.controllers.ts | 71 ++++++++++++++++++-- src/modules/user/user.messages.ts | 9 ++- src/modules/user/user.middlewares.ts | 53 ++++++++------- src/modules/user/user.requests.ts | 11 +++- src/modules/user/user.routes.ts | 15 ++++- src/modules/user/user.schema.ts | 97 ++++++++++++++++------------ src/modules/user/user.services.ts | 66 +++++++++++++++---- 7 files changed, 231 insertions(+), 91 deletions(-) diff --git a/src/modules/user/user.controllers.ts b/src/modules/user/user.controllers.ts index 79bc2a4..e57c8a0 100644 --- a/src/modules/user/user.controllers.ts +++ b/src/modules/user/user.controllers.ts @@ -12,17 +12,26 @@ import { OTP_MESSAGES } from "../otp/otp.messages"; import { UserRole } from "./user.enum"; import { USER_MESSAGES } from "./user.messages"; import { + + BlockBody, + ListAccountQuery, + LoginRequestBody, LogoutReqBody, RefreshTokenReqBody, RegisterReqBody, TokenPayload, UpdateMeReqBody, - UserResponseAfterCheckEmailOrPhone, -} from "./user.requests"; -import User from "./user.schema"; -import usersService from "./user.services"; + UserResponseAfterCheckEmailOrPhone +} from './user.requests' +import User from './user.schema' +import usersService from './user.services' +import adminService from "../admin/admin.services"; +import { UserList } from "~/constants/user.type"; +import { UserRole } from "./user.enum"; + + export const registerController = async ( req: Request, @@ -254,9 +263,56 @@ export const refreshTokenController = async ( return res.json({ message: USER_MESSAGES.REFRESH_TOKEN_SUCCESSFULLY, - data: { access_token }, - }); -}; + + data: { access_token } + }) +} + +export const blockAccountController = async ( + req: Request, + res: Response +) => { + console.log(req.cookies['refresh_token']) + await usersService.logout(req.cookies['refresh_token']) + res.clearCookie('refresh_token') + try { + const reason = req.body.description_reason + const picture_image_prove = req.body.picture_image_prove + const timeBlock = new Date() + const user_id = req.body._id + await usersService.blockAccount( + user_id, + reason, + picture_image_prove, + timeBlock + ) + return res.json({ + message: USER_MESSAGES.USER_HAS_BEEN_BLOCKED + }) + } catch (error) { + return res.status(400).json({ + message: 'Bad request' + }) + } +} + +export const unblockAccountController = async ( + req: Request, + res: Response +) => { + try { + const user_id = req.body._id + await usersService.unblockAccount(user_id) + return res.json({ + message: USER_MESSAGES.USER_UNBLOCK_SUCCESSFULLY + }) + } catch (error) { + return res.status(400).json({ + message: 'Bad request' + }) + } +} + export const getListUserController = async (req: Request, res: Response) => { const query = req.queryListAccount as ListAccountQuery; @@ -344,3 +400,4 @@ export const getListUserController = async (req: Request, res: Response) => { }); } }; + diff --git a/src/modules/user/user.messages.ts b/src/modules/user/user.messages.ts index e36fdbc..f774ed7 100644 --- a/src/modules/user/user.messages.ts +++ b/src/modules/user/user.messages.ts @@ -105,9 +105,14 @@ export const USER_MESSAGES = { WRONG_PASS_5_TIMES: "Entered wrong password over 5 times!", //token - REFRESH_TOKEN_IS_REQUIRED: "Refresh token is required", - OTP_IS_INCORRECT: "OTP is incorrect", + + REFRESH_TOKEN_IS_REQUIRED: 'Refresh token is required', + OTP_IS_INCORRECT: 'OTP is incorrect', + // block + USER_HAS_BEEN_BLOCKED: 'user has been blocked', + USER_UNBLOCK_SUCCESSFULLY: 'user unblock successfully' //don't have permission DONT_HAVE_PERMISSION: `You don't have permission`, } as const; + diff --git a/src/modules/user/user.middlewares.ts b/src/modules/user/user.middlewares.ts index 6de47cd..15b2149 100644 --- a/src/modules/user/user.middlewares.ts +++ b/src/modules/user/user.middlewares.ts @@ -1,27 +1,29 @@ -import "dotenv/config"; -import { NextFunction, Request, Response } from "express"; -import { ParamsDictionary } from "express-serve-static-core"; -import { ParamSchema, checkSchema } from "express-validator"; +import 'dotenv/config' +import { NextFunction, Request, Response } from 'express' +import { ParamsDictionary } from 'express-serve-static-core' +import { ParamSchema, checkSchema } from 'express-validator' import { StatusCodes } from "http-status-codes"; -import { JsonWebTokenError } from "jsonwebtoken"; -import { capitalize, escape } from "lodash"; -import { ObjectId } from "mongodb"; -import validator from "validator"; -import { HTTP_STATUS } from "~/constants/httpStatus"; -import databaseService from "~/database/database.services"; -import { ErrorEntity, ErrorWithStatus } from "~/errors/errors.entityError"; -import { USER_MESSAGES } from "~/modules/user/user.messages"; -import { isDeveloperAgent } from "~/utils/agent"; -import { encrypt, hashPassword } from "~/utils/crypto"; +import { JsonWebTokenError } from 'jsonwebtoken' +import { capitalize, escape } from 'lodash' +import { ObjectId } from 'mongodb' +import validator from 'validator' +import { HTTP_STATUS } from '~/constants/httpStatus' +import databaseService from '~/database/database.services' +import { ErrorEntity, ErrorWithStatus } from '~/errors/errors.entityError' +import { USER_MESSAGES } from '~/modules/user/user.messages' +import { isDeveloperAgent } from '~/utils/agent' +import { encrypt, hashPassword } from '~/utils/crypto' import { numberToEnum } from "~/utils/handler"; -import { verifyToken } from "~/utils/jwt"; -import { isValidPhoneNumberForCountry, validate } from "~/utils/validation"; -import { OTP_STATUS } from "../otp/otp.enum"; -import { OTP_MESSAGES } from "../otp/otp.messages"; -import otpService from "../otp/otp.services"; -import { NoticeUser, UserRole, UserVerifyStatus } from "./user.enum"; -import { LoginRequestBody, TokenPayload } from "./user.requests"; -import usersService from "./user.services"; +import { verifyToken } from '~/utils/jwt' +import { isValidPhoneNumberForCountry, validate } from '~/utils/validation' +import { OTP_STATUS } from '../otp/otp.enum' +import { OTP_MESSAGES } from '../otp/otp.messages' +import otpService from '../otp/otp.services' +import { NoticeUser, Subscription, UserVerifyStatus, UserRole, } from './user.enum' +import { LoginRequestBody, TokenPayload } from './user.requests' +import usersService from './user.services' +import { StatusCodes } from 'http-status-codes' + //! Prevent db injection, XSS attack export const paramSchema: ParamSchema = { @@ -284,7 +286,9 @@ export const loginValidator = validate( if ( user.notice === NoticeUser.Banned || - user.reasonBanned !== "" + user.reasonBanned !== '' || + user.block === Subscription.True + ) { throw new ErrorWithStatus({ message: USER_MESSAGES.ACCOUNT_IS_BANNED, @@ -369,7 +373,8 @@ export const loginValidator = validate( if ( user.notice === NoticeUser.Banned || - user.reasonBanned !== "" + user.reasonBanned !== '' || + user.block === Subscription.True ) { throw new ErrorWithStatus({ message: USER_MESSAGES.ACCOUNT_IS_BANNED, diff --git a/src/modules/user/user.requests.ts b/src/modules/user/user.requests.ts index 0a39c89..0df941e 100644 --- a/src/modules/user/user.requests.ts +++ b/src/modules/user/user.requests.ts @@ -1,3 +1,4 @@ +import { ObjectId } from 'mongodb' import { JwtPayload } from "jsonwebtoken"; import { Subscription, @@ -79,8 +80,14 @@ export type LogoutReqBody = { }; export type RefreshTokenReqBody = { - refresh_token: string; -}; + refresh_token: string +} +export interface BlockBody { + _id: ObjectId + description_reason?: string + picture_image_prove?: string + refresh_token: string +} export type ListAccountQuery = { page?: number; diff --git a/src/modules/user/user.routes.ts b/src/modules/user/user.routes.ts index afc9c9b..3693d91 100644 --- a/src/modules/user/user.routes.ts +++ b/src/modules/user/user.routes.ts @@ -3,6 +3,7 @@ import { Router } from "express"; import { cronJobFake } from "~/utils/cronJobFake"; import { wrapAsync } from "~/utils/handler"; import { + blockAccountController, changePasswordController, forgotPasswordController, getListUserController, @@ -14,6 +15,7 @@ import { resetPasswordController, searchAccountController, sendVerifyAccountOTPController, + unblockAccountController, updateMeController, verifyAccountController, verifyForgotPasswordTokenController, @@ -233,8 +235,17 @@ usersRouter.post( usersRouter.post( "/refresh-token", refreshTokenCookieValidator, - wrapAsync(refreshTokenController), -); + wrapAsync(refreshTokenController) +) + +usersRouter.post( + '/block', + accessTokenValidator, + refreshTokenCookieValidator, + wrapAsync(blockAccountController) +) + +usersRouter.post('/unblock', wrapAsync(unblockAccountController)) usersRouter.get( "/list-account", diff --git a/src/modules/user/user.schema.ts b/src/modules/user/user.schema.ts index 08ddd13..2b67d16 100644 --- a/src/modules/user/user.schema.ts +++ b/src/modules/user/user.schema.ts @@ -9,56 +9,69 @@ import { interface UserType { _id?: ObjectId; // username: string - first_name: string; - last_name: string; - phone_number?: string; - password: string; - email?: string; - role?: UserRole; - created_at?: Date; - updated_at?: Date; - status?: UserVerifyStatus; - notice?: NoticeUser; - wrongPasswordTimes?: number; - reasonBanned?: string; - avatar_url?: string; - subscription: Subscription; + first_name: string + last_name: string + phone_number?: string + password: string + email?: string + role?: UserRole + created_at?: Date + updated_at?: Date + status?: UserVerifyStatus + notice?: NoticeUser + block?: Subscription + reasonBlocked?: string + picture_image_prove?: string + block_time?: Date + wrongPasswordTimes?: number + reasonBanned?: string + avatar_url?: string + subscription: Subscription } export default class User { _id?: ObjectId; // username: string - first_name: string; - last_name: string; - phone_number: string; - password: string; - email: string; - role: UserRole; - created_at: Date; - updated_at: Date; - status: UserVerifyStatus; - notice: NoticeUser; - wrongPasswordTimes?: number; - reasonBanned?: string; - avatar_url: string; - subscription: Subscription; + first_name: string + last_name: string + phone_number: string + password: string + email: string + role: UserRole + created_at: Date + updated_at: Date + status: UserVerifyStatus + notice: NoticeUser + block: Subscription + reasonBlocked?: string + picture_image_prove?: string + block_time: Date + wrongPasswordTimes?: number + reasonBanned?: string + avatar_url: string + subscription: Subscription + constructor(user: UserType) { const date = new Date(); this._id = user._id || new ObjectId(); // this.username = user.username || '' - this.first_name = user.first_name || ""; - this.last_name = user.last_name || ""; - this.phone_number = user.phone_number || ""; - this.password = user.password; - this.email = user.email || ""; - this.role = user.role || UserRole.Customer; - this.created_at = user.created_at || date; - this.updated_at = user.updated_at || date; - this.status = user.status || UserVerifyStatus.Unverified; - this.notice = NoticeUser.Active; - this.wrongPasswordTimes = 0; - this.reasonBanned = user.reasonBanned || ""; - this.avatar_url = user.avatar_url || ""; - this.subscription = user.subscription || Subscription.False; + this.first_name = user.first_name || '' + this.last_name = user.last_name || '' + this.phone_number = user.phone_number || '' + this.password = user.password + this.email = user.email || '' + this.role = user.role || UserRole.Customer + this.created_at = user.created_at || date + this.updated_at = user.updated_at || date + this.status = user.status || UserVerifyStatus.Unverified + this.notice = NoticeUser.Active + this.block = Subscription.False + this.reasonBlocked = user.reasonBlocked || '' + this.picture_image_prove = user.picture_image_prove || '' + this.block_time = user.block_time || date + this.wrongPasswordTimes = 0 + this.reasonBanned = user.reasonBanned || '' + this.avatar_url = user.avatar_url || '' + this.subscription = user.subscription || Subscription.False } } diff --git a/src/modules/user/user.services.ts b/src/modules/user/user.services.ts index 8345a8c..9fc17b1 100644 --- a/src/modules/user/user.services.ts +++ b/src/modules/user/user.services.ts @@ -1,16 +1,22 @@ -import "dotenv/config"; -import { capitalize, omit } from "lodash"; -import { ObjectId } from "mongodb"; -import otpGenerator from "otp-generator"; +import 'dotenv/config' +import { capitalize, omit } from 'lodash' +import { ObjectId } from 'mongodb' +import otpGenerator from 'otp-generator' import { UserList } from "~/constants/user.type"; -import databaseService from "~/database/database.services"; -import { capitalizePro } from "~/utils/capitalize"; -import decrypt, { encrypt, hashPassword } from "~/utils/crypto"; -import { signToken, verifyToken } from "~/utils/jwt"; -import { OTP_KIND } from "../otp/otp.enum"; -import otpService from "../otp/otp.services"; -import RefreshToken from "../refreshToken/refreshToken.schema"; -import { NoticeUser, TokenType, UserRole, UserVerifyStatus } from "./user.enum"; +import databaseService from '~/database/database.services' +import { capitalizePro } from '~/utils/capitalize' +import decrypt, { encrypt, hashPassword } from '~/utils/crypto' +import { signToken, verifyToken } from '~/utils/jwt' +import { OTP_KIND } from '../otp/otp.enum' +import otpService from '../otp/otp.services' +import RefreshToken from '../refreshToken/refreshToken.schema' +import { + NoticeUser, + Subscription, + TokenType, + UserRole, + UserVerifyStatus +} from './user.enum' import { ListAccountQuery, LogoutReqBody, @@ -357,6 +363,42 @@ class UsersService { return true; } + async blockAccount( + user_id: ObjectId, + re?: string, + pi?: string, + time?: Date + ) { + await databaseService.users.updateMany( + { _id: new ObjectId(user_id) }, + { + $set: { + block: Subscription.True, + reasonBlocked: re, + picture_image_prove: pi, + block_time: time + } + } + ) + + return true + } + + async unblockAccount(user_id: ObjectId) { + await databaseService.users.updateMany( + { _id: new ObjectId(user_id) }, + { + $set: { + block: Subscription.False, + reasonBlocked: '', + picture_image_prove: '' + } + } + ) + + return true + } + async updateMe({ user_id, payload,