From f03aa8a7fa808ac1fd6f03472e8fd743fe95887d Mon Sep 17 00:00:00 2001 From: Heisjabo Date: Tue, 16 Jul 2024 15:05:46 +0200 Subject: [PATCH] Fix(chat): public and private chat - Add user profile associations on chats - fix private chat socket --- src/config/socketCofing.ts | 6 +- src/controllers/userControllers.ts | 20 ++ src/docs/swagger.ts | 3 +- src/docs/users.ts | 262 ++++++++++++++------------- src/routes/userRoutes.ts | 38 +--- src/sequelize/models/privateChats.ts | 29 +-- src/services/privateChat.service.ts | 58 ++++-- src/services/user.service.ts | 16 ++ 8 files changed, 248 insertions(+), 184 deletions(-) diff --git a/src/config/socketCofing.ts b/src/config/socketCofing.ts index d1b77bd..c10633c 100644 --- a/src/config/socketCofing.ts +++ b/src/config/socketCofing.ts @@ -20,7 +20,6 @@ let users = new Map(); users.set(userId, socket.id) } - }) socket.on('disconnect', () => { io.emit('removed'); @@ -48,8 +47,9 @@ let users = new Map(); }); } -export const getSocketIdOfUser =( userId:string ) =>{ - return users.get(userId) + +export const getSocketIdOfUser =( userId: string ) =>{ + return users.get(JSON.stringify(userId)) } export default socket; diff --git a/src/controllers/userControllers.ts b/src/controllers/userControllers.ts index 016f01b..1c4b4a4 100644 --- a/src/controllers/userControllers.ts +++ b/src/controllers/userControllers.ts @@ -42,6 +42,26 @@ export const fetchAllUsers = async (req: Request, res: Response) => { }); } }; +export const fetchUserById = async (req: Request, res: Response) => { + try { + // @ts-ignore + const userId = req.user?.id; + const user = await userService.getUserById(Number(userId)); + if (!user) { + return res.status(404).json({ + message: "user not found", + }); + } else { + res.status(200).json(user); + } + } catch (error: any) { + console.log(error) + res.status(500).json({ + message: "Internal server error", + error: error.message, + }); + } +}; export const userLogin = async (req: Request, res: Response) => { const { email, password } = req.body; diff --git a/src/docs/swagger.ts b/src/docs/swagger.ts index 37024eb..db1d631 100644 --- a/src/docs/swagger.ts +++ b/src/docs/swagger.ts @@ -20,6 +20,7 @@ import { updateForgotPassword, verifyUserAccessToken, verifyUserEmail, + getUser } from "./users"; import { getProducts, @@ -117,7 +118,7 @@ const options = { }, "/api/v1/users/me": { - get: verifyUserAccessToken, + get: getUser, }, "/api/v1/roles": { get: getRoles, diff --git a/src/docs/users.ts b/src/docs/users.ts index 6db6fdf..504afa2 100644 --- a/src/docs/users.ts +++ b/src/docs/users.ts @@ -1,5 +1,5 @@ -import { response } from "express" -import { format } from "path" +import { response } from "express"; +import { format } from "path"; export const userSchema = { type: "object", @@ -16,12 +16,12 @@ export const userSchema = { password: { type: "string", }, -} -} + }, +}; -export const loginSchema ={ +export const loginSchema = { type: "object", - properties :{ + properties: { email: { type: "string", format: "email", @@ -47,13 +47,12 @@ export const updatePasswordSchema = { }, }; - export const profileSchema = { type: "object", - properties :{ - profileImage:{ - type: "string", - format: "binary" + properties: { + profileImage: { + type: "string", + format: "binary", }, fullName: { type: "string", @@ -85,32 +84,55 @@ export const profileSchema = { country: { type: "string", }, - }, -} +}; export const getUsers = { - tags: ["Users"], - summary: "Get all users", - responses: { - 200: { - description: "OK", - content: { - "application/json": { - schema: { - type: "array", - items: { - $ref: "#/components/schemas/User", - }, + tags: ["Users"], + summary: "Get all users", + responses: { + 200: { + description: "OK", + content: { + "application/json": { + schema: { + type: "array", + items: { + $ref: "#/components/schemas/User", }, }, }, }, }, - password: { - type: "string", + }, + password: { + type: "string", + }, +}; +export const getUser = { + tags: ["Users"], + security: [{ bearerAuth: [] }], + summary: "Get my profile", + responses: { + 200: { + description: "OK", + content: { + "application/json": { + schema: { + type: "object", + $ref: "#/components/schemas/User", + profile: { + schema: { + type: "object", + $ref: "#/components/schemas/Profile", + }, + }, + }, + }, + }, }, - } + }, +}; export const createUsers = { tags: ["Users"], @@ -133,32 +155,32 @@ export const createUsers = { schema: { $ref: "#/components/schemas/User", }, - }, - } - } - } - } - - export const getProfileUser = { - tags: ["Users"], - security: [{ bearerAuth: [] }], - summary: "Get a profile", - responses: { - 200: { - description: "OK", - content: { - "application/json": { - schema: { - type: "array", - items: { - $ref: "#/components/schemas/Profile", - }, - }, + }, + }, + }, + }, +}; + +export const getProfileUser = { + tags: ["Users"], + security: [{ bearerAuth: [] }], + summary: "Get a profile", + responses: { + 200: { + description: "OK", + content: { + "application/json": { + schema: { + type: "array", + items: { + $ref: "#/components/schemas/Profile", }, }, }, }, - } + }, + }, +}; export const loginAsUser = { tags: ["Users"], @@ -191,37 +213,36 @@ export const loginAsUser = { }, }, }; - export const updateProfile = { - tags: ["Users"], - security: [{bearerAuth: []}], - summary: "Update user profile", - requestBody: { - required: true, - content: { - "multipart/form-data": { - schema: { - $ref: "#/components/schemas/Profile", - }, - }, +export const updateProfile = { + tags: ["Users"], + security: [{ bearerAuth: [] }], + summary: "Update user profile", + requestBody: { + required: true, + content: { + "multipart/form-data": { + schema: { + $ref: "#/components/schemas/Profile", }, }, - responses: { - 201: { - description: "Updated", - content: { - "application/json": { - schema: { - $ref: "#/components/schemas/Profile", - }, - }, + }, + }, + responses: { + 201: { + description: "Updated", + content: { + "application/json": { + schema: { + $ref: "#/components/schemas/Profile", }, }, - 400: { - description: "Bad request", - }, }, - } - + }, + 400: { + description: "Bad request", + }, + }, +}; export const passwordUpdate = { tags: ["Users"], @@ -314,7 +335,7 @@ export const verifyUserAccessToken = { }, }; -export const updateUserRole ={ +export const updateUserRole = { tags: ["Users"], security: [{ bearerAuth: [] }], summary: "Update user role", @@ -381,8 +402,8 @@ export const changeUserAccountStatus = { }, 500: { description: "Internal Server Error", - } - } + }, + }, }; export const logUserOut = { @@ -398,10 +419,9 @@ export const logUserOut = { }, 500: { description: "Internal Server Error", - } - } - -} + }, + }, +}; export const sendResetLink = { tags: ["Users"], @@ -416,25 +436,25 @@ export const sendResetLink = { properties: { email: { type: "string", - description: "The email address of the user" - } + description: "The email address of the user", + }, }, - required: ["email"] - } - } - } + required: ["email"], + }, + }, + }, }, responses: { 200: { - description: "Password reset link sent successfully" + description: "Password reset link sent successfully", }, 400: { - description: "Bad request" + description: "Bad request", }, 500: { - description: "Internal server error" - } - } + description: "Internal server error", + }, + }, }; export const updateForgotPassword = { @@ -450,36 +470,36 @@ export const updateForgotPassword = { properties: { token: { type: "string", - description: "The reset token sent to the user's email" + description: "The reset token sent to the user's email", }, password: { type: "string", - description: "The new password" + description: "The new password", }, confirmPassword: { type: "string", - description: "Confirm new password" - } + description: "Confirm new password", + }, }, - required: ["token", "newPassword", "confirmPassword"] - } - } - } + required: ["token", "newPassword", "confirmPassword"], + }, + }, + }, }, responses: { 200: { - description: "Password updated successfully" + description: "Password updated successfully", }, 400: { - description: "Invalid or expired token" + description: "Invalid or expired token", }, 404: { - description: "User not found" + description: "User not found", }, 500: { - description: "Internal server error" - } - } + description: "Internal server error", + }, + }, }; export const verifyUserEmail = { tags: ["Users"], @@ -494,26 +514,26 @@ export const verifyUserEmail = { properties: { email: { type: "string", - description: "The user's email address" - } + description: "The user's email address", + }, }, - required: ["email"] - } - } - } + required: ["email"], + }, + }, + }, }, responses: { 200: { - description: "Verification email sent successfully" + description: "Verification email sent successfully", }, 400: { - description: "Invalid email address" + description: "Invalid email address", }, 404: { - description: "User not found" + description: "User not found", }, 500: { - description: "Internal server error" - } - } -}; \ No newline at end of file + description: "Internal server error", + }, + }, +}; diff --git a/src/routes/userRoutes.ts b/src/routes/userRoutes.ts index 743892d..e3cd533 100644 --- a/src/routes/userRoutes.ts +++ b/src/routes/userRoutes.ts @@ -1,24 +1,5 @@ import { Router } from "express"; -import { - fetchAllUsers, - createUserController, - userLogin, - updatePassword, - tokenVerification, - handleSuccess, - handleFailure, - updateProfileController, - getProfileController, - otpVerification, - updateUserRole, - changeUserAccountStatus, - logout, - sendResetLinkEmail, - resetPasswordController, - verifyUserEmailController, - verifyUserController, - fetchAllsellers, -} from "../controllers/userControllers"; +import { fetchAllUsers, createUserController, userLogin, updatePassword, tokenVerification, handleSuccess, handleFailure,updateProfileController, getProfileController, otpVerification,updateUserRole, changeUserAccountStatus, logout, sendResetLinkEmail, resetPasswordController, verifyUserEmailController, verifyUserController, fetchAllsellers, fetchUserById } from "../controllers/userControllers"; import { emailValidation, validateSchema } from "../middlewares/validator"; import { isLoggedIn } from "../middlewares/isLoggedIn"; import { passwordUpdateSchema } from "../schemas/passwordUpdate"; @@ -38,9 +19,9 @@ import { isPasswordOutOfDate } from "../middlewares/isPasswordOutOfDate"; import { isVerified } from "../middlewares/isVerified"; const userRoutes = Router(); -userRoutes.get("/", isLoggedIn, isAdmin, fetchAllUsers); -userRoutes.put("/passwordupdate", isLoggedIn, validateSchema(passwordUpdateSchema), updatePassword); -userRoutes.post("/login", emailValidation, validateSchema(logInSchema), isDisabled, isVerified, userLogin); +userRoutes.get("/",isLoggedIn, fetchAllUsers); +userRoutes.put("/passwordupdate", isLoggedIn, validateSchema(passwordUpdateSchema), updatePassword) +userRoutes.post("/login", emailValidation,validateSchema(logInSchema),isDisabled,isVerified,userLogin); userRoutes.post("/register", emailValidation, validateSchema(signUpSchema), createUserController); userRoutes.put("/passwordupdate", isLoggedIn, validateSchema(passwordUpdateSchema), updatePassword); userRoutes.get("/sellers", fetchAllsellers); @@ -67,11 +48,10 @@ userRoutes.get("/auth/google", authenticateUser); userRoutes.get("/auth/google/callback", callbackFn); userRoutes.get("/auth/google/success", handleSuccess); userRoutes.get("/auth/google/failure", handleFailure); -userRoutes.post("/password-reset-link", sendResetLinkEmail); -userRoutes.patch("/reset-password", resetPasswordController); -userRoutes.post("/verify-user-email", verifyUserEmailController); -userRoutes.get("/verify-user", verifyUserController); - -userRoutes.get("/me", verifyToken); +userRoutes.post('/password-reset-link', sendResetLinkEmail); +userRoutes.patch('/reset-password', resetPasswordController); +userRoutes.post('/verify-user-email', verifyUserEmailController); +userRoutes.get('/verify-user', verifyUserController); +userRoutes.get("/me", isLoggedIn, fetchUserById); export default userRoutes; diff --git a/src/sequelize/models/privateChats.ts b/src/sequelize/models/privateChats.ts index 379f4ee..7cf1297 100644 --- a/src/sequelize/models/privateChats.ts +++ b/src/sequelize/models/privateChats.ts @@ -56,17 +56,22 @@ PrivateChat.init({ modelName:"PrivateChats" }); -User.hasMany(PrivateChat,{ - foreignKey: 'userId' -}) -PrivateChat.belongsTo(User,{ - foreignKey: 'userId' -}) -User.hasMany(PrivateChat,{ - foreignKey: 'receiverId' -}) -PrivateChat.belongsTo(User,{ - foreignKey: 'receiverId' -}) +User.hasMany(PrivateChat, { + foreignKey: 'userId', + as: 'sentChats' +}); +PrivateChat.belongsTo(User, { + foreignKey: 'userId', + as: 'sender' +}); + +User.hasMany(PrivateChat, { + foreignKey: 'receiverId', + as: 'receivedChats' +}); +PrivateChat.belongsTo(User, { + foreignKey: 'receiverId', + as: 'receiver' +}); export default PrivateChat; \ No newline at end of file diff --git a/src/services/privateChat.service.ts b/src/services/privateChat.service.ts index 2b5a0a4..ebf5ceb 100644 --- a/src/services/privateChat.service.ts +++ b/src/services/privateChat.service.ts @@ -3,6 +3,7 @@ import PrivateChat from "../sequelize/models/privateChats"; import Message from "../sequelize/models/messages"; import { findUserById } from "./user.service"; import {Op} from "sequelize"; +import Profile from "../sequelize/models/profiles"; export const createPrivateChat = async (userId: number, receiverId: number) => { try { @@ -48,27 +49,48 @@ export const CreatePrivateMessage = async(message:any) => { } }; -export const getAllUserPrivateChats = async(userId: number) => { - try{ - const userChats = await PrivateChat.findAll({ - where: { - [Op.or]: [ - {userId: userId}, - {receiverId: userId} - ]}, - include: [ +export const getAllUserPrivateChats = async (userId: number) => { + try { + const userChats = await PrivateChat.findAll({ + where: { + [Op.or]: [ + { userId: userId }, + { receiverId: userId } + ] + }, + include: [ + { + model: Message, + as: 'messages' + }, + { + model: User, + as: 'sender', + include: [ { - model: Message, - as: "messages" - }] + model: Profile, + as: 'profile' + } + ] + }, + { + model: User, + as: 'receiver', + include: [ + { + model: Profile, + as: 'profile' + } + ] } - ); - return userChats + ] + }); + return userChats; + } catch (error) { + console.log(error) + // throw new Error(`Error while Fetching your Chats: ${error}`); } - catch(error){ - throw new Error(`Error while Fetching your Chats: ${error}`); - } -}; + }; export const getUserToUserPrivateMessages = async (userId: number, receiverId: number) =>{ try{ diff --git a/src/services/user.service.ts b/src/services/user.service.ts index 391f462..03ffd13 100644 --- a/src/services/user.service.ts +++ b/src/services/user.service.ts @@ -33,6 +33,7 @@ export const getAllUsers = async () => { try { const users = await User.findAll({ attributes: ['id', 'name', 'username', 'email','lastPasswordUpdateTime', 'roleId', 'createdAt', 'updatedAt','isActive'], + include: [ {model: Profile, as: "profile" }] }); if (users.length === 0) { console.log("no user"); @@ -43,6 +44,21 @@ export const getAllUsers = async () => { throw new Error(error.message); } }; +export const getUserById = async (id: number) => { + try { + const user = await User.findByPk(id, { + // attributes: ['id', 'name', 'username', 'email','lastPasswordUpdateTime', 'roleId', 'createdAt', 'updatedAt','isActive'], + include: [ {model: Profile, as: "profile" }] + }); + if (!user) { + return null; + } + return user; + } catch (error: any) { + // console.log(error.message); + throw new Error(error); + } +}; export const loggedInUser = async (email: string) => { try {