import response from "../../response.js"; import models from "../../models/index.js"; import fs from "fs"; import path from "path"; import { clearFileBuffers, saveFileToDisk, } from "../../middlewares/uploadExercise.js"; export const getExercises = async (req, res) => { try { const exercises = await models.Exercise.findAll({ where: { IS_DELETED: 0 }, include: [ { model: models.MultipleChoices, as: "multipleChoices", }, { model: models.MatchingPairs, as: "matchingPairs", }, { model: models.TrueFalse, as: "trueFalse", }, ], }); if (exercises.length === 0) { return response(404, null, "No exercises found", res); } const result = exercises.map((exercise) => { const exerciseData = { ...exercise.dataValues }; const questionType = exercise.QUESTION_TYPE; if (questionType === "MCQ") { delete exerciseData.matchingPairs; delete exerciseData.trueFalse; } else if (questionType === "MPQ") { delete exerciseData.multipleChoices; delete exerciseData.trueFalse; } else if (questionType === "TFQ") { delete exerciseData.multipleChoices; delete exerciseData.matchingPairs; } else { delete exerciseData.multipleChoices; delete exerciseData.matchingPairs; delete exerciseData.trueFalse; } return exerciseData; }); response(200, result, "Success", res); } catch (error) { console.log(error); res.status(500).json({ message: "Internal Server Error" }); } }; export const getExerciseById = async (req, res) => { try { const { id } = req.params; const exercise = await models.Exercise.findOne({ where: { ID_ADMIN_EXERCISE: id, IS_DELETED: 0 }, include: [ { model: models.MultipleChoices, as: "multipleChoices", }, { model: models.MatchingPairs, as: "matchingPairs", }, { model: models.TrueFalse, as: "trueFalse", }, ], }); if (!exercise) { return response(404, null, "Exercise not found", res); } const exerciseData = { ...exercise.dataValues }; const questionType = exercise.QUESTION_TYPE; if (questionType === "MCQ") { if (exerciseData.multipleChoices) { exerciseData.multipleChoices = exerciseData.multipleChoices.map( (choice) => choice.dataValues ); } delete exerciseData.matchingPairs; delete exerciseData.trueFalse; } else if (questionType === "MPQ") { if (exerciseData.matchingPairs) { exerciseData.matchingPairs = exerciseData.matchingPairs.map( (pair) => pair.dataValues ); } delete exerciseData.multipleChoices; delete exerciseData.trueFalse; } else if (questionType === "TFQ") { if (exerciseData.trueFalse) { exerciseData.trueFalse = exerciseData.trueFalse.map( (tf) => tf.dataValues ); } delete exerciseData.multipleChoices; delete exerciseData.matchingPairs; } else { delete exerciseData.multipleChoices; delete exerciseData.matchingPairs; delete exerciseData.trueFalse; } response(200, exerciseData, "Success", res); } catch (error) { console.log(error); res.status(500).json({ message: "Internal Server Error" }); } }; export const getExercisesForAdmin = async (req, res) => { const { page = 1, limit = 10, search = "", sort = "time" } = req.query; try { const { rows: exercises } = await models.Exercise.findAndCountAll({ where: { IS_DELETED: 0, ...(search && { [models.Op.or]: [ { "$levelExercise->levelTopic->topicSection.NAME_SECTION$": { [models.Op.like]: `%${search}%`, }, }, { "$levelExercise->levelTopic.NAME_TOPIC$": { [models.Op.like]: `%${search}%`, }, }, { "$levelExercise.NAME_LEVEL$": { [models.Op.like]: `%${search}%` }, }, { QUESTION: { [models.Op.like]: `%${search}%` } }, ], }), }, distinct: true, include: [ { model: models.Level, as: "levelExercise", attributes: ["NAME_LEVEL"], include: [ { model: models.Topic, as: "levelTopic", attributes: ["NAME_TOPIC"], include: [ { model: models.Section, as: "topicSection", attributes: ["NAME_SECTION"], }, ], }, ], }, { model: models.MultipleChoices, as: "multipleChoices", attributes: ["ANSWER_KEY"], }, { model: models.MatchingPairs, as: "matchingPairs", attributes: ["LEFT_PAIR", "RIGHT_PAIR"], }, { model: models.TrueFalse, as: "trueFalse", attributes: ["IS_TRUE"], }, ], }); const formattedExercises = exercises.map((exercise) => { const questionType = exercise.QUESTION_TYPE; let answerKey = null; const nameSection = exercise.levelExercise?.levelTopic?.topicSection?.NAME_SECTION || "Unknown Section"; const nameTopic = exercise.levelExercise?.levelTopic?.NAME_TOPIC || "Unknown Topic"; if (questionType === "MCQ" && exercise.multipleChoices.length > 0) { answerKey = exercise.multipleChoices[0].ANSWER_KEY; } else if (questionType === "MPQ" && exercise.matchingPairs.length > 0) { answerKey = exercise.matchingPairs .map((pair) => `${pair.LEFT_PAIR}-${pair.RIGHT_PAIR}`) .join(", "); } else if (questionType === "TFQ" && exercise.trueFalse.length > 0) { answerKey = exercise.trueFalse[0].IS_TRUE === 1 ? "true" : "false"; } return { ID_ADMIN_EXERCISE: exercise.ID_ADMIN_EXERCISE, NAME_SECTION: nameSection, NAME_TOPIC: nameTopic, NAME_LEVEL: exercise.levelExercise.NAME_LEVEL, TITLE: exercise.TITLE, QUESTION: exercise.QUESTION, QUESTION_TYPE: questionType, ANSWER_KEY: answerKey, TIME_ADMIN_EXC: exercise.TIME_ADMIN_EXC, }; }); const filteredExercises = formattedExercises.filter( (exercise) => exercise.NAME_SECTION !== "Unknown Section" && exercise.NAME_TOPIC !== "Unknown Topic" ); if (sort === "section") { filteredExercises.sort((a, b) => { return a.NAME_SECTION.localeCompare(b.NAME_SECTION); }); } else if (sort === "topic") { filteredExercises.sort((a, b) => { return a.NAME_TOPIC.localeCompare(b.NAME_TOPIC); }); } else if (sort === "level") { filteredExercises.sort((a, b) => { return a.NAME_LEVEL.localeCompare(b.NAME_LEVEL); }); } else if (sort === "question") { filteredExercises.sort((a, b) => { return a.QUESTION.localeCompare(b.QUESTION); }); } else if (sort === "answer") { filteredExercises.sort((a, b) => { return (a.ANSWER_KEY || "").localeCompare(b.ANSWER_KEY || ""); }); } else { filteredExercises.sort((a, b) => { return new Date(b.TIME_ADMIN_EXC) - new Date(a.TIME_ADMIN_EXC); }); } const formattedSortedExercises = filteredExercises.map((exercise) => { const formattedTimeAdminExc = new Date(exercise.TIME_ADMIN_EXC) .toLocaleString("en-GB", { hour12: false, hour: "2-digit", minute: "2-digit", day: "2-digit", month: "2-digit", year: "numeric", }) .replace(/(\d{2}\/\d{2}\/\d{4}), (\d{2}:\d{2})/, "$2 $1"); return { ...exercise, TIME_ADMIN_EXC: formattedTimeAdminExc, }; }); const paginatedExercises = formattedSortedExercises.slice( (page - 1) * limit, page * limit ); const totalPages = Math.ceil(formattedSortedExercises.length / limit); response( 200, { exercises: paginatedExercises, currentPage: parseInt(page), totalPages: totalPages, totalExercises: formattedSortedExercises.length, }, "Success", res ); } catch (error) { console.log(error); response(500, null, "Error retrieving exercises data!", res); } }; export const getExerciseByLevelId = async (req, res) => { try { const { idLevel } = req.params; const levelExists = await models.Level.findByPk(idLevel, { include: [ { model: models.Topic, as: "levelTopic", attributes: ["NAME_TOPIC"], include: [ { model: models.Section, as: "topicSection", attributes: ["NAME_SECTION"], }, ], }, ], attributes: ["NAME_LEVEL"], }); if (!levelExists) { return response(404, null, "Level not found", res); } const exercises = await models.Exercise.findAll({ where: { ID_LEVEL: idLevel, IS_DELETED: 0 }, include: [ { model: models.MultipleChoices, as: "multipleChoices" }, { model: models.MatchingPairs, as: "matchingPairs" }, { model: models.TrueFalse, as: "trueFalse" }, ], order: [["TIME_ADMIN_EXC", "ASC"]], }); if (!exercises || exercises.length === 0) { return response(200, null, "No exercises found for this level", res); } const formattedExercises = exercises.map((exercise) => { const exerciseData = { ...exercise.dataValues }; const questionType = exercise.QUESTION_TYPE; if (questionType === "MCQ") { if (exerciseData.multipleChoices) { exerciseData.multipleChoices = exerciseData.multipleChoices.map( (choice) => { const { ANSWER_KEY, ...rest } = choice.dataValues; return rest; } ); } delete exerciseData.matchingPairs; delete exerciseData.trueFalse; } else if (questionType === "MPQ") { if (exerciseData.matchingPairs) { exerciseData.matchingPairs = exerciseData.matchingPairs.map( (pair) => pair.dataValues ); } delete exerciseData.multipleChoices; delete exerciseData.trueFalse; } else if (questionType === "TFQ") { if (exerciseData.trueFalse) { exerciseData.trueFalse = exerciseData.trueFalse.map((tf) => { const { IS_TRUE, ...rest } = tf.dataValues; return rest; }); } delete exerciseData.multipleChoices; delete exerciseData.matchingPairs; } else { delete exerciseData.multipleChoices; delete exerciseData.matchingPairs; delete exerciseData.trueFalse; } return exerciseData; }); const responsePayload = { NAME_SECTION: levelExists.levelTopic.topicSection.NAME_SECTION, NAME_TOPIC: levelExists.levelTopic.NAME_TOPIC, NAME_LEVEL: levelExists.NAME_LEVEL, EXERCISES: formattedExercises, }; response(200, responsePayload, "Success", res); } catch (error) { console.log(error); res.status(500).json({ message: "Internal Server Error" }); } }; export const getExerciseByLevelIdForAdmin = async (req, res) => { try { const { idLevel } = req.params; const levelExists = await models.Level.findByPk(idLevel, { include: [ { model: models.Topic, as: "levelTopic", attributes: ["NAME_TOPIC"], include: [ { model: models.Section, as: "topicSection", attributes: ["NAME_SECTION"], }, ], }, ], attributes: ["NAME_LEVEL"], }); if (!levelExists) { return response(404, null, "Level not found", res); } const exercises = await models.Exercise.findAll({ where: { ID_LEVEL: idLevel, IS_DELETED: 0 }, include: [ { model: models.MultipleChoices, as: "multipleChoices" }, { model: models.MatchingPairs, as: "matchingPairs" }, { model: models.TrueFalse, as: "trueFalse" }, ], order: [ [ models.Sequelize.literal( "CAST(SUBSTRING_INDEX(TITLE, ' ', -1) AS UNSIGNED)" ), "ASC", ], ], }); let formattedExercises = []; if (exercises && exercises.length > 0) { formattedExercises = exercises.map((exercise) => { const exerciseData = { ...exercise.dataValues }; const questionType = exercise.QUESTION_TYPE; if (questionType === "MCQ") { if (exerciseData.multipleChoices) { exerciseData.multipleChoices = exerciseData.multipleChoices.map( (choice) => choice.dataValues ); } delete exerciseData.matchingPairs; delete exerciseData.trueFalse; } else if (questionType === "MPQ") { if (exerciseData.matchingPairs) { exerciseData.matchingPairs = exerciseData.matchingPairs.map( (pair) => pair.dataValues ); } delete exerciseData.multipleChoices; delete exerciseData.trueFalse; } else if (questionType === "TFQ") { if (exerciseData.trueFalse) { exerciseData.trueFalse = exerciseData.trueFalse.map( (tf) => tf.dataValues ); } delete exerciseData.multipleChoices; delete exerciseData.matchingPairs; } else { delete exerciseData.multipleChoices; delete exerciseData.matchingPairs; delete exerciseData.trueFalse; } return exerciseData; }); } const responsePayload = { NAME_SECTION: levelExists.levelTopic.topicSection.NAME_SECTION, NAME_TOPIC: levelExists.levelTopic.NAME_TOPIC, NAME_LEVEL: levelExists.NAME_LEVEL, EXERCISES: formattedExercises, }; response(200, responsePayload, "Success", res); } catch (error) { console.log(error); res.status(500).json({ message: "Internal Server Error" }); } }; export const createExercises = async (req, res) => { const { exercises } = req.body; if (!exercises || !Array.isArray(exercises) || exercises.length === 0) { return response(400, null, "Exercises array is required", res); } const transaction = await models.db.transaction(); try { const createdExercises = []; const levelId = exercises[0]?.ID_LEVEL; let lastExercise = await models.Exercise.findOne({ where: { ID_LEVEL: levelId }, order: [ [ models.Sequelize.literal( "CAST(SUBSTRING_INDEX(TITLE, ' ', -1) AS UNSIGNED)" ), "DESC", ], ], limit: 1, }); let lastTitleNumber = 0; if (lastExercise && lastExercise.TITLE) { const lastTitleParts = lastExercise.TITLE.split(" "); lastTitleNumber = parseInt(lastTitleParts[1], 10) || 0; } for (let i = 0; i < exercises.length; i++) { const exerciseData = exercises[i]; const { ID_LEVEL, TITLE, QUESTION, SCORE_WEIGHT, VIDEO, QUESTION_TYPE } = exerciseData; if (!ID_LEVEL) throw new Error("Level ID is required"); if (!QUESTION) throw new Error("Question is required"); if (!SCORE_WEIGHT) throw new Error("Score weight is required"); const level = await models.Level.findByPk(ID_LEVEL); if (!level) throw new Error("Level not found"); let generatedTitle = TITLE; if (!TITLE) { lastTitleNumber++; generatedTitle = `Question ${lastTitleNumber}`; } const existingExercise = await models.Exercise.findOne({ where: { ID_LEVEL, TITLE: generatedTitle }, }); if (existingExercise) { throw new Error( `An exercise with the TITLE ${generatedTitle} already exists` ); } const newExercise = await models.Exercise.create( { ID_LEVEL, TITLE: generatedTitle, QUESTION, SCORE_WEIGHT, QUESTION_TYPE, AUDIO: null, VIDEO: VIDEO || null, IMAGE: null, }, { transaction } ); const AUDIO = req.filesToSave[`AUDIO[${i}]`] || null; const IMAGE = req.filesToSave[`IMAGE[${i}]`] || null; const audioFilename = AUDIO ? saveFileToDisk( AUDIO, "AUDIO", ID_LEVEL, newExercise.ID_ADMIN_EXERCISE ) : null; const imageFilename = IMAGE ? saveFileToDisk( IMAGE, "IMAGE", ID_LEVEL, newExercise.ID_ADMIN_EXERCISE ) : null; newExercise.AUDIO = audioFilename; newExercise.IMAGE = imageFilename; await newExercise.save({ transaction }); let questionDetails = null; switch (QUESTION_TYPE) { case "MCQ": const { OPTION_A, OPTION_B, OPTION_C, OPTION_D, OPTION_E, ANSWER_KEY, } = exerciseData; if ( !OPTION_A || !OPTION_B || !OPTION_C || !OPTION_D || !OPTION_E || !ANSWER_KEY ) { throw new Error( "All options and answer key are required for Multiple Choice" ); } questionDetails = await models.MultipleChoices.create( { ID_ADMIN_EXERCISE: newExercise.ID_ADMIN_EXERCISE, OPTION_A, OPTION_B, OPTION_C, OPTION_D, OPTION_E, ANSWER_KEY, }, { transaction } ); break; case "MPQ": const { PAIRS } = exerciseData; if (!PAIRS || !Array.isArray(PAIRS) || PAIRS.length === 0) { throw new Error("At least one pair is required for Matching Pairs"); } const matchingPairsPromises = PAIRS.map((pair) => models.MatchingPairs.create( { ID_ADMIN_EXERCISE: newExercise.ID_ADMIN_EXERCISE, LEFT_PAIR: pair.LEFT_PAIR, RIGHT_PAIR: pair.RIGHT_PAIR, }, { transaction } ) ); await Promise.all(matchingPairsPromises); questionDetails = PAIRS; break; case "TFQ": const { IS_TRUE } = exerciseData; if (typeof IS_TRUE === "undefined") { throw new Error("IS_TRUE is required for True/False"); } questionDetails = await models.TrueFalse.create( { ID_ADMIN_EXERCISE: newExercise.ID_ADMIN_EXERCISE, IS_TRUE, }, { transaction } ); break; default: throw new Error("Unsupported question type"); } newExercise.dataValues.questionDetails = questionDetails; createdExercises.push(newExercise); } await transaction.commit(); response(201, createdExercises, "Exercises created successfully", res); } catch (error) { console.error(error); await transaction.rollback(); response(500, null, error.message || "Internal Server Error", res); } }; export const updateExerciseById = async (req, res) => { const { id } = req.params; const { ID_LEVEL, QUESTION, SCORE_WEIGHT, VIDEO, PAIRS } = req.body; const { IMAGE, AUDIO } = req.filesToSave || {}; const transaction = await models.db.transaction(); try { const exercise = await models.Exercise.findByPk(id, { transaction }); if (!exercise) { clearFileBuffers({ IMAGE, AUDIO }); await transaction.rollback(); return response(404, null, "Exercise not found", res); } const { QUESTION_TYPE } = exercise; if (ID_LEVEL) { const level = await models.Level.findByPk(ID_LEVEL, { transaction }); if (!level) { clearFileBuffers({ IMAGE, AUDIO }); await transaction.rollback(); return response(404, null, "Level not found", res); } exercise.ID_LEVEL = ID_LEVEL; } if (QUESTION) exercise.QUESTION = QUESTION; if (SCORE_WEIGHT) exercise.SCORE_WEIGHT = SCORE_WEIGHT; if (VIDEO) exercise.VIDEO = VIDEO; if (AUDIO) { if (exercise.AUDIO) { const oldAudioPath = path.join( process.cwd(), "media/uploads/exercise/audio", exercise.AUDIO ); if (fs.existsSync(oldAudioPath)) { fs.unlinkSync(oldAudioPath); } } exercise.AUDIO = saveFileToDisk( AUDIO, "AUDIO", ID_LEVEL || exercise.ID_LEVEL, exercise.ID_ADMIN_EXERCISE ); } if (IMAGE) { if (exercise.IMAGE) { const oldImagePath = path.join( process.cwd(), "media/uploads/exercise/image", exercise.IMAGE ); if (fs.existsSync(oldImagePath)) { fs.unlinkSync(oldImagePath); } } exercise.IMAGE = saveFileToDisk( IMAGE, "IMAGE", ID_LEVEL || exercise.ID_LEVEL, exercise.ID_ADMIN_EXERCISE ); } await exercise.save({ transaction }); let payload = { ID_ADMIN_EXERCISE: exercise.ID_ADMIN_EXERCISE, ID_LEVEL: exercise.ID_LEVEL, QUESTION: exercise.QUESTION, SCORE_WEIGHT: exercise.SCORE_WEIGHT, QUESTION_TYPE: exercise.QUESTION_TYPE, AUDIO: exercise.AUDIO, VIDEO: exercise.VIDEO, IMAGE: exercise.IMAGE, IS_DELETED: exercise.IS_DELETED, TIME_ADMIN_EXC: exercise.TIME_ADMIN_EXC, }; if (QUESTION_TYPE === "MPQ") { if (!Array.isArray(PAIRS) || PAIRS.length === 0) { await transaction.rollback(); return response( 400, null, "At least one pair is required for Matching Pairs", res ); } const existingPairs = await models.MatchingPairs.findAll({ where: { ID_ADMIN_EXERCISE: exercise.ID_ADMIN_EXERCISE }, transaction, }); const pairsToDelete = new Set( existingPairs.map((pair) => pair.ID_MATCHING_PAIRS) ); for (const pair of PAIRS) { if (pair.ID_MATCHING_PAIRS) { const existingPair = existingPairs.find( (p) => p.ID_MATCHING_PAIRS === pair.ID_MATCHING_PAIRS ); if (existingPair) { existingPair.LEFT_PAIR = pair.LEFT_PAIR; existingPair.RIGHT_PAIR = pair.RIGHT_PAIR; await existingPair.save({ transaction }); pairsToDelete.delete(existingPair.ID_MATCHING_PAIRS); } } else { await models.MatchingPairs.create( { ID_ADMIN_EXERCISE: exercise.ID_ADMIN_EXERCISE, LEFT_PAIR: pair.LEFT_PAIR, RIGHT_PAIR: pair.RIGHT_PAIR, }, { transaction } ); } } for (const pairId of pairsToDelete) { await models.MatchingPairs.destroy({ where: { ID_MATCHING_PAIRS: pairId }, transaction, }); } const updatedPairs = await models.MatchingPairs.findAll({ where: { ID_ADMIN_EXERCISE: exercise.ID_ADMIN_EXERCISE }, }); payload.matchingPairs = updatedPairs; } else if (QUESTION_TYPE === "MCQ") { const { OPTION_A, OPTION_B, OPTION_C, OPTION_D, OPTION_E, ANSWER_KEY } = req.body; const multipleChoices = await models.MultipleChoices.findOne({ where: { ID_ADMIN_EXERCISE: exercise.ID_ADMIN_EXERCISE }, transaction, }); if (!multipleChoices) { await transaction.rollback(); return response(404, null, "Multiple Choices not found", res); } if (OPTION_A) multipleChoices.OPTION_A = OPTION_A; if (OPTION_B) multipleChoices.OPTION_B = OPTION_B; if (OPTION_C) multipleChoices.OPTION_C = OPTION_C; if (OPTION_D) multipleChoices.OPTION_D = OPTION_D; if (OPTION_E) multipleChoices.OPTION_E = OPTION_E; if (ANSWER_KEY) multipleChoices.ANSWER_KEY = ANSWER_KEY; await multipleChoices.save({ transaction }); payload.multipleChoices = multipleChoices; } else if (QUESTION_TYPE === "TFQ") { const { IS_TRUE } = req.body; const trueFalse = await models.TrueFalse.findOne({ where: { ID_ADMIN_EXERCISE: exercise.ID_ADMIN_EXERCISE }, transaction, }); if (!trueFalse) { await transaction.rollback(); return response(404, null, "True/False exercise not found", res); } if (typeof IS_TRUE !== "undefined") { trueFalse.IS_TRUE = IS_TRUE; } await trueFalse.save({ transaction }); payload.trueFalse = trueFalse; } await transaction.commit(); response(200, payload, "Exercise updated successfully", res); } catch (error) { console.error(error); clearFileBuffers({ IMAGE, AUDIO }); await transaction.rollback(); response(500, null, "Internal Server Error", res); } }; export const updateExerciseTitle = async (req, res) => { const { ID_LEVEL, exercises } = req.body; const transaction = await models.db.transaction(); try { const existingExercises = await models.Exercise.findAll({ where: { IS_DELETED: 0, ID_LEVEL, }, order: [ [ models.Sequelize.literal( "CAST(SUBSTRING_INDEX(TITLE, ' ', -1) AS UNSIGNED)" ), "ASC", ], ], transaction, }); if (exercises.length !== existingExercises.length) { await transaction.rollback(); return response(400, null, "Mismatch in exercise count", res); } const sortedTitles = existingExercises.map((ex) => ex.TITLE); const updatedExercises = []; for (let i = 0; i < exercises.length; i++) { const { ID_ADMIN_EXERCISE } = exercises[i]; const newTitle = sortedTitles[i]; const exerciseToUpdate = await models.Exercise.findByPk( ID_ADMIN_EXERCISE, { transaction, } ); if (!exerciseToUpdate) { await transaction.rollback(); return response( 404, null, `Exercise with ID ${ID_ADMIN_EXERCISE} not found`, res ); } if (exerciseToUpdate.TITLE !== newTitle) { await exerciseToUpdate.update({ TITLE: newTitle }, { transaction }); updatedExercises.push({ ID_ADMIN_EXERCISE: exerciseToUpdate.ID_ADMIN_EXERCISE, TITLE: newTitle, oldTitle: exercises[i].TITLE, }); } } await transaction.commit(); response( 200, { updatedExercises }, "Exercises titles swapped successfully", res ); } catch (error) { console.error(error); await transaction.rollback(); response(500, null, "Internal Server Error", res); } }; export const deleteExerciseById = async (req, res) => { const { id } = req.params; const transaction = await models.db.transaction(); try { const exercise = await models.Exercise.findByPk(id); if (!exercise) { await transaction.rollback(); return response(404, null, "Exercise not found", res); } await exercise.update({ IS_DELETED: 1 }, { transaction }); await transaction.commit(); response(200, null, "Exercise soft-deleted successfully", res); } catch (error) { console.log(error); await transaction.rollback(); response(500, null, "Internal Server Error", res); } }; export const deleteExerciseFileById = async (req, res) => { const { id } = req.params; const { fileType } = req.body; if (!["audio", "image", "video"].includes(fileType)) { return response(400, null, "Invalid file type specified", res); } try { const exercise = await models.Exercise.findByPk(id); if (!exercise) { return response(404, null, "Exercise not found", res); } let filePath; let fileName; if (fileType === "audio" && exercise.AUDIO) { fileName = exercise.AUDIO; filePath = path.join( process.cwd(), "media/uploads/exercise/audio", fileName ); exercise.AUDIO = null; } else if (fileType === "image" && exercise.IMAGE) { fileName = exercise.IMAGE; filePath = path.join( process.cwd(), "media/uploads/exercise/image", fileName ); exercise.IMAGE = null; } else if (fileType === "video" && exercise.VIDEO) { exercise.VIDEO = null; } else { return response( 404, null, `${ fileType.charAt(0).toUpperCase() + fileType.slice(1) } file not found`, res ); } if (fs.existsSync(filePath)) { fs.unlinkSync(filePath); } await exercise.save(); response( 200, exercise, `${ fileType.charAt(0).toUpperCase() + fileType.slice(1) } file deleted successfully`, res ); } catch (error) { console.log(error); response(500, null, "Internal Server Error", res); } };