diff --git a/package-lock.json b/package-lock.json index f098145..757708d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "dependencies": { "@types/request": "^2.48.12", + "axios": "^1.7.2", "cookie-parser": "^1.4.6", "cookie-session": "^2.1.0", "cors": "^2.8.5", @@ -1594,7 +1595,6 @@ "version": "1.7.2", "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", - "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", diff --git a/package.json b/package.json index c954692..d1575da 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ }, "dependencies": { "@types/request": "^2.48.12", + "axios": "^1.7.2", "cookie-parser": "^1.4.6", "cookie-session": "^2.1.0", "cors": "^2.8.5", diff --git a/src/constants/user.type.ts b/src/constants/user.type.ts index 67eeb49..c8ca794 100644 --- a/src/constants/user.type.ts +++ b/src/constants/user.type.ts @@ -1,7 +1,8 @@ import { ObjectId } from "mongodb"; import { UserRole, UserVerifyStatus } from "~/modules/user/user.enum"; +import User from "~/modules/user/user.schema"; -export type UserList = { +export interface UserList { _id: ObjectId; first_name: string; last_name: string; @@ -9,4 +10,6 @@ export type UserList = { email: string; role: UserRole; status: UserVerifyStatus; -}; +} + +export type UserAvatarInfo = Pick; diff --git a/src/modules/protectRouting/mapRouteWithRole.json b/src/modules/protectRouting/mapRouteWithRole.json index 91782f4..4478885 100644 --- a/src/modules/protectRouting/mapRouteWithRole.json +++ b/src/modules/protectRouting/mapRouteWithRole.json @@ -1,184 +1,189 @@ -[ - { - "module": "admin", - "route": { - "login": { - "api": "/admin/login", - "method": "post", - "access_token": false - }, - "createEmployee": { - "api": "/admin/createEmployee", - "method": "post", - "access_token": true - }, - ":id": { - "api": "/admin/:id", - "method": "patch", - "access_token": true - } - } - }, - { - "module": "captcha", - "route": { - "/": { - "api": "/", - "method": "get", - "access_token": false - }, - "verify": { - "api": "/captcha/verify", - "method": "post", - "access_token": false - } - } - }, - { - "module": "menu", - "route": { - ":language": { - "api": "/menu/:language", - "method": "get", - "access_token": false - } - } - }, - { - "module": "oauth", - "route": { - "google": { - "api": "/oauth/google", - "method": "get", - "access_token": false - }, - "google/callback": { - "api": "/oauth/google/callback", - "method": "get", - "access_token": false - }, - "facebook": { - "api": "/oauth/facebook", - "method": "get", - "access_token": false - }, - "facebook/callback": { - "api": "/oauth/facebook/callback", - "method": "get", - "access_token": false - }, - "login-success": { - "api": "/oauth/login-success", - "method": "post", - "access_token": false - }, - "login-fail": { - "api": "/oauth/login-fail", - "method": "post", - "access_token": false - } - } - }, - { - "module": "otp", - "route": { - "send-otp-phone": { - "api": "/otp/send-otp-phone", - "method": "post", - "access_token": true - }, - "send-otp-email": { - "api": "/otp/send-otp-email", - "method": "post", - "access_token": true - } - } - }, - { - "module": "password", - "route": { - "updatePass": { - "api": "/pass/updatePass", - "method": "post", - "access_token": true - } - } - }, - { - "module": "user", - "route": { - "register": { - "api": "/user/register", - "method": "post", - "access_token": false - }, - "login": { - "api": "/user/login", - "method": "post", - "access_token": false - }, - "forgot-password": { - "api": "/user/forgot-password", - "method": "post", - "access_token": false - }, - "verify-otp": { - "api": "/user/verify-otp", - "method": "post", - "access_token": false - }, - "reset-password": { - "api": "/user/reset-password", - "method": "post", - "access_token": false - }, - "send-verify-account-otp": { - "api": "/user/send-verify-account-otp", - "method": "post", - "access_token": true - }, - "verify-account": { - "api": "/user/verify-account", - "method": "post", - "access_token": true - }, - "change-password": { - "api": "/user/change-password", - "method": "post", - "access_token": true - }, - "me": [ - { - "api": "/user/me", - "method": "get", - "access_token": true - }, - { - "api": "/user/me", - "method": "patch", - "access_token": true - } - ], - "search": { - "api": "/user/search", - "method": "post", - "access_token": true - }, - "logout": { - "api": "/user/logout", - "method": "post", - "access_token": true - }, - "refresh-token": { - "api": "/user/refresh-token", - "method": "post", - "access_token": false - }, - "list-account": { - "api": "/user/list-account", - "method": "get", - "access_token": false - } - } - } -] +[ + { + "module": "admin", + "route": { + "login": { + "api": "/admin/login", + "method": "post", + "access_token": false + }, + "createEmployee": { + "api": "/admin/createEmployee", + "method": "post", + "access_token": true + }, + ":id": { + "api": "/admin/:id", + "method": "patch", + "access_token": true + } + } + }, + { + "module": "captcha", + "route": { + "/": { + "api": "/", + "method": "get", + "access_token": false + }, + "verify": { + "api": "/captcha/verify", + "method": "post", + "access_token": false + } + } + }, + { + "module": "menu", + "route": { + ":language": { + "api": "/menu/:language", + "method": "get", + "access_token": false + } + } + }, + { + "module": "oauth", + "route": { + "google": { + "api": "/oauth/google", + "method": "get", + "access_token": false + }, + "google/callback": { + "api": "/oauth/google/callback", + "method": "get", + "access_token": false + }, + "facebook": { + "api": "/oauth/facebook", + "method": "get", + "access_token": false + }, + "facebook/callback": { + "api": "/oauth/facebook/callback", + "method": "get", + "access_token": false + }, + "login-success": { + "api": "/oauth/login-success", + "method": "post", + "access_token": false + }, + "login-fail": { + "api": "/oauth/login-fail", + "method": "post", + "access_token": false + } + } + }, + { + "module": "otp", + "route": { + "send-otp-phone": { + "api": "/otp/send-otp-phone", + "method": "post", + "access_token": true + }, + "send-otp-email": { + "api": "/otp/send-otp-email", + "method": "post", + "access_token": true + } + } + }, + { + "module": "password", + "route": { + "updatePass": { + "api": "/pass/updatePass", + "method": "post", + "access_token": true + } + } + }, + { + "module": "user", + "route": { + "register": { + "api": "/user/register", + "method": "post", + "access_token": false + }, + "login": { + "api": "/user/login", + "method": "post", + "access_token": false + }, + "forgot-password": { + "api": "/user/forgot-password", + "method": "post", + "access_token": false + }, + "verify-otp": { + "api": "/user/verify-otp", + "method": "post", + "access_token": false + }, + "reset-password": { + "api": "/user/reset-password", + "method": "post", + "access_token": false + }, + "send-verify-account-otp": { + "api": "/user/send-verify-account-otp", + "method": "post", + "access_token": true + }, + "verify-account": { + "api": "/user/verify-account", + "method": "post", + "access_token": true + }, + "change-password": { + "api": "/user/change-password", + "method": "post", + "access_token": true + }, + "me": [ + { + "api": "/user/me", + "method": "get", + "access_token": true + }, + { + "api": "/user/me", + "method": "patch", + "access_token": true + } + ], + "search": { + "api": "/user/search", + "method": "post", + "access_token": true + }, + "logout": { + "api": "/user/logout", + "method": "post", + "access_token": true + }, + "refresh-token": { + "api": "/user/refresh-token", + "method": "post", + "access_token": false + }, + "list-account": { + "api": "/user/list-account", + "method": "get", + "access_token": false + }, + "getLinkPic": { + "api": "/user/getLinkPic", + "method": "post", + "access_token": false + } + } + } +] diff --git a/src/modules/user/user.controllers.ts b/src/modules/user/user.controllers.ts index 3bfceb7..3322fd4 100644 --- a/src/modules/user/user.controllers.ts +++ b/src/modules/user/user.controllers.ts @@ -1,454 +1,454 @@ -import "dotenv/config"; -import { NextFunction, Request, Response } from "express"; -import { ParamsDictionary } from "express-serve-static-core"; -import { StatusCodes } from "http-status-codes"; -import { pick } from "lodash"; -import { ObjectId } from "mongodb"; -import { HTTP_STATUS } from "~/constants/httpStatus"; -import { UserList } from "~/constants/user.type"; -import decrypt, { encrypt } from "~/utils/crypto"; -import adminService from "../admin/admin.services"; -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"; -import { v4 as uuidv4 } from "uuid"; -import path from "path"; -import { storageRef } from "~/config/firebase.config"; -import fs from "fs"; - -export const registerController = async ( - req: Request, - res: Response, - next: NextFunction, -) => { - const { access_token, refresh_token } = await usersService.register( - req.body, - ); - res.cookie("refresh_token", refresh_token, { - httpOnly: true, - secure: true, - maxAge: Number(process.env.COOKIE_EXPIRE), - }); - - return res.json({ - message: USER_MESSAGES.REGISTER_SUCCESS, - data: { access_token, refresh_token }, - }); -}; - -export const loginController = async ( - req: Request, - res: Response, - next: NextFunction, -) => { - const user = req.user as User; - const user_id = user._id as ObjectId; - const role = user.role; - const result = await usersService.login({ - user_id: user_id.toString(), - status: user.status, - role: role, - }); - - const { refresh_token } = result; - res.cookie("refresh_token", refresh_token, { - httpOnly: true, - secure: true, - maxAge: Number(process.env.COOKIE_EXPIRE), - }); - - return res.json({ - message: USER_MESSAGES.LOGIN_SUCCESS, - data: result, - }); -}; - -export const forgotPasswordController = async ( - req: Request, - res: Response, - next: NextFunction, -) => { - const result = - req.body.type === "email" - ? await usersService.sendForgotPasswordOTPByEmail(req.body.email) - : await usersService.sendForgotPasswordOTPByPhone( - req.body.phone_number, - ); - return res.status(200).json({ - message: OTP_MESSAGES.SEND_OTP_SUCCESSFULLY, - }); -}; - -export const verifyForgotPasswordTokenController = async ( - req: Request, - res: Response, - next: NextFunction, -) => { - return res.status(200).json({ - message: OTP_MESSAGES.VERIFY_OTP_SUCCESSFULLY, - }); -}; - -export const resetPasswordController = async (req: Request, res: Response) => { - const user_id = req.body.user_id; - const password = req.body.password; - const result = await usersService.resetPassword(user_id, password); - return res.status(200).json({ - message: OTP_MESSAGES.RESET_PASSWORD_SUCCESSFULLY, - details: result, - }); -}; - -export const sendVerifyAccountOTPController = async ( - req: Request, - res: Response, -) => { - const result = - req.body.type === "email" - ? await usersService.sendVerifyAccountOTPByEmail(req.body.email) - : await usersService.sendVerifyAccountOTPByPhone( - req.body.phone_number, - ); - return res.status(200).json({ - message: OTP_MESSAGES.SEND_OTP_SUCCESSFULLY, - }); -}; - -export const verifyAccountController = async (req: Request, res: Response) => { - const user_id = req.body.user_id; - const result = await usersService.verifyAccount(user_id); - return res.status(200).json({ - message: USER_MESSAGES.VERIFY_ACCOUNT_SUCCESSFULLY, - details: result, - }); -}; - -export const getMeController = async (req: Request, res: Response) => { - const { user_id } = req.decoded_authorization as TokenPayload; - const user = await usersService.getMe(user_id); - - const { first_name, last_name, status } = user; - const email = user.email !== "" ? await decrypt(user.email) : ""; - const phone_number = - user.phone_number !== "" ? await decrypt(user.phone_number) : ""; - - return res.status(200).json({ - message: USER_MESSAGES.GET_ME_SUCCESSFULLY, - data: { - user_id, - first_name, - last_name, - email, - phone_number, - status, - }, - }); -}; - -export const updateMeController = async ( - req: Request, - res: Response, - next: NextFunction, -) => { - const { user_id } = (req as Request).decoded_authorization as TokenPayload; - const allowedFields: (keyof UpdateMeReqBody)[] = [ - "first_name", - "last_name", - "email", - "phone_number", - "avatar_url", - "subscription", - "password", - ]; - const body = pick(req.body, allowedFields); - const user = await usersService.updateMe({ - user_id, - payload: body as UpdateMeReqBody, - }); - return res.json({ - message: USER_MESSAGES.UPDATE_ME_SUCCESSFULLY, - user, - }); -}; - -export const changePasswordController = async (req: Request, res: Response) => { - const user_id = req.body.user_id; - const new_password = req.body.new_password; - const result = await usersService.resetPassword(user_id, new_password); - return res.status(StatusCodes.OK).json({ - message: USER_MESSAGES.CHANGE_PASSWORD_SUCCESSFULLY, - details: result, - }); -}; - -export const searchAccountController = async (req: Request, res: Response) => { - const data = req.body as UserResponseAfterCheckEmailOrPhone; - - let result = false; - let user; - - if (data.type === "email") { - if (await usersService.checkEmailExist(encrypt(data.email))) { - result = true; - user = await usersService.findUserByEmail(encrypt(data.email)); - } - } else { - if ( - await usersService.checkPhoneNumberExist(encrypt(data.phone_number)) - ) { - result = true; - user = await usersService.findUserByPhone( - encrypt(data.phone_number), - ); - } - } - - if (result) { - res.status(HTTP_STATUS.OK).json({ - isExist: result, - data: user, - }); - } else { - res.status(HTTP_STATUS.NOT_FOUND).json({ - isExist: result, - }); - } -}; - -export const logoutController = async ( - req: Request, - res: Response, -) => { - console.log(req.cookies["refresh_token"]); - await usersService.logout(req.cookies["refresh_token"]); - res.clearCookie("refresh_token"); - return res.json({ - message: USER_MESSAGES.LOGOUT_SUCCESSFULLY, - }); -}; - -export const refreshTokenController = async ( - req: Request, - res: Response, -) => { - const payload = req.decoded_refresh_token as TokenPayload; - const old_refresh_token = req.cookies["refresh_token"]; - const { access_token, refresh_token } = await usersService.refreshToken( - old_refresh_token, - payload, - ); - - res.cookie("refresh_token", refresh_token, { - httpOnly: true, - secure: true, - maxAge: Number(process.env.COOKIE_EXPIRE), - }); - - return res.json({ - message: USER_MESSAGES.REFRESH_TOKEN_SUCCESSFULLY, - - 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; - - if (!query.role) { - const listUser = await usersService.getListCustomer(); - const listEmployee = await adminService.getListAccountEmployee(); - const newList = [...listUser, ...listEmployee]; - const result: UserList[] = []; - for (let i = 0; i < newList.length; i++) { - if ( - i >= - Number(query.limit) * Number(query.page) - - Number(query.limit) && - i < Number(query.limit) * Number(query.page) - ) { - result.push(newList[i]); - } - } - const total_page = Math.ceil(newList.length / Number(query.limit)); - - return res.json({ - message: "Get list user success", - data: { - list_user: result, - pagination: { - page: query.page, - limit: query.limit, - total_page, - }, - }, - }); - } else if (query.role == UserRole.Customer) { - const listUser = await usersService.getListCustomer(); - const result: UserList[] = []; - for (let i = 0; i < listUser.length; i++) { - if ( - i >= - Number(query.limit) * Number(query.page) - - Number(query.limit) && - i < Number(query.limit) * Number(query.page) - ) { - result.push(listUser[i]); - } - } - const total_page = Math.ceil(listUser.length / Number(query.limit)); - - return res.json({ - message: "Get list user success", - data: { - list_user: result, - pagination: { - page: query.page, - limit: query.limit, - total_page, - }, - }, - }); - } else if (query.role == UserRole.Employee) { - const listEmployee = await adminService.getListAccountEmployee(); - const result: UserList[] = []; - for (let i = 0; i < listEmployee.length; i++) { - if ( - i >= - Number(query.limit) * Number(query.page) - - Number(query.limit) && - i < Number(query.limit) * Number(query.page) - ) { - console.log(1); - result.push(listEmployee[i]); - } - } - const total_page = Math.ceil(listEmployee.length / Number(query.limit)); - - return res.json({ - message: "Get list user success", - data: { - list_user: result, - pagination: { - page: query.page, - limit: query.limit, - total_page, - }, - }, - }); - } -}; -async function downloadAndUploadImage( - imageUrl: string, - filename: string, -): Promise { - const response = await fetch(imageUrl); - if (!response.ok) { - throw new Error(`Failed to download image from ${imageUrl}`); - } - const buffer = await response.arrayBuffer(); - const tempFilePath = path.resolve("imageUpload") + `\\${filename}.jpeg`; - // //checkTypeLink - // const fileType = await fileTypeFromBuffer(buffer) - // if (!fileType || !fileType.mime.startsWith('image/')) { - // await fs.promises.unlink(tempFilePath) - // throw new Error(`File ${tempFilePath} is not an image!!!`) - // } - await fs.promises.writeFile(tempFilePath, Buffer.from(buffer)); - try { - const url = await uploadFile(tempFilePath, filename); - return url; - } catch (err) { - throw new Error(USER_MESSAGES.CANT_UPLOAD_IMG); - } finally { - await fs.promises.unlink(tempFilePath); - } -} - -export const getLinkPicture = async ( - req: Request, - res: Response, -) => { - const imageUrl = req.body.avatar_url; - const user_Id = (req.body as User)._id; - if (!imageUrl) { - return res.json({ - message: USER_MESSAGES.CANT_FIND_THIS_IMAGE, - }); - } - const url = await downloadAndUploadImage(imageUrl, `${user_Id}`); - console.log(url); - return res.status(200).json({ - message: USER_MESSAGES.UPLOAD_PICTURE_SUCCESSFULLY, - details: url, - }); -}; -async function uploadFile(path: string, filename: string): Promise { - // Upload the File - const storage = await storageRef.upload(path, { - public: true, - destination: `/uploads/${filename}`, - metadata: { - contentType: "image/jpeg", - firebaseStorageDownloadTokens: uuidv4(), - }, - }); - return storage[0].metadata.mediaLink ?? ""; -} +import "dotenv/config"; +import { NextFunction, Request, Response } from "express"; +import { ParamsDictionary } from "express-serve-static-core"; +import fs from "fs"; +import { StatusCodes } from "http-status-codes"; +import { pick } from "lodash"; +import { ObjectId } from "mongodb"; +import path from "path"; +import { v4 as uuidv4 } from "uuid"; +import { storageRef } from "~/config/firebase.config"; +import { HTTP_STATUS } from "~/constants/httpStatus"; +import { UserList } from "~/constants/user.type"; +import decrypt, { encrypt } from "~/utils/crypto"; +import adminService from "../admin/admin.services"; +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"; + +export const registerController = async ( + req: Request, + res: Response, + next: NextFunction, +) => { + const { access_token, refresh_token } = await usersService.register( + req.body, + ); + res.cookie("refresh_token", refresh_token, { + httpOnly: true, + secure: true, + maxAge: Number(process.env.COOKIE_EXPIRE), + }); + + return res.json({ + message: USER_MESSAGES.REGISTER_SUCCESS, + data: { access_token, refresh_token }, + }); +}; + +export const loginController = async ( + req: Request, + res: Response, + next: NextFunction, +) => { + const user = req.user!; + const user_id = user._id!; + const role = user.role; + const result = await usersService.login({ + user_id: user_id.toString(), + status: user.status, + role: role, + }); + + const { refresh_token } = result; + res.cookie("refresh_token", refresh_token, { + httpOnly: true, + secure: true, + maxAge: Number(process.env.COOKIE_EXPIRE), + }); + + return res.json({ + message: USER_MESSAGES.LOGIN_SUCCESS, + data: result, + }); +}; + +export const forgotPasswordController = async ( + req: Request, + res: Response, + next: NextFunction, +) => { + const result = + req.body.type === "email" + ? await usersService.sendForgotPasswordOTPByEmail(req.body.email) + : await usersService.sendForgotPasswordOTPByPhone( + req.body.phone_number, + ); + return res.status(200).json({ + message: OTP_MESSAGES.SEND_OTP_SUCCESSFULLY, + }); +}; + +export const verifyForgotPasswordTokenController = async ( + req: Request, + res: Response, + next: NextFunction, +) => { + return res.status(200).json({ + message: OTP_MESSAGES.VERIFY_OTP_SUCCESSFULLY, + }); +}; + +export const resetPasswordController = async (req: Request, res: Response) => { + const user_id = req.body.user_id; + const password = req.body.password; + const result = await usersService.resetPassword(user_id, password); + return res.status(200).json({ + message: OTP_MESSAGES.RESET_PASSWORD_SUCCESSFULLY, + details: result, + }); +}; + +export const sendVerifyAccountOTPController = async ( + req: Request, + res: Response, +) => { + const result = + req.body.type === "email" + ? await usersService.sendVerifyAccountOTPByEmail(req.body.email) + : await usersService.sendVerifyAccountOTPByPhone( + req.body.phone_number, + ); + return res.status(200).json({ + message: OTP_MESSAGES.SEND_OTP_SUCCESSFULLY, + }); +}; + +export const verifyAccountController = async (req: Request, res: Response) => { + const user_id = req.body.user_id; + const result = await usersService.verifyAccount(user_id); + return res.status(200).json({ + message: USER_MESSAGES.VERIFY_ACCOUNT_SUCCESSFULLY, + details: result, + }); +}; + +export const getMeController = async (req: Request, res: Response) => { + const { user_id } = req.decoded_authorization!; + const user = await usersService.getMe(user_id); + + const { first_name, last_name, status } = user; + const email = user.email !== "" ? await decrypt(user.email) : ""; + const phone_number = + user.phone_number !== "" ? await decrypt(user.phone_number) : ""; + + return res.status(200).json({ + message: USER_MESSAGES.GET_ME_SUCCESSFULLY, + data: { + user_id, + first_name, + last_name, + email, + phone_number, + status, + }, + }); +}; + +export const updateMeController = async ( + req: Request, + res: Response, + next: NextFunction, +) => { + const { user_id } = (req as Request).decoded_authorization!; + const allowedFields: (keyof UpdateMeReqBody)[] = [ + "first_name", + "last_name", + "email", + "phone_number", + "avatar_url", + "subscription", + "password", + ]; + const body = pick(req.body, allowedFields); + const user = await usersService.updateMe({ + user_id, + payload: body as UpdateMeReqBody, + }); + return res.json({ + message: USER_MESSAGES.UPDATE_ME_SUCCESSFULLY, + user, + }); +}; + +export const changePasswordController = async (req: Request, res: Response) => { + const user_id = req.body.user_id; + const new_password = req.body.new_password; + const result = await usersService.resetPassword(user_id, new_password); + return res.status(StatusCodes.OK).json({ + message: USER_MESSAGES.CHANGE_PASSWORD_SUCCESSFULLY, + details: result, + }); +}; + +export const searchAccountController = async (req: Request, res: Response) => { + const data = req.body as UserResponseAfterCheckEmailOrPhone; + + let result = false; + let user; + + if (data.type === "email") { + if (await usersService.checkEmailExist(encrypt(data.email))) { + result = true; + user = await usersService.findUserByEmail(encrypt(data.email)); + } + } else { + if ( + await usersService.checkPhoneNumberExist(encrypt(data.phone_number)) + ) { + result = true; + user = await usersService.findUserByPhone( + encrypt(data.phone_number), + ); + } + } + + if (result) { + res.status(HTTP_STATUS.OK).json({ + isExist: result, + data: user, + }); + } else { + res.status(HTTP_STATUS.NOT_FOUND).json({ + isExist: result, + }); + } +}; + +export const logoutController = async ( + req: Request, + res: Response, +) => { + console.log(req.cookies.refresh_token); + await usersService.logout(req.cookies.refresh_token); + res.clearCookie("refresh_token"); + return res.json({ + message: USER_MESSAGES.LOGOUT_SUCCESSFULLY, + }); +}; + +export const refreshTokenController = async ( + req: Request, + res: Response, +) => { + const payload = req.decoded_refresh_token!; + const old_refresh_token = req.cookies.refresh_token; + const { access_token, refresh_token } = await usersService.refreshToken( + old_refresh_token, + payload, + ); + + res.cookie("refresh_token", refresh_token, { + httpOnly: true, + secure: true, + maxAge: Number(process.env.COOKIE_EXPIRE), + }); + + return res.json({ + message: USER_MESSAGES.REFRESH_TOKEN_SUCCESSFULLY, + + 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!; + + if (!query.role) { + const listUser = await usersService.getListCustomer(); + const listEmployee = await adminService.getListAccountEmployee(); + const newList = [...listUser, ...listEmployee]; + const result: UserList[] = []; + for (let i = 0; i < newList.length; i++) { + if ( + i >= + Number(query.limit) * Number(query.page) - + Number(query.limit) && + i < Number(query.limit) * Number(query.page) + ) { + result.push(newList[i]); + } + } + const total_page = Math.ceil(newList.length / Number(query.limit)); + + return res.json({ + message: "Get list user success", + data: { + list_user: result, + pagination: { + page: query.page, + limit: query.limit, + total_page, + }, + }, + }); + } else if (query.role == UserRole.Customer) { + const listUser = await usersService.getListCustomer(); + const result: UserList[] = []; + for (let i = 0; i < listUser.length; i++) { + if ( + i >= + Number(query.limit) * Number(query.page) - + Number(query.limit) && + i < Number(query.limit) * Number(query.page) + ) { + result.push(listUser[i]); + } + } + const total_page = Math.ceil(listUser.length / Number(query.limit)); + + return res.json({ + message: "Get list user success", + data: { + list_user: result, + pagination: { + page: query.page, + limit: query.limit, + total_page, + }, + }, + }); + } else if (query.role == UserRole.Employee) { + const listEmployee = await adminService.getListAccountEmployee(); + const result: UserList[] = []; + for (let i = 0; i < listEmployee.length; i++) { + if ( + i >= + Number(query.limit) * Number(query.page) - + Number(query.limit) && + i < Number(query.limit) * Number(query.page) + ) { + console.log(1); + result.push(listEmployee[i]); + } + } + const total_page = Math.ceil(listEmployee.length / Number(query.limit)); + + return res.json({ + message: "Get list user success", + data: { + list_user: result, + pagination: { + page: query.page, + limit: query.limit, + total_page, + }, + }, + }); + } +}; +async function downloadAndUploadImage( + imageUrl: string, + filename: string, +): Promise { + const response = await fetch(imageUrl); + if (!response.ok) { + throw new Error(`Failed to download image from ${imageUrl}`); + } + const buffer = await response.arrayBuffer(); + const tempFilePath = path.resolve("imageUpload") + `\\${filename}.jpeg`; + // //checkTypeLink + // const fileType = await fileTypeFromBuffer(buffer) + // if (!fileType || !fileType.mime.startsWith('image/')) { + // await fs.promises.unlink(tempFilePath) + // throw new Error(`File ${tempFilePath} is not an image!!!`) + // } + await fs.promises.writeFile(tempFilePath, Buffer.from(buffer)); + try { + const url = await uploadFile(tempFilePath, filename); + return url; + } catch (err) { + throw new Error(USER_MESSAGES.CANT_UPLOAD_IMG); + } finally { + await fs.promises.unlink(tempFilePath); + } +} + +export const getLinkPicture = async ( + req: Request, + res: Response, +) => { + const imageUrl = req.body.avatar_url; + const user_Id = (req.body as User)._id; + if (!imageUrl) { + return res.json({ + message: USER_MESSAGES.CANT_FIND_THIS_IMAGE, + }); + } + const url = await downloadAndUploadImage(imageUrl, `${user_Id}`); + console.log(url); + return res.status(HTTP_STATUS.OK).json({ + message: USER_MESSAGES.UPLOAD_PICTURE_SUCCESSFULLY, + details: url, + }); +}; +async function uploadFile(path: string, filename: string): Promise { + // Upload the File + const storage = await storageRef.upload(path, { + public: true, + destination: `/uploads/${filename}`, + metadata: { + contentType: "image/jpeg", + firebaseStorageDownloadTokens: uuidv4(), + }, + }); + return storage[0].metadata.mediaLink ?? ""; +} diff --git a/src/modules/user/user.services.ts b/src/modules/user/user.services.ts index db58d00..e71fa75 100644 --- a/src/modules/user/user.services.ts +++ b/src/modules/user/user.services.ts @@ -1,495 +1,524 @@ -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, - Subscription, - TokenType, - UserRole, - UserVerifyStatus, -} from "./user.enum"; -import { - ListAccountQuery, - LogoutReqBody, - RegisterOauthReqBody, - RegisterReqBody, - TokenPayload, - UpdateMeReqBody, -} from "./user.requests"; -import User from "./user.schema"; - -class UsersService { - private decodeRefreshToken(refresh_token: string) { - return verifyToken({ - token: refresh_token, - secretOrPublickey: process.env.JWT_SECRET_REFRESH_TOKEN as string, - }); - } - - private signAccessToken( - user_id: string, - status: UserVerifyStatus, - role: UserRole, - ) { - return signToken({ - payload: { - user_id: user_id, - token_type: TokenType.Access, - status, - role, - }, - options: { expiresIn: process.env.ACCESS_TOKEN_EXPIRE_MINUTES }, - privateKey: process.env.JWT_SECRET_ACCESS_TOKEN as string, - }); - } - - private signRefreshToken({ - user_id, - status, - exp, - }: { - user_id: string; - status: UserVerifyStatus; - exp?: number; - }) { - // return signToken({ - // payload: { - // user_id: user_id, - // token_type: TokenType.Refresh, - // status - // }, - // options: { expiresIn: process.env.REFRESH_TOKEN_EXPIRE_DAYS }, - // privateKey: process.env.JWT_SECRET_REFRESH_TOKEN as string - // }) - if (exp) { - return signToken({ - payload: { - user_id, - token_type: TokenType.Refresh, - status, - exp, - }, - privateKey: process.env.JWT_SECRET_REFRESH_TOKEN as string, - }); - } else { - return signToken({ - payload: { - user_id, - token_type: TokenType.Refresh, - status, - }, - options: { expiresIn: process.env.REFRESH_TOKEN_EXPIRE_DAYS }, - privateKey: process.env.JWT_SECRET_REFRESH_TOKEN as string, - }); - } - } - - private signAccessAndRefreshToken( - user_id: string, - status: UserVerifyStatus, - role: UserRole, - ) { - return Promise.all([ - this.signAccessToken(user_id, status, role), - this.signRefreshToken({ user_id, status }), - ]); - } - - async checkEmailExist(email: string) { - const user = await databaseService.users.findOne({ email }); - return Boolean(user); - } - - async checkEmailIsVerified(email: string) { - const user = await databaseService.users.findOne({ - email, - status: UserVerifyStatus.Verified, - }); - return Boolean(user); - } - - async checkPhoneNumberIsVerified(phone_number: string) { - const user = await databaseService.users.findOne({ - phone_number, - status: UserVerifyStatus.Verified, - }); - return Boolean(user); - } - - // async checkUsernameExist(username: string) { - // const user = await databaseService.users.findOne({ username }) - // return Boolean(user) - // } - - async findUserByEmail(email: string) { - const user = await databaseService.users.findOne({ email }); - return user; - } - - async findUserByPhone(phone_number: string) { - const user = await databaseService.users.findOne({ phone_number }); - return user; - } - - async checkPhoneNumberExist(phone_number: string) { - const user = await databaseService.users.findOne({ phone_number }); - return Boolean(user); - } - - async checkPasswordExist(password: string) { - password = hashPassword(password); - const user = await databaseService.users.findOne({ password }); - return Boolean(user); - } - - async findUserByID(user_id: string) { - const user = await databaseService.users.findOne({ - _id: new ObjectId(user_id), - }); - return user; - } - - // get all data from users collection for upsert to firebase - async findAllUser() { - const users = await databaseService.users.find().toArray(); - return users; - } - - async getMe(user_id: string) { - const user = await databaseService.users.findOne( - { _id: new ObjectId(user_id) }, - { - projection: { - password: 0, - }, - }, - ); - return user as User; - } - - async isWarning(user_id: ObjectId) { - const user = await databaseService.users.findOne({ - _id: user_id, - }); - return user?.notice === NoticeUser.Warning; - } - - async register( - payload: RegisterReqBody | RegisterOauthReqBody, - provider?: string, - ) { - const user_id = new ObjectId(); - - const [access_token, refresh_token] = - await this.signAccessAndRefreshToken( - user_id.toString(), - UserVerifyStatus.Unverified, - UserRole.Customer, - ); - - const { iat, exp } = await this.decodeRefreshToken(refresh_token); - - if (provider === "google" || provider === "facebook") { - await databaseService.users.insertOne( - new User({ - _id: user_id, - ...(omit(payload, [ - "phone_number", - ]) as RegisterOauthReqBody), - password: hashPassword(payload.password), - email: encrypt(payload.email), - status: UserVerifyStatus.Verified, - }), - ); - } else { - await databaseService.users.insertOne( - new User({ - _id: user_id, - ...(omit(payload, ["email_phone"]) as RegisterReqBody), - password: hashPassword(payload.password), - // username: - // capitalize(payload.first_name) + - // ' ' + - // capitalizePro(payload.last_name), - first_name: capitalize(payload.first_name), - last_name: capitalizePro(payload.last_name), - email: payload.email?.length ? encrypt(payload.email) : "", - phone_number: payload.phone_number?.length - ? encrypt(payload.phone_number) - : "", - }), - ); - } - - await databaseService.refreshTokens.insertOne( - new RefreshToken({ - token: refresh_token, - user_id: new ObjectId(user_id), - iat, - exp, - }), - ); - - return { access_token, refresh_token }; - } - - async login({ - user_id, - status, - role, - }: { - user_id: string; - status: UserVerifyStatus; - role: UserRole; - }) { - const [access_token, refresh_token] = - await this.signAccessAndRefreshToken(user_id, status, role); - - const { iat, exp } = await this.decodeRefreshToken(refresh_token); - - await databaseService.refreshTokens.insertOne( - new RefreshToken({ - token: refresh_token, - user_id: new ObjectId(user_id), - iat, - exp, - }), - ); - - return { access_token, refresh_token }; - } - - async sendForgotPasswordOTPByEmail(email: string) { - // send otp to email - const otp = otpGenerator.generate(6, { - upperCaseAlphabets: false, - lowerCaseAlphabets: false, - specialChars: false, - }); - - const result = await otpService.sendEmail({ - email, - otp, - kind: OTP_KIND.PasswordRecovery, - }); - - return { otp_id: result.insertedId, otp: otp }; - } - - async sendForgotPasswordOTPByPhone(phone_number: string) { - // send otp to phone - const otp = otpGenerator.generate(6, { - upperCaseAlphabets: false, - lowerCaseAlphabets: false, - specialChars: false, - }); - - const result = await otpService.sendPhone({ - phone_number, - otp, - kind: OTP_KIND.PasswordRecovery, - }); - - return { otp_id: result.insertedId, otp: otp }; - } - - async sendVerifyAccountOTPByEmail(email: string) { - // send otp to email - const otp = otpGenerator.generate(6, { - upperCaseAlphabets: false, - lowerCaseAlphabets: false, - specialChars: false, - }); - - const result = await otpService.sendEmail({ - email, - otp, - kind: OTP_KIND.VerifyAccount, - }); - - return { otp_id: result.insertedId, otp: otp }; - } - - async sendVerifyAccountOTPByPhone(phone_number: string) { - // send otp to phone - const otp = otpGenerator.generate(6, { - upperCaseAlphabets: false, - lowerCaseAlphabets: false, - specialChars: false, - }); - - const result = await otpService.sendPhone({ - phone_number, - otp, - kind: OTP_KIND.VerifyAccount, - }); - - return { otp_id: result.insertedId, otp: otp }; - } - - async disableOTP(user_id: ObjectId) { - await otpService.checkExistOtp(user_id); - return true; - } - - async resetPassword(user_id: ObjectId, password: string) { - const hashedPassword = hashPassword(password); - await databaseService.users.updateOne( - { _id: user_id }, - { $set: { password: hashedPassword } }, - ); - await this.disableOTP(user_id); - - return true; - } - - async verifyAccount(user_id: ObjectId) { - await databaseService.users.updateOne( - { _id: user_id }, - { $set: { status: UserVerifyStatus.Verified } }, - ); - - await this.disableOTP(user_id); - - 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, - }: { - user_id: string; - payload: UpdateMeReqBody; - }) { - if (payload.password) payload.password = hashPassword(payload.password); - - const user = await databaseService.users.findOneAndUpdate( - { _id: new ObjectId(user_id) }, - [ - { - $set: { - ...payload, - updated_at: "$$NOW", - }, - }, - ], - { - returnDocument: "after", - projection: { - password: 0, - email_verify_token: 0, - forgot_password_token: 0, - }, - }, - ); - - return user; - } - - async logout({ refresh_token }: LogoutReqBody) { - await databaseService.refreshTokens.deleteOne({ - token: refresh_token, - }); - } - - async refreshToken(refresh_token: string, payload: TokenPayload) { - const { user_id, status, exp, role } = payload; - const [access_token, new_refresh_token] = await Promise.all([ - this.signAccessToken(user_id, status, role), - this.signRefreshToken({ user_id, status, exp }), - ]); - const { iat } = await this.decodeRefreshToken(refresh_token); - //xóa và cập nhật lại refresh token mới vào database - await databaseService.refreshTokens.deleteOne({ token: refresh_token }); - await databaseService.refreshTokens.insertOne( - new RefreshToken({ - user_id: new ObjectId(user_id), - token: new_refresh_token, - exp, - iat, - }), - ); - return { access_token, refresh_token: new_refresh_token }; - } - - async getListCustomer() { - const listUser = await databaseService.users - .find( - {}, - { - projection: { - password: 0, - updated_at: 0, - created_at: 0, - notice: 0, - wrongPasswordTimes: 0, - reasonBanned: 0, - subscription: 0, - }, - }, - ) - .toArray(); - const result = listUser.map((user) => { - if (user.email && user.phone_number) { - const email = decrypt(user.email); - const phoneNumber = decrypt(user.phone_number); - return { ...user, email: email, phone_number: phoneNumber }; - } else if (user.email) { - const email = decrypt(user.email); - return { ...user, email: email }; - } else { - const phoneNumber = decrypt(user.phone_number); - return { ...user, phone_number: phoneNumber }; - } - }); - return result; - } -} - -const usersService = new UsersService(); -export default usersService; +import "dotenv/config"; +import { capitalize, omit } from "lodash"; +import { ObjectId } from "mongodb"; +import otpGenerator from "otp-generator"; +import { UserAvatarInfo, 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, + Subscription, + TokenType, + UserRole, + UserVerifyStatus, +} from "./user.enum"; +import { + ListAccountQuery, + LogoutReqBody, + RegisterOauthReqBody, + RegisterReqBody, + TokenPayload, + UpdateMeReqBody, +} from "./user.requests"; +import User from "./user.schema"; + +class UsersService { + private decodeRefreshToken(refresh_token: string) { + return verifyToken({ + token: refresh_token, + secretOrPublickey: process.env.JWT_SECRET_REFRESH_TOKEN!, + }); + } + + private signAccessToken( + user_id: string, + status: UserVerifyStatus, + role: UserRole, + ) { + return signToken({ + payload: { + user_id: user_id, + token_type: TokenType.Access, + status, + role, + }, + options: { expiresIn: process.env.ACCESS_TOKEN_EXPIRE_MINUTES }, + privateKey: process.env.JWT_SECRET_ACCESS_TOKEN!, + }); + } + + private signRefreshToken({ + user_id, + status, + exp, + }: { + user_id: string; + status: UserVerifyStatus; + exp?: number; + }) { + // return signToken({ + // payload: { + // user_id: user_id, + // token_type: TokenType.Refresh, + // status + // }, + // options: { expiresIn: process.env.REFRESH_TOKEN_EXPIRE_DAYS }, + // privateKey: process.env.JWT_SECRET_REFRESH_TOKEN as string + // }) + if (exp) { + return signToken({ + payload: { + user_id, + token_type: TokenType.Refresh, + status, + exp, + }, + privateKey: process.env.JWT_SECRET_REFRESH_TOKEN!, + }); + } else { + return signToken({ + payload: { + user_id, + token_type: TokenType.Refresh, + status, + }, + options: { expiresIn: process.env.REFRESH_TOKEN_EXPIRE_DAYS }, + privateKey: process.env.JWT_SECRET_REFRESH_TOKEN!, + }); + } + } + + private signAccessAndRefreshToken( + user_id: string, + status: UserVerifyStatus, + role: UserRole, + ) { + return Promise.all([ + this.signAccessToken(user_id, status, role), + this.signRefreshToken({ user_id, status }), + ]); + } + + async checkEmailExist(email: string) { + const user = await databaseService.users.findOne({ email }); + return Boolean(user); + } + + async checkEmailIsVerified(email: string) { + const user = await databaseService.users.findOne({ + email, + status: UserVerifyStatus.Verified, + }); + return Boolean(user); + } + + async checkPhoneNumberIsVerified(phone_number: string) { + const user = await databaseService.users.findOne({ + phone_number, + status: UserVerifyStatus.Verified, + }); + return Boolean(user); + } + + // async checkUsernameExist(username: string) { + // const user = await databaseService.users.findOne({ username }) + // return Boolean(user) + // } + + async findUserByEmail(email: string) { + const user = await databaseService.users.findOne({ email }); + return user; + } + + async findUserByPhone(phone_number: string) { + const user = await databaseService.users.findOne({ phone_number }); + return user; + } + + async checkPhoneNumberExist(phone_number: string) { + const user = await databaseService.users.findOne({ phone_number }); + return Boolean(user); + } + + async checkPasswordExist(password: string) { + password = hashPassword(password); + const user = await databaseService.users.findOne({ password }); + return Boolean(user); + } + + async findUserByID(user_id: string) { + const user = await databaseService.users.findOne({ + _id: new ObjectId(user_id), + }); + return user; + } + + // get all data from users collection for upsert to firebase + async findAllUser() { + const users = await databaseService.users.find().toArray(); + return users; + } + + async getAllAccountIDAndAvatarUrl(): Promise { + const users = await databaseService.users + .find({}, { projection: { _id: 1, avatar_url: 1 } }) + .toArray(); + return users; + } + + async updateAvatarUrl( + user_id: string, + avatar_url: string, + ): Promise { + const user = await this.findUserByID(user_id); + + try { + if (!user) { + throw new Error("User not found"); + } + + const { modifiedCount } = await databaseService.users.updateOne( + { _id: new ObjectId(user_id) }, + { $set: { avatar_url } }, + ); + + return modifiedCount > 0; + } catch (error) { + throw new Error("Fail to update avatar url"); + } + } + + async getMe(user_id: string) { + const user = await databaseService.users.findOne( + { _id: new ObjectId(user_id) }, + { + projection: { + password: 0, + }, + }, + ); + return user as User; + } + + async isWarning(user_id: ObjectId) { + const user = await databaseService.users.findOne({ + _id: user_id, + }); + return user?.notice === NoticeUser.Warning; + } + + async register( + payload: RegisterReqBody | RegisterOauthReqBody, + provider?: string, + ) { + const user_id = new ObjectId(); + + const [access_token, refresh_token] = + await this.signAccessAndRefreshToken( + user_id.toString(), + UserVerifyStatus.Unverified, + UserRole.Customer, + ); + + const { iat, exp } = await this.decodeRefreshToken(refresh_token); + + if (provider === "google" || provider === "facebook") { + await databaseService.users.insertOne( + new User({ + _id: user_id, + ...(omit(payload, [ + "phone_number", + ]) as RegisterOauthReqBody), + password: hashPassword(payload.password), + email: encrypt(payload.email), + status: UserVerifyStatus.Verified, + }), + ); + } else { + await databaseService.users.insertOne( + new User({ + _id: user_id, + ...(omit(payload, ["email_phone"]) as RegisterReqBody), + password: hashPassword(payload.password), + // username: + // capitalize(payload.first_name) + + // ' ' + + // capitalizePro(payload.last_name), + first_name: capitalize(payload.first_name), + last_name: capitalizePro(payload.last_name), + email: payload.email?.length ? encrypt(payload.email) : "", + phone_number: payload.phone_number?.length + ? encrypt(payload.phone_number) + : "", + }), + ); + } + + await databaseService.refreshTokens.insertOne( + new RefreshToken({ + token: refresh_token, + user_id: new ObjectId(user_id), + iat, + exp, + }), + ); + + return { access_token, refresh_token }; + } + + async login({ + user_id, + status, + role, + }: { + user_id: string; + status: UserVerifyStatus; + role: UserRole; + }) { + const [access_token, refresh_token] = + await this.signAccessAndRefreshToken(user_id, status, role); + + const { iat, exp } = await this.decodeRefreshToken(refresh_token); + + await databaseService.refreshTokens.insertOne( + new RefreshToken({ + token: refresh_token, + user_id: new ObjectId(user_id), + iat, + exp, + }), + ); + + return { access_token, refresh_token }; + } + + async sendForgotPasswordOTPByEmail(email: string) { + // send otp to email + const otp = otpGenerator.generate(6, { + upperCaseAlphabets: false, + lowerCaseAlphabets: false, + specialChars: false, + }); + + const result = await otpService.sendEmail({ + email, + otp, + kind: OTP_KIND.PasswordRecovery, + }); + + return { otp_id: result.insertedId, otp: otp }; + } + + async sendForgotPasswordOTPByPhone(phone_number: string) { + // send otp to phone + const otp = otpGenerator.generate(6, { + upperCaseAlphabets: false, + lowerCaseAlphabets: false, + specialChars: false, + }); + + const result = await otpService.sendPhone({ + phone_number, + otp, + kind: OTP_KIND.PasswordRecovery, + }); + + return { otp_id: result.insertedId, otp: otp }; + } + + async sendVerifyAccountOTPByEmail(email: string) { + // send otp to email + const otp = otpGenerator.generate(6, { + upperCaseAlphabets: false, + lowerCaseAlphabets: false, + specialChars: false, + }); + + const result = await otpService.sendEmail({ + email, + otp, + kind: OTP_KIND.VerifyAccount, + }); + + return { otp_id: result.insertedId, otp: otp }; + } + + async sendVerifyAccountOTPByPhone(phone_number: string) { + // send otp to phone + const otp = otpGenerator.generate(6, { + upperCaseAlphabets: false, + lowerCaseAlphabets: false, + specialChars: false, + }); + + const result = await otpService.sendPhone({ + phone_number, + otp, + kind: OTP_KIND.VerifyAccount, + }); + + return { otp_id: result.insertedId, otp: otp }; + } + + async disableOTP(user_id: ObjectId) { + await otpService.checkExistOtp(user_id); + return true; + } + + async resetPassword(user_id: ObjectId, password: string) { + const hashedPassword = hashPassword(password); + await databaseService.users.updateOne( + { _id: user_id }, + { $set: { password: hashedPassword } }, + ); + await this.disableOTP(user_id); + + return true; + } + + async verifyAccount(user_id: ObjectId) { + await databaseService.users.updateOne( + { _id: user_id }, + { $set: { status: UserVerifyStatus.Verified } }, + ); + + await this.disableOTP(user_id); + + 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, + }: { + user_id: string; + payload: UpdateMeReqBody; + }) { + if (payload.password) payload.password = hashPassword(payload.password); + + const user = await databaseService.users.findOneAndUpdate( + { _id: new ObjectId(user_id) }, + [ + { + $set: { + ...payload, + updated_at: "$$NOW", + }, + }, + ], + { + returnDocument: "after", + projection: { + password: 0, + email_verify_token: 0, + forgot_password_token: 0, + }, + }, + ); + + return user; + } + + async logout({ refresh_token }: LogoutReqBody) { + await databaseService.refreshTokens.deleteOne({ + token: refresh_token, + }); + } + + async refreshToken(refresh_token: string, payload: TokenPayload) { + const { user_id, status, exp, role } = payload; + const [access_token, new_refresh_token] = await Promise.all([ + this.signAccessToken(user_id, status, role), + this.signRefreshToken({ user_id, status, exp }), + ]); + const { iat } = await this.decodeRefreshToken(refresh_token); + //xóa và cập nhật lại refresh token mới vào database + await databaseService.refreshTokens.deleteOne({ token: refresh_token }); + await databaseService.refreshTokens.insertOne( + new RefreshToken({ + user_id: new ObjectId(user_id), + token: new_refresh_token, + exp, + iat, + }), + ); + return { access_token, refresh_token: new_refresh_token }; + } + + async getListCustomer() { + const listUser = await databaseService.users + .find( + {}, + { + projection: { + password: 0, + updated_at: 0, + created_at: 0, + notice: 0, + wrongPasswordTimes: 0, + reasonBanned: 0, + subscription: 0, + }, + }, + ) + .toArray(); + const result = listUser.map((user) => { + if (user.email && user.phone_number) { + const email = decrypt(user.email); + const phoneNumber = decrypt(user.phone_number); + return { ...user, email: email, phone_number: phoneNumber }; + } else if (user.email) { + const email = decrypt(user.email); + return { ...user, email: email }; + } else { + const phoneNumber = decrypt(user.phone_number); + return { ...user, phone_number: phoneNumber }; + } + }); + return result; + } +} + +const usersService = new UsersService(); +export default usersService; diff --git a/src/utils/getAllDataUser.ts b/src/utils/getAllDataUser.ts index 3d7b08b..9ff1060 100644 --- a/src/utils/getAllDataUser.ts +++ b/src/utils/getAllDataUser.ts @@ -1,9 +1,83 @@ -import usersService from "~/modules/user/user.services"; -(async function () { - try { - const data = await usersService.findAllUser(); - console.log(data); - } catch (err) { - console.log(err); - } -})(); +import axios from "axios"; +import usersService from "~/modules/user/user.services"; + +//https://storage.googleapis.com/download/storage/v1/b/nodejs-uploadpictureavatar.appspot.com/o/%2Fuploads%2F6679183417a257d1c2c91cac?generation=1720452962084222&alt=media +const responseFirebasePattern = + /^https:\/\/storage\.googleapis\.com\/download\/storage\/v1\/b\/nodejs-uploadpictureavatar\.appspot\.com\/o\/%2Fuploads%2F[a-zA-Z0-9]+(\?generation=\d+&alt=media)?$/; + +interface ResponseDataUploadImage { + data: { + message: string; + details: string; + }; +} + +(async function (): Promise { + try { + const data = await usersService.getAllAccountIDAndAvatarUrl(); + + // if have avatar_url, then i will send a request upload to firebase storage + // if not, i will skip this step + + const listAccountHaveAvatar = data.filter( + (item) => + item.avatar_url !== "" && + !responseFirebasePattern.test(item.avatar_url), + ); + + if (listAccountHaveAvatar.length === 0) { + console.log("No account's avatar need to convert to Firebase"); + return; + } + + console.log( + "Account have avatar need to convert to Firebase: ", + JSON.stringify(listAccountHaveAvatar, null, 2), + ); + + for (const account of listAccountHaveAvatar) { + if (account._id) { + const id = account._id.toString(); + console.time(`Processing time for account ID: ${id}`); + try { + const response: ResponseDataUploadImage = await axios.post( + "http://localhost:4000/user/getLinkPic", + { + _id: id, + avatar_url: account.avatar_url, + }, + ); + + // Assuming the response structure is as described + if ( + response.data.message === "Upload picture successfully" + ) { + // update the avatar_url in the database + await usersService.updateAvatarUrl( + id, + response.data.details, + ); + //"Request successful for account:", + console.log( + `Request successful for account: ${id} ${response.data.message} Details URL: ${response.data.details}`, + ); + } else { + // Handle unexpected response structure + console.log(`Unexpected response for account: ${id}`); + } + } catch (error) { + console.error("Request failed for account:", id, error); + } finally { + console.timeEnd(`Processing time for account ID: ${id}`); + } + } else { + console.log( + "userId is missing for account:", + JSON.stringify(account, null, 2), + ); + } + } + } catch (err) { + console.log(err); + } +})();