Skip to content

Commit dcfcf6b

Browse files
committed
D7: Add requested fixes
Signed-off-by: SeeuSim <[email protected]>
1 parent 10db91f commit dcfcf6b

File tree

8 files changed

+100
-71
lines changed

8 files changed

+100
-71
lines changed

backend/collaboration/src/controller/room-auth-controller.ts

Lines changed: 26 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,42 @@
1-
import { sql } from 'drizzle-orm';
21
import type { Request, Response } from 'express';
32
import { StatusCodes } from 'http-status-codes';
43

5-
import { db, rooms } from '@/lib/db';
4+
import { logger } from '@/lib/utils';
5+
import { roomAuthService } from '@/service/get/room-auth-service';
66

7-
export async function authCheck(req: Request, res: Response) {
8-
const roomId = req.query.roomId as string | undefined;
9-
const userId = req.query.userId as string | undefined;
10-
const questionId = req.query.questionId as string | undefined;
7+
type QueryParams = {
8+
roomId: string;
9+
userId: string;
10+
};
1111

12-
if (!roomId || !userId || !questionId) {
13-
return {
14-
code: StatusCodes.UNPROCESSABLE_ENTITY,
15-
error: {
16-
message: 'Malformed',
17-
},
18-
};
12+
// Returns the questionId if valid.
13+
export async function authCheck(
14+
req: Request<unknown, unknown, unknown, Partial<QueryParams>>,
15+
res: Response
16+
) {
17+
const { roomId, userId } = req.query;
18+
19+
if (!roomId || !userId) {
20+
return res.status(StatusCodes.UNPROCESSABLE_ENTITY).json('Malformed request');
1921
}
2022

2123
try {
22-
const room = await db
23-
.select()
24-
.from(rooms)
25-
.where(sql`${rooms.roomId} = ${roomId} and ${rooms.questionId} = ${questionId}`)
26-
.limit(1);
27-
28-
if (room.length === 0) {
29-
return res.status(StatusCodes.NOT_FOUND).json({
30-
error: {
31-
message: 'Room not found',
32-
},
33-
});
34-
}
35-
36-
const { userId1, userId2 } = room[0];
24+
const response = await roomAuthService({
25+
roomId,
26+
userId,
27+
});
3728

38-
if (userId !== userId1 && userId !== userId2) {
39-
return res.status(StatusCodes.FORBIDDEN).json({
40-
code: StatusCodes.FORBIDDEN,
41-
error: { message: 'User is not authorized to access this room' },
42-
});
29+
if (response.data) {
30+
return res.status(response.code).json(response.data);
4331
}
4432

45-
return res.status(StatusCodes.OK).json({
46-
code: StatusCodes.OK,
47-
data: { roomId },
48-
});
33+
return res
34+
.status(response.code)
35+
.json({ error: response.error || { message: 'An error occurred.' } });
4936
} catch (error) {
50-
console.error('Error authenticating room:', error);
37+
const { name, stack, cause, message } = error as Error;
38+
logger.error('Error authenticating room: ' + JSON.stringify({ name, stack, message, cause }));
5139
return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
52-
code: StatusCodes.INTERNAL_SERVER_ERROR,
5340
error: { message: 'An error occurred while authenticating the room' },
5441
});
5542
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { and, eq } from 'drizzle-orm';
2+
import { StatusCodes } from 'http-status-codes';
3+
4+
import { db, rooms } from '@/lib/db';
5+
import { IServiceResponse } from '@/types';
6+
7+
import { IGetAuthRoomPayload } from './types';
8+
9+
export const roomAuthService = async (
10+
params: IGetAuthRoomPayload
11+
): Promise<IServiceResponse<{ questionId: number }>> => {
12+
const authedRooms = await db
13+
.select()
14+
.from(rooms)
15+
.where(and(eq(rooms.roomId, params.roomId)))
16+
.limit(1);
17+
18+
if (!authedRooms || authedRooms.length === 0) {
19+
return {
20+
code: StatusCodes.UNAUTHORIZED,
21+
error: {
22+
message: 'No room with the given ID exists',
23+
},
24+
};
25+
}
26+
27+
const authedRoom = authedRooms[0];
28+
const { userId1, userId2, questionId } = authedRoom;
29+
30+
if (![userId1, userId2].includes(params.userId)) {
31+
return {
32+
code: StatusCodes.UNAUTHORIZED,
33+
error: {
34+
message: 'No room with the given ID exists',
35+
},
36+
};
37+
}
38+
39+
return {
40+
code: StatusCodes.OK,
41+
data: {
42+
questionId,
43+
},
44+
};
45+
};

backend/collaboration/src/service/get/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,8 @@ export type IGetCollabRoomPayload = {
99
export type IGetCollabRoomResponse = IServiceResponse<{
1010
roomName: string;
1111
}>;
12+
13+
export type IGetAuthRoomPayload = {
14+
roomId: string;
15+
userId: string;
16+
};

frontend/src/components/blocks/interview/question-attempts/attempt-details/main.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,13 @@ export const AttemptDetailsDialog: FC<PropsWithChildren<AttemptDetailsPaneProps>
3131
) : (
3232
<DialogTrigger>{triggerText}</DialogTrigger>
3333
)}
34-
<DialogContent className='border-border text-primary'>
34+
<DialogContent className='border-border text-primary max-w-screen-md'>
3535
<DialogHeader>
3636
<DialogTitle className=''>
3737
Attempt&nbsp;<span className='font-mono'>{attemptId}</span>
3838
</DialogTitle>
3939
</DialogHeader>
40-
<DialogDescription>
40+
<DialogDescription className='size-full max-h-[calc(100dvh-200px)] overflow-auto'>
4141
<CodeViewer {...{ code, language }} />
4242
</DialogDescription>
4343
<DialogFooter>

frontend/src/lib/router.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export const router = createBrowserRouter([
4646
},
4747
{
4848
path: ROUTES.INTERVIEW,
49-
loader: interviewRoomLoader(queryClient),
49+
loader: interviewRoomLoader,
5050
element: <InterviewRoomContainer />,
5151
},
5252
{
Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,36 @@
1-
import { QueryClient, useSuspenseQuery } from '@tanstack/react-query';
1+
import { useSuspenseQuery } from '@tanstack/react-query';
22
import { type LoaderFunctionArgs, Navigate, useLoaderData } from 'react-router-dom';
33

44
import { usePageTitle } from '@/lib/hooks';
5-
import { questionDetailsQuery } from '@/lib/queries/question-details';
65
import { ROUTES } from '@/lib/routes';
76
import { checkRoomAuthorization } from '@/services/collab-service';
87
import { useAuthedRoute } from '@/stores/auth-store';
98

109
import InterviewRoom from './interview-room';
1110

12-
export const loader =
13-
(queryClient: QueryClient) =>
14-
async ({ params, request }: LoaderFunctionArgs) => {
15-
const roomId = params.roomId;
16-
const url = new URL(request.url);
17-
const questionId = Number.parseInt(url.searchParams.get('questionId')!);
18-
await queryClient.ensureQueryData(questionDetailsQuery(questionId));
19-
return {
20-
roomId,
21-
questionId,
22-
};
11+
export const loader = ({ params }: LoaderFunctionArgs) => {
12+
const roomId = params.roomId;
13+
14+
return {
15+
roomId,
2316
};
17+
};
2418

2519
export const InterviewRoomContainer = () => {
2620
usePageTitle(ROUTES.INTERVIEW);
27-
const { roomId, questionId } = useLoaderData() as Awaited<ReturnType<ReturnType<typeof loader>>>;
21+
const { roomId } = useLoaderData() as ReturnType<typeof loader>;
2822
const { userId } = useAuthedRoute();
2923

3024
const safeRoomId = roomId ?? '';
3125

3226
const { data: authResult, error } = useSuspenseQuery({
33-
queryKey: ['checkRoomAuthorization', safeRoomId, userId, questionId],
34-
queryFn: () => checkRoomAuthorization(safeRoomId!, userId!, questionId),
27+
queryKey: ['checkRoomAuthorization', safeRoomId, userId],
28+
queryFn: () => checkRoomAuthorization(safeRoomId, userId),
3529
});
3630

37-
if (error || !authResult?.isAuthed || !roomId) {
31+
if (error || !authResult?.isAuthed || !roomId || !authResult?.questionId) {
3832
return <Navigate to={ROUTES.HOME} />;
3933
}
4034

41-
return <InterviewRoom questionId={questionId} roomId={roomId} />;
35+
return <InterviewRoom questionId={authResult.questionId} roomId={roomId} />;
4236
};

frontend/src/routes/match/waiting-room/waiting.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,11 @@ export const WaitingRoom = ({ socketPort, setIsModalOpen }: IWaitingRoomProps) =
128128
console.log(`Received match: ${JSON.stringify(data)}`);
129129

130130
const roomId = data?.roomId;
131-
const questionId = data?.questionId;
132131
countdownRef.current = 0;
133132
clearInterval(timerRef.current!);
134133

135134
socket.close();
136-
navigate(`${ROUTES.INTERVIEW.replace(':roomId', roomId)}?questionId=${questionId}`);
135+
navigate(`${ROUTES.INTERVIEW.replace(':roomId', roomId)}`);
137136
});
138137

139138
socket.on(MATCHING_EVENT.FAILED, () => {
Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
11
import { collabApiGetClient } from './api-clients';
22

33
const COLLAB_SERVICE_ROUTES = {
4-
CHECK_ROOM_AUTH: '/room/auth?roomId=<roomId>&userId=<userId>&questionId=<questionId>',
4+
CHECK_ROOM_AUTH: '/room/auth',
55
};
66

7-
export const checkRoomAuthorization = (roomId: string, userId: string, questionId: number) => {
7+
export const checkRoomAuthorization = (roomId: string, userId: string) => {
8+
const searchParams = new URLSearchParams();
9+
searchParams.set('roomId', roomId);
10+
searchParams.set('userId', userId);
811
return collabApiGetClient
9-
.get(
10-
COLLAB_SERVICE_ROUTES.CHECK_ROOM_AUTH.replace(/<roomId>/, roomId)
11-
.replace(/<userId>/, userId)
12-
.replace(/<questionId>/, questionId.toString())
13-
)
12+
.get(`${COLLAB_SERVICE_ROUTES.CHECK_ROOM_AUTH}?${searchParams.toString()}`)
1413
.then((response) => {
15-
return { isAuthed: response.status < 400 };
14+
return { isAuthed: response.status < 400, questionId: response.data?.questionId };
1615
})
1716
.catch((err) => {
1817
console.log(err);
19-
return { isAuthed: false };
18+
return { isAuthed: false, questionId: undefined };
2019
});
2120
};

0 commit comments

Comments
 (0)