Skip to content

Commit ce4ad15

Browse files
committed
Add endpoints to create, get and delete share entries
1 parent 74b36fc commit ce4ad15

File tree

3 files changed

+229
-7
lines changed

3 files changed

+229
-7
lines changed
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
import { Request, Response } from "express";
2+
import asyncHandler from "../middleware/asyncHandler";
3+
import { supabase } from "../db/setupDb";
4+
5+
export default {
6+
/**
7+
* Create a new share entry
8+
* @route POST /api/shared
9+
*/
10+
createShare: asyncHandler(async (req: Request, res: Response) => {
11+
try {
12+
const owner_id = (req as any).user.id;
13+
const owner_email = (req as any).user.email;
14+
const { shared_email, calendar_id } = req.body;
15+
16+
if (!shared_email || !calendar_id) {
17+
return res
18+
.status(400)
19+
.json({ error: "Shared user email and calendar ID are required" });
20+
}
21+
22+
// Owner cannot share a timetable to themselve
23+
if (shared_email === owner_email) {
24+
return res
25+
.status(400)
26+
.json({ error: "Users cannot share timetable to themselves " });
27+
}
28+
29+
// Query users for shared_id using email
30+
const { data: sharedUser, error: sharedError } = await supabase.rpc(
31+
"get_user_id_by_email",
32+
{
33+
email: shared_email,
34+
}
35+
);
36+
37+
if (sharedError) {
38+
return res.status(500).json({ error: sharedError.message });
39+
}
40+
41+
if (!sharedUser) {
42+
return res
43+
.status(404)
44+
.json({ error: "User with provided email not found" });
45+
}
46+
47+
const shared_id = sharedUser[0].id;
48+
49+
// Check for the calendar exists and belongs to the owner
50+
const { data: timeTable, error: timeTableError } = await supabase
51+
.schema("timetable")
52+
.from("timetables")
53+
.select("id")
54+
.eq("id", calendar_id)
55+
.eq("user_id", owner_id)
56+
.maybeSingle();
57+
58+
if (timeTableError || !timeTable) {
59+
return res.status(404).json({
60+
error: "Timetable not found or user unauthorized to share",
61+
});
62+
}
63+
64+
// Check if the sharing has already existed
65+
const { data: existingShare, error: existingShareError } = await supabase
66+
.schema("timetable")
67+
.from("shared")
68+
.select("id")
69+
.eq("calendar_id", calendar_id)
70+
.eq("owner_id", owner_id)
71+
.eq("shared_id", shared_id)
72+
.maybeSingle();
73+
74+
if (existingShare) {
75+
return res.status(400).json({
76+
error: "This calendar has already been shared with the provided user",
77+
});
78+
}
79+
80+
// Inser the shared timetable entry
81+
const { data: shareInsert, error: shareError } = await supabase
82+
.schema("timetable")
83+
.from("shared")
84+
.insert([{ owner_id, shared_id, calendar_id }])
85+
.select("*")
86+
.single();
87+
88+
if (shareError) {
89+
return res.status(400).json({ error: shareError.message });
90+
}
91+
92+
return res.status(201).json(shareInsert);
93+
} catch (error) {
94+
return res.status(500).send({ error });
95+
}
96+
}),
97+
98+
/**
99+
* Get all share timetables for the current user
100+
* @route GET /api/shared
101+
*/
102+
103+
getShare: asyncHandler(async (req: Request, res: Response) => {
104+
try {
105+
const user_id = (req as any).user.id;
106+
107+
//Fetch all shared calendar IDs where user is the shared recipient
108+
const { data: shareData, error: sharedError } = await supabase
109+
.schema("timetable")
110+
.from("shared")
111+
.select(
112+
"calendar_id, timetables!inner(id, user_id, timetable_title, semester, favorite)"
113+
)
114+
.eq("shared_id", user_id);
115+
116+
if (sharedError) {
117+
return res.status(400).json({ error: sharedError.message });
118+
}
119+
120+
if (!shareData || shareData.length === 0) {
121+
return res
122+
.status(404)
123+
.json({ error: "No shared timetables found for this user" });
124+
}
125+
126+
return res.status(200).json(shareData);
127+
} catch (error) {
128+
return res.status(500).send({ error });
129+
}
130+
}),
131+
132+
/**
133+
* Delete all shared record for a timetable as the timetable's owner
134+
* @route DELETE /api/shared/owner/:calendar_id
135+
*/
136+
deleteOwnerShare: asyncHandler(async (req: Request, res: Response) => {
137+
try {
138+
const owner_id = (req as any).user.id;
139+
const { calendar_id } = req.params;
140+
141+
const { error: deleteError } = await supabase
142+
.schema("timetable")
143+
.from("shared")
144+
.delete()
145+
.eq("calendar_id", calendar_id)
146+
.eq("owner_id", owner_id);
147+
148+
if (deleteError) {
149+
return res.status(400).json({ error: deleteError.message });
150+
}
151+
152+
return res.status(200).send({
153+
message: "All sharing records for the timetable deleted successfully",
154+
});
155+
} catch (error) {
156+
return res.status(500).send({ error });
157+
}
158+
}),
159+
160+
/**
161+
* Delete a shared entryas shared userd
162+
* @route DELETE /api/shared/:calendar_id
163+
*/
164+
deleteShare: asyncHandler(async (req: Request, res: Response) => {
165+
try {
166+
const shared_id = (req as any).user.id;
167+
const { calendar_id } = req.params;
168+
169+
const { error: deleteError } = await supabase
170+
.schema("timetable")
171+
.from("shared")
172+
.delete()
173+
.eq("calendar_id", calendar_id)
174+
.eq("shared_id", shared_id);
175+
if (deleteError) {
176+
return res.status(400).json({ error: deleteError.message });
177+
}
178+
179+
return res
180+
.status(200)
181+
.json({ message: "Sharing record deleted successfully" });
182+
} catch (error) {
183+
return res.status(500).send({ error });
184+
}
185+
}),
186+
};

course-matrix/backend/src/controllers/timetablesController.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { Request, Response } from "express";
22
import asyncHandler from "../middleware/asyncHandler";
33
import { supabase } from "../db/setupDb";
4-
import { maybeCoerceBoolean } from "openai/core";
54

65
export default {
76
/**

course-matrix/backend/src/routes/timetableRouter.ts

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import express from "express";
22
import timetableController from "../controllers/timetablesController";
33
import eventController from "../controllers/eventsController";
44
import restrictionsController from "../controllers/restrictionsController";
5+
import sharesController from "../controllers/sharesController";
56
import { authHandler } from "../middleware/authHandler";
67

78
export const timetableRouter = express.Router();
@@ -35,7 +36,7 @@ timetableRouter.put("/:id", authHandler, timetableController.updateTimetable);
3536
timetableRouter.delete(
3637
"/:id",
3738
authHandler,
38-
timetableController.deleteTimetable,
39+
timetableController.deleteTimetable
3940
);
4041

4142
/**
@@ -53,7 +54,7 @@ timetableRouter.post("/events", authHandler, eventController.createEvent);
5354
timetableRouter.get(
5455
"/events/:calendar_id",
5556
authHandler,
56-
eventController.getEvents,
57+
eventController.getEvents
5758
);
5859

5960
/**
@@ -78,7 +79,7 @@ timetableRouter.delete("/events/:id", authHandler, eventController.deleteEvent);
7879
timetableRouter.post(
7980
"/restrictions",
8081
authHandler,
81-
restrictionsController.createRestriction,
82+
restrictionsController.createRestriction
8283
);
8384

8485
/**
@@ -89,7 +90,7 @@ timetableRouter.post(
8990
timetableRouter.get(
9091
"/restrictions/:calendar_id",
9192
authHandler,
92-
restrictionsController.getRestriction,
93+
restrictionsController.getRestriction
9394
);
9495

9596
/**
@@ -100,7 +101,7 @@ timetableRouter.get(
100101
timetableRouter.put(
101102
"/restrictions/:id",
102103
authHandler,
103-
restrictionsController.updateRestriction,
104+
restrictionsController.updateRestriction
104105
);
105106

106107
/**
@@ -111,5 +112,41 @@ timetableRouter.put(
111112
timetableRouter.delete(
112113
"/restrictions/:id",
113114
authHandler,
114-
restrictionsController.deleteRestriction,
115+
restrictionsController.deleteRestriction
116+
);
117+
118+
/**
119+
* Route to create shared entry
120+
* @route POST /api/timetables/shared
121+
* @middleware authHandler - Middleware to check if the user is authenticated
122+
*/
123+
timetableRouter.post("/shared", authHandler, sharesController.createShare);
124+
125+
/**
126+
* Route to get all shared entry for authenticated user
127+
* @route GET /api/timetables/shared
128+
* @middleware authHandler - Middleware to check if the user is authenticated
129+
*/
130+
timetableRouter.get("/shared", authHandler, sharesController.getShare);
131+
132+
/**
133+
* Route to delete all shared entries for a timetable as timetable's owner
134+
* @route DELETE /api/timetables/shared/owner/:calendar_id
135+
* @middleware authHandler - Middleware to check if the user is authenticated
136+
*/
137+
timetableRouter.delete(
138+
"/shared/owner/:calendar_id",
139+
authHandler,
140+
sharesController.deleteOwnerShare
141+
);
142+
143+
/**
144+
* Route to delete a single entry for the authneticate user
145+
* @route DELETE /api/timetables/shared/:calendar_id
146+
* @middleware authHandler - Middleware to check if the user is authenticated
147+
*/
148+
timetableRouter.delete(
149+
"/shared/:calendar_id",
150+
authHandler,
151+
sharesController.deleteShare
115152
);

0 commit comments

Comments
 (0)