Skip to content

Commit d34eeab

Browse files
authored
Merge pull request #25 from MridulDhiman/master
added Role based Access Control in Collaborative Room
2 parents 3562410 + 6b4fbeb commit d34eeab

31 files changed

+902
-200
lines changed

pnpm-lock.yaml

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/app/room/[roomId]/layout.jsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1+
import RoomNavbar from "@/src/components/common/RoomNavbar";
12
import SocketProvider from "@/src/context/SocketContext";
3+
import RoomContextProvider from "@/src/context/RoomContext";
24

35
const RoomLayout = ({ children }) => {
46
return (
5-
<div className="w-full h-screen bg-white">
6-
<SocketProvider>{children}</SocketProvider>
7+
<div className="bg-black">
8+
<RoomContextProvider>
9+
<SocketProvider>
10+
<RoomNavbar />
11+
{children}
12+
</SocketProvider>
13+
</RoomContextProvider>
714
</div>
815
);
916
};

src/app/room/[roomId]/page.jsx

Lines changed: 68 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import { redirect } from "next/navigation";
22
import CollaborativeSandpackEditor from "@/src/components/editor/CollaborativeSandpackEditor";
3-
import { templates } from "@/src/utils";
4-
import { auth } from "@clerk/nextjs/server";
5-
import { createNewRoom, fetchLatestRoomFilesState } from "@/src/lib/actions";
6-
import { logFailureCb, handleFailureCase } from "@/src/lib/utils";
3+
import { auth, currentUser } from "@clerk/nextjs/server";
4+
import { createNewRoom, fetchLatestRoomFilesState, fetchRoomMembers, inviteUserToRoom } from "@/src/lib/actions";
5+
import { logFailureCb, handleFailureCase, templates, RoomRole } from "@/src/lib/utils";
76

87

98

109
export default async function CollaborativeRoomPage({ params, searchParams }) {
1110
const { userId } = await auth();
11+
const user = await currentUser();
1212
const roomId = params["roomId"];
1313
const template = searchParams["template"];
1414
console.log("Template: ", template);
@@ -18,7 +18,7 @@ export default async function CollaborativeRoomPage({ params, searchParams }) {
1818
redirect("/");
1919
}
2020

21-
let shardDetails;
21+
let shardDetails, userRole;
2222
if (roomId === "new-room") {
2323
if (!template || !templates.includes(template)) {
2424
console.log("Template not valid");
@@ -33,36 +33,94 @@ export default async function CollaborativeRoomPage({ params, searchParams }) {
3333
src: "createNewRoom()"
3434
}, logFailureCb);
3535

36-
shardDetails = out.data.shards;
36+
shardDetails = out.data.shards?.[0];
3737
const files = out.data.files;
3838
console.log("shard details: ", shardDetails);
3939
console.log("files: ", files);
4040
shardDetails.files = files;
41+
42+
const newRoomId = shardDetails?.id;
43+
44+
let roomMembersResponse = await fetchRoomMembers(userId, newRoomId);
45+
if (!roomMembersResponse || roomMembersResponse?.error || !roomMembersResponse?.data) {
46+
console.log("could not fetch room members: "+ roomMembersResponse);
47+
if(roomMembersResponse?.error) console.log("explicit error message: " + roomMembersResponse?.error?.message || "could not fetch room members")
48+
redirect("/");
49+
}
50+
51+
console.log("members: ", roomMembersResponse.data);
52+
// Member type => [{id: number, userId: string, roomId: number, role : enum<'owner', 'editor', 'viewer'>}]
53+
let members = roomMembersResponse.data.members ?? [];
54+
const isOwner = members.some(member => member.userId === userId && member.role === RoomRole.OWNER);
55+
56+
if(isOwner) {
57+
// user is already an owner of the room, redirect to the room
58+
redirect(`/room/${newRoomId}`);
59+
}
60+
else {
61+
// user is not an owner of the room, add them as an owner
62+
const out = await inviteUserToRoom(userId, newRoomId, user.primaryEmailAddress?.emailAddress ?? "", RoomRole.OWNER);
63+
if(out?.error) {
64+
console.log("error occurred in inviteUserToRoom: ", out);
65+
redirect("/");
66+
}
67+
68+
userRole = RoomRole.OWNER;
69+
}
4170
}
4271

4372
if (roomId !== "new-room") {
4473
let out = await fetchLatestRoomFilesState(userId, roomId);
4574
if (!out || out?.error || !out?.data) {
4675
console.log("could not fetch latest room state: "+ out);
4776
if(out?.error) console.log("explicit error message: " + out?.error?.message)
48-
redirect("/your-work");
77+
redirect("/");
4978
}
5079

5180
let data = out.data;
5281
shardDetails = data.shard;
82+
83+
let roomMembersResponse = await fetchRoomMembers(userId, roomId);
84+
if (!roomMembersResponse || roomMembersResponse?.error || !roomMembersResponse?.data) {
85+
console.log("could not fetch room members: "+ roomMembersResponse);
86+
if(roomMembersResponse?.error) console.log("explicit error message: " + roomMembersResponse?.error?.message || "could not fetch room members")
87+
redirect("/");
5388
}
5489

55-
console.log("Shard details: ", shardDetails);
90+
console.log("members: ", roomMembersResponse.data);
91+
// Member type => [{id: number, userId: string, roomId: number, role : enum<'owner', 'editor', 'viewer'>}]
92+
let members = roomMembersResponse.data.members ?? [];
93+
const isMember = members.some(member => member.userId === userId);
5694

57-
const { userId: creator, isTemplate, id } = shardDetails;
95+
if(shardDetails?.userId === userId && !isMember) {
96+
// user is the creator of the room and not a member yet, add them as a "owner"
97+
const out = await inviteUserToRoom(userId, roomId, user.primaryEmailAddress?.emailAddress ?? "", RoomRole.OWNER);
98+
if(out?.error) {
99+
console.log("error occurred in inviteUserToRoom: ", out);
100+
redirect("/");
101+
}
102+
userRole = RoomRole.OWNER;
103+
}
104+
else if (!isMember && shardDetails?.userId !== userId) {
105+
// user is not a member of the room and is not the creator of the room, redirect to the home page
106+
redirect("/");
107+
}
108+
else if(isMember) {
109+
userRole = members.find(member => member.userId === userId)?.role ?? 'undefined';
110+
}
111+
}
112+
113+
console.log("Shard details: ", shardDetails);
114+
console.log("User role: ", userRole);
58115

59116
return (
60117
<>
61118
<CollaborativeSandpackEditor
62119
shardDetails={JSON.stringify(shardDetails)}
63-
template={isTemplate ? shardDetails?.templateType : "react"}
120+
template={shardDetails?.templateType}
64121
id={shardDetails?.id ?? ""}
65122
isNewShard={roomId === "new-room"}
123+
userRole={userRole}
66124
/>
67125
</>
68126
);

src/app/shard/template/[template]/page.jsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import { redirect } from "next/navigation";
2-
import { templates } from "@/src/utils";
32
import SandpackEditor from "@/src/components/editor/SandpackEditor";
43
import { auth, currentUser } from "@clerk/nextjs/server";
54
import { createShard } from "@/src/lib/actions";
6-
import { handleFailureCase, logFailureCb } from "@/src/lib/utils";
5+
import { handleFailureCase, logFailureCb, templates } from "@/src/lib/utils";
76

87

98
const page = async ({ params }) => {

src/app/try-editor/[template]/page.jsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import SandpackEditor from "@/src/components/editor/SandpackEditor";
2-
import { templates } from "@/src/utils";
3-
2+
import { templates } from "@/src/lib/utils";
43
import { redirect } from "next/navigation";
54
export const generateMetadata = ({ params }) => {
65
let template = params.template;

src/components/chat/ChatBox.jsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
"use client";
22

3-
import { useSocket } from "@/src/context/SocketContext";
43
import { useUser } from "@clerk/nextjs";
5-
import { useEffect, useRef, useState, useCallback } from "react";
4+
import { useEffect, useRef, useState } from "react";
65
import { Minimize2, Maximize2, X } from "lucide-react";
76
import { Rnd } from "react-rnd";
7+
import { useSocket } from "@/src/hooks/useSocket";
88

99
export default function ChatBox({roomId}) {
1010
const { socket, chatMessages, sendChatMessage } = useSocket();
@@ -32,7 +32,7 @@ export default function ChatBox({roomId}) {
3232

3333
const message = {
3434
text: newMessage,
35-
sender: user.fullName,
35+
sender: user.username,
3636
timestamp: new Date().toISOString(),
3737
};
3838

@@ -91,12 +91,12 @@ export default function ChatBox({roomId}) {
9191
<div
9292
key={index}
9393
className={`flex flex-col ${
94-
msg.sender === user.fullName ? "items-end" : "items-start"
94+
msg.sender === user.username ? "items-end" : "items-start"
9595
}`}
9696
>
9797
<div
9898
className={`max-w-[80%] rounded-lg p-2 ${
99-
msg.sender === user.fullName
99+
msg.sender === user.username
100100
? "bg-blue-600 text-white"
101101
: "bg-gray-700 text-white"
102102
}`}

src/components/comment/CommentMsg.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import Avatar from "react-avatar";
55
import Button from "../ui/Button";
66
import Comment from "../ui/icons/Comment";
77
import { cn } from "@/src/lib/utils";
8-
import { useActiveComment } from "@/src/context/CommentContext";
8+
import { useActiveComment } from "@/src/hooks/useActiveComment";
99

1010
const CommentMsg = ({
1111
_id,

src/components/comment/CommentTextbox.jsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import React, { useState, useRef, useEffect } from "react";
22
import { Textarea } from "../ui/textarea";
33
import Button from "../ui/Button";
4-
import { useActiveComment } from "../../context/CommentContext";
5-
import { toast } from "sonner";
64
import { useUser } from "@clerk/nextjs";
5+
import { useActiveComment } from "@/src/hooks/useActiveComment";
76

87
const CommentTextBox = () => {
98
const [input, setInput] = useState("");

src/components/common/ItemsList.jsx

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
1-
import { useRouter } from "next/navigation";
2-
import { useAuth, useUser } from "@clerk/nextjs";
1+
import { useUser } from "@clerk/nextjs";
32
import { toast } from "sonner";
43
import clsx from "clsx";
54
import Link from "next/link";
5+
import { usePathname } from "next/navigation";
6+
import { isRoomPath } from "@/src/lib/utils";
67

78
const ItemsList = () => {
89
const { isSignedIn } = useUser();
9-
const { userId } = useAuth();
10-
const router = useRouter();
10+
const pathname = usePathname();
1111
if (!isSignedIn) {
1212
toast.error("not signed in");
1313
return null;
1414
}
15+
1516

1617
const liItems = [
1718
// {
@@ -36,11 +37,12 @@ const ItemsList = () => {
3637
},
3738
];
3839

39-
const evenLiStyles = ``;
40-
return (
40+
const isRoomPathFlag = isRoomPath(pathname);
41+
42+
return (
4143
<>
4244
<ul className="rounded-sm flex gap-3">
43-
{liItems.map((item, index) => (
45+
{!isRoomPathFlag && liItems.map((item, index) => (
4446
<Link
4547
key={index}
4648
href={item.target}

0 commit comments

Comments
 (0)