-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #12 from ad956/fix/pusher-chat
💬 feat(chats): Add real-time chat functionality with UI and backend updates
- Loading branch information
Showing
20 changed files
with
841 additions
and
350 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import dbConfig from "@utils/db"; | ||
import { NextResponse } from "next/server"; | ||
import { Message } from "@models/index"; | ||
import { Types } from "mongoose"; | ||
import { errorHandler, STATUS_CODES } from "@utils/index"; | ||
|
||
export async function POST(req: Request) { | ||
try { | ||
const authHeader = req.headers.get("Authorization"); | ||
|
||
// Authorization check | ||
if (!authHeader) { | ||
return errorHandler( | ||
"Authorization header is missing", | ||
STATUS_CODES.UNAUTHORIZED | ||
); | ||
} | ||
|
||
await dbConfig(); | ||
const { roomId, userId } = await req.json(); | ||
|
||
if (!roomId || !userId) { | ||
return errorHandler( | ||
"roomId and userId are required", | ||
STATUS_CODES.BAD_REQUEST | ||
); | ||
} | ||
|
||
// mark all unread messages in the room as read | ||
await Message.updateMany( | ||
{ | ||
roomId: new Types.ObjectId(roomId), | ||
senderId: { $ne: new Types.ObjectId(userId) }, | ||
isRead: false, | ||
}, | ||
{ | ||
isRead: true, | ||
} | ||
); | ||
|
||
return NextResponse.json({ success: true }); | ||
} catch (error) { | ||
console.error("Error marking messages as read:", error); | ||
return errorHandler( | ||
"Failed to mark messages as read", | ||
STATUS_CODES.SERVER_ERROR | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import dbConfig from "@utils/db"; | ||
import { NextResponse } from "next/server"; | ||
import { Patient, Doctor, Message, Room } from "@models/index"; | ||
import { pusherServer } from "@lib/pusher"; | ||
import { Types } from "mongoose"; | ||
import { errorHandler, STATUS_CODES } from "@utils/index"; | ||
|
||
export async function GET(req: Request) { | ||
try { | ||
const authHeader = req.headers.get("Authorization"); | ||
|
||
// Authorization check | ||
if (!authHeader) { | ||
return errorHandler( | ||
"Authorization header is missing", | ||
STATUS_CODES.UNAUTHORIZED | ||
); | ||
} | ||
|
||
await dbConfig(); | ||
const { searchParams } = new URL(req.url); | ||
const roomId = searchParams.get("roomId"); | ||
const page = parseInt(searchParams.get("page") || "1"); | ||
const limit = parseInt(searchParams.get("limit") || "50"); | ||
|
||
if (!roomId) { | ||
return errorHandler("roomId is required", STATUS_CODES.BAD_REQUEST); | ||
} | ||
|
||
const messages = await Message.find({ roomId: new Types.ObjectId(roomId) }) | ||
.sort({ createdAt: -1 }) | ||
.skip((page - 1) * limit) | ||
.limit(limit) | ||
.populate("senderId", "firstname lastname profile") | ||
.sort({ createdAt: 1 }); | ||
|
||
return NextResponse.json(messages); | ||
} catch (error) { | ||
console.error("Error fetching messages:", error); | ||
return errorHandler("Failed to fetch messages", STATUS_CODES.SERVER_ERROR); | ||
} | ||
} | ||
|
||
export async function POST(req: Request) { | ||
try { | ||
const authHeader = req.headers.get("Authorization"); | ||
|
||
// Authorization check | ||
if (!authHeader) { | ||
return errorHandler( | ||
"Authorization header is missing", | ||
STATUS_CODES.UNAUTHORIZED | ||
); | ||
} | ||
|
||
await dbConfig(); | ||
const { roomId, senderId, senderRole, message } = await req.json(); | ||
|
||
if (!roomId || !senderId || !senderRole || !message) { | ||
return errorHandler("Missing required fields", STATUS_CODES.BAD_REQUEST); | ||
} | ||
|
||
const newMessage = await Message.create({ | ||
roomId: new Types.ObjectId(roomId), | ||
senderId: new Types.ObjectId(senderId), | ||
senderRole, | ||
message, | ||
}); | ||
|
||
// update room's lastMessage and timestamp | ||
await Room.findByIdAndUpdate(new Types.ObjectId(roomId), { | ||
lastMessage: newMessage._id, | ||
updatedAt: new Date(), | ||
}); | ||
|
||
// trigger Pusher event | ||
await pusherServer.trigger(`chat-${roomId}`, "new-message", newMessage); | ||
|
||
return NextResponse.json(newMessage); | ||
} catch (error) { | ||
console.error("Error sending message:", error); | ||
return errorHandler("Failed to send message", STATUS_CODES.SERVER_ERROR); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
import { NextResponse } from "next/server"; | ||
import dbConfig from "@utils/db"; | ||
import { Room } from "@models/chat"; | ||
import { Types } from "mongoose"; | ||
import { errorHandler, STATUS_CODES } from "@utils/index"; | ||
|
||
export async function GET(req: Request) { | ||
try { | ||
const authHeader = req.headers.get("Authorization"); | ||
|
||
// Authorization check | ||
if (!authHeader) { | ||
return errorHandler( | ||
"Authorization header is missing", | ||
STATUS_CODES.UNAUTHORIZED | ||
); | ||
} | ||
|
||
await dbConfig(); | ||
const { searchParams } = new URL(req.url); | ||
const userId = searchParams.get("userId"); | ||
const role = searchParams.get("role"); | ||
|
||
if (!userId || !role) { | ||
return errorHandler("Missing userId or role", STATUS_CODES.BAD_REQUEST); | ||
} | ||
|
||
// find all rooms where the user is a participant | ||
const rooms = await Room.find({ | ||
participants: { | ||
$elemMatch: { userId: new Types.ObjectId(userId), role }, | ||
}, | ||
}) | ||
.populate([ | ||
{ | ||
path: "participants.userId", | ||
match: { role: { $ne: role } }, // get the other participant's info | ||
select: "firstname lastname profile specialty", | ||
}, | ||
{ | ||
path: "lastMessage", | ||
select: "message createdAt isRead", | ||
}, | ||
]) | ||
.sort({ updatedAt: -1 }); | ||
|
||
return NextResponse.json(rooms); | ||
} catch (error) { | ||
console.error("Error fetching chat rooms:", error); | ||
return errorHandler( | ||
"Failed to fetch chat rooms", | ||
STATUS_CODES.SERVER_ERROR | ||
); | ||
} | ||
} | ||
|
||
export async function POST(req: Request) { | ||
try { | ||
const authHeader = req.headers.get("Authorization"); | ||
|
||
// Authorization check | ||
if (!authHeader) { | ||
return errorHandler( | ||
"Authorization header is missing", | ||
STATUS_CODES.UNAUTHORIZED | ||
); | ||
} | ||
|
||
await dbConfig(); | ||
const { patientId, doctorId } = await req.json(); | ||
|
||
if (!patientId || !doctorId) { | ||
return errorHandler( | ||
"Both patientId and doctorId are required", | ||
STATUS_CODES.BAD_REQUEST | ||
); | ||
} | ||
|
||
// check if room already exists | ||
const existingRoom = await Room.findOne({ | ||
participants: { | ||
$all: [ | ||
{ | ||
$elemMatch: { | ||
userId: new Types.ObjectId(patientId), | ||
role: "Patient", | ||
}, | ||
}, | ||
{ | ||
$elemMatch: { | ||
userId: new Types.ObjectId(doctorId), | ||
role: "Doctor", | ||
}, | ||
}, | ||
], | ||
}, | ||
}); | ||
|
||
if (existingRoom) { | ||
return NextResponse.json(existingRoom); | ||
} | ||
|
||
// create new room | ||
const room = await Room.create({ | ||
participants: [ | ||
{ userId: patientId, role: "Patient" }, | ||
{ userId: doctorId, role: "Doctor" }, | ||
], | ||
}); | ||
|
||
return NextResponse.json(room); | ||
} catch (error) { | ||
console.error("Error creating chat room:", error); | ||
return errorHandler( | ||
"Failed to create chat room", | ||
STATUS_CODES.SERVER_ERROR | ||
); | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.