diff --git a/backend/src/controllers/timetable/deleteTimetable.ts b/backend/src/controllers/timetable/deleteTimetable.ts index 6a0d1f5f..931506d3 100644 --- a/backend/src/controllers/timetable/deleteTimetable.ts +++ b/backend/src/controllers/timetable/deleteTimetable.ts @@ -83,7 +83,7 @@ export const deleteTimetable = async (req: Request, res: Response) => { headers: { "Content-Type": "application/json", }, - body: JSON.stringify({ id: timetable.id }), + body: JSON.stringify({ id: req.params.id }), }); if (!res.ok) { const resJson = await res.json(); diff --git a/backend/src/controllers/timetable/searchTimetable.ts b/backend/src/controllers/timetable/searchTimetable.ts new file mode 100644 index 00000000..6466830a --- /dev/null +++ b/backend/src/controllers/timetable/searchTimetable.ts @@ -0,0 +1,43 @@ +import { Request, Response } from "express"; +import { z } from "zod"; +import { namedNonEmptyStringType } from "../../../../lib/src/zodFieldTypes.js"; +import { env } from "../../config/server.js"; +import { Timetable } from "../../entity/entities.js"; +import { validate } from "../../middleware/zodValidateRequest.js"; + +const searchTimetableSchema = z.object({ + query: z.object({ + query: namedNonEmptyStringType("query"), + }), +}); + +export const searchTimetableValidator = validate(searchTimetableSchema); + +export const searchTimetable = async (req: Request, res: Response) => { + try { + const { query } = req.query; + + const searchServiceURL = `${env.SEARCH_SERVICE_URL}/timetable/search?query=${query}`; + + const response = await fetch(searchServiceURL, { + method: "GET", + headers: { "Content-Type": "application/json" }, + }); + + const searchResults = await response.json(); + + if (!response.ok) { + console.log("Error while searching timetable: ", searchResults.error); + return res.status(500).json({ message: "Internal Server Error" }); + } + + const timetables = searchResults.map( + (el: { timetable: Timetable; score: string }) => el.timetable, + ); + + return res.json(timetables); + } catch (err) { + console.log(err); + return res.status(500).json({ message: "Internal Server Error" }); + } +}; diff --git a/backend/src/controllers/timetable/updateChangedTimetable.ts b/backend/src/controllers/timetable/updateChangedTimetable.ts index fb5f3663..28679bab 100644 --- a/backend/src/controllers/timetable/updateChangedTimetable.ts +++ b/backend/src/controllers/timetable/updateChangedTimetable.ts @@ -2,21 +2,26 @@ import type { Request, Response } from "express"; import { z } from "zod"; import { courseWithSectionsType, + namedNonEmptyStringType, sectionTypeList, } from "../../../../lib/src/index.js"; +import { env } from "../../config/server.js"; import { AppDataSource } from "../../db.js"; import { Course, Section, Timetable } from "../../entity/entities.js"; import { validate } from "../../middleware/zodValidateRequest.js"; +import { timetableRepository } from "../../repositories/timetableRepository.js"; import { checkForExamTimingsChange } from "../../utils/checkForChange.js"; import { checkForClassHoursClash, checkForExamHoursClash, } from "../../utils/checkForClashes.js"; +import sqids from "../../utils/sqids.js"; import { addExamTimings, removeSection } from "../../utils/updateSection.js"; import { updateSectionWarnings } from "../../utils/updateWarnings.js"; const dataSchema = z.object({ body: z.object({ + chronoSecret: namedNonEmptyStringType("chronoSecret"), course: courseWithSectionsType, }), }); @@ -25,6 +30,9 @@ export const updateChangedTimetableValidator = validate(dataSchema); export const updateChangedTimetable = async (req: Request, res: Response) => { try { + if (env.CHRONO_SECRET !== req.body.chronoSecret) { + return res.status(401).json({ message: "Chrono Secret is incorrect" }); + } // Use a transaction because we will run many dependent mutations const queryRunner = AppDataSource.createQueryRunner(); await queryRunner.connect(); @@ -220,10 +228,107 @@ export const updateChangedTimetable = async (req: Request, res: Response) => { console.log("Error while querying for course: ", err.message); return res.status(500).json({ message: "Internal Server Error" }); } - // After everything passes fine, commit the transaction await queryRunner.commitTransaction(); queryRunner.release(); + + // update course in search service + try { + const searchServiceURL = `${env.SEARCH_SERVICE_URL}/course/remove`; + const res = await fetch(searchServiceURL, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ id: course.id }), + }); + if (!res.ok) { + const resJson = await res.json(); + console.log(resJson.error); + } + } catch (err: any) { + console.log( + "Error while removing course from search service: ", + err.message, + ); + return res.status(500).json({ message: "Internal Server Error" }); + } + + try { + const searchServiceURL = `${env.SEARCH_SERVICE_URL}/course/add`; + const res = await fetch(searchServiceURL, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(course), + }); + if (!res.ok) { + const resJson = await res.json(); + console.log(resJson.error); + } + } catch (err: any) { + console.log("Error while adding course to search service: ", err.message); + return res.status(500).json({ message: "Internal Server Error" }); + } + + // update timetables in search service + for (const timetable of timetables) { + try { + const searchServiceURL = `${env.SEARCH_SERVICE_URL}/timetable/remove`; + const encodedId = sqids.encode([timetable.id]); + const res = await fetch(searchServiceURL, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ id: encodedId }), + }); + + if (!res.ok) { + const resJson = await res.json(); + console.log(resJson.error); + } + } catch (err: any) { + console.log( + "Error while removing timetable from search service: ", + err.message, + ); + return res.status(500).json({ message: "Internal Server Error" }); + } + if (!timetable.draft && !timetable.private) { + const timetableWithSections = await timetableRepository + .createQueryBuilder("timetable") + .leftJoinAndSelect("timetable.sections", "section") + .where("timetable.id=:id", { id: timetable.id }) + .getOne(); + const encodedId = sqids.encode([timetable.id]); + const timetableWithSectionsString = { + ...timetableWithSections, + id: encodedId, + }; + try { + const searchServiceURL = `${env.SEARCH_SERVICE_URL}/timetable/add`; + const res = await fetch(searchServiceURL, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(timetableWithSectionsString), + }); + if (!res.ok) { + const resJson = await res.json(); + console.log(resJson.error); + } + } catch (err: any) { + console.log( + "Error while adding timetable to search service: ", + err.message, + ); + return res.status(500).json({ message: "Internal Server Error" }); + } + } + } return res.json({ message: "Timetable successfully updated" }); } catch (err: any) { console.log(err); diff --git a/backend/src/routers/timetableRouter.ts b/backend/src/routers/timetableRouter.ts index f1df5252..e3df6361 100644 --- a/backend/src/routers/timetableRouter.ts +++ b/backend/src/routers/timetableRouter.ts @@ -30,6 +30,10 @@ import { removeSection, removeSectionValidator, } from "../controllers/timetable/removeSection.js"; +import { + searchTimetable, + searchTimetableValidator, +} from "../controllers/timetable/searchTimetable.js"; import { authenticate } from "../middleware/auth.js"; const timetableRouter = express.Router(); @@ -41,6 +45,12 @@ timetableRouter.get( getPublicTimetablesValidator, getPublicTimetables, ); +timetableRouter.get( + "/search", + authenticate, + searchTimetableValidator, + searchTimetable, +); timetableRouter.get("/:id", getTimetableByIdValidator, getTimetableById); timetableRouter.post( "/:id/delete",