diff --git a/controllers/level.js b/controllers/level.js new file mode 100644 index 0000000..2eeae64 --- /dev/null +++ b/controllers/level.js @@ -0,0 +1,397 @@ +import response from "../response.js"; +import models from "../models/index.js"; +import { + clearFileBuffers, + saveFileToDisk, + generateHash, +} from "../middlewares/uploadLevel.js"; +import fs from "fs"; +import path from "path"; +import crypto from "crypto"; + +export const getAllLevels = async (req, res) => { + try { + const levels = await models.Level.findAll(); + response(200, levels, "Success", res); + } catch (error) { + console.log(error); + response(500, null, "Error retrieving levels data!", res); + } +}; + +export const getAllLevelById = async (req, res) => { + try { + const { id } = req.params; + const level = await models.Level.findByPk(id); + + if (!level) { + return response(404, null, "Level not found", res); + } + + response(200, level, "Success", res); + } catch (error) { + console.log(error); + res.status(500).json({ message: "Internal Server Error" }); + } +}; + +export const getLevels = async (req, res) => { + try { + const levels = await models.Level.findAll({ + attributes: { + exclude: ["route1", "route2", "route3", "route4"], + }, + }); + + response(200, levels, "Success", res); + } catch (error) { + console.log(error); + res.status(500).json({ message: "Internal Server Error" }); + } +}; + +export const getLevelById = async (req, res) => { + try { + const { id } = req.params; + const level = await models.Level.findByPk(id, { + attributes: { + exclude: ["route1", "route2", "route3", "route4"], + }, + }); + + if (!level) { + return response(404, null, "Level not found", res); + } + + response(200, level, "Success", res); + } catch (error) { + console.log(error); + res.status(500).json({ message: "Internal Server Error" }); + } +}; + +export const createLevel = async (req, res) => { + const { title, subject_id, topic_id, is_pretest, content, youtube } = + req.body; + + // Files to be saved if everything else is okay + const { video, audio, image } = req.filesToSave || {}; + + // Validate title + if (!title) { + clearFileBuffers({ video, audio, image }); + return response(400, null, "Title is required", res); + } + + // Validate subject_id + if (!subject_id) { + clearFileBuffers({ video, audio, image }); + return response(400, null, "Subject ID is required", res); + } + + // Validate topic_id + if (!topic_id) { + clearFileBuffers({ video, audio, image }); + return response(400, null, "Topic ID is required", res); + } + + try { + // Check if the title already exists under the same topic_id + const existingLevel = await models.Level.findOne({ + where: { title, topic_id }, + }); + + if (existingLevel) { + clearFileBuffers({ video, audio, image }); + return response( + 409, + null, + "A level with this title already exists under this topic", + res + ); // 409 Conflict + } + + // Save files to disk + const videoFilename = video + ? saveFileToDisk(video, "video", title, topic_id, subject_id) + : null; + const audioFilename = audio + ? saveFileToDisk(audio, "audio", title, topic_id, subject_id) + : null; + const imageFilename = image + ? saveFileToDisk(image, "image", title, topic_id, subject_id) + : null; + + // Create the new level + const newLevel = await models.Level.create({ + title, + subject_id, + topic_id, + is_pretest: is_pretest || 0, + content, + video: videoFilename, + audio: audioFilename, + image: imageFilename, + youtube, + route1: 0, + route2: 0, + route3: 0, + route4: 0, + }); + + // Update routes with the newly created level's ID + await newLevel.update({ + route1: newLevel.id, + route2: newLevel.id, + route3: newLevel.id, + route4: newLevel.id, + }); + + response(201, newLevel, "Level created successfully", res); + } catch (error) { + console.log(error); + clearFileBuffers({ video, audio, image }); + return response(500, null, "Internal Server Error", res); + } +}; + +export const updateLevelById = async (req, res) => { + const { id } = req.params; + const { title, subject_id, topic_id, is_pretest, content, youtube } = + req.body; + + // Files to be saved if everything else is okay + const { video, audio, image } = req.filesToSave || {}; + + try { + // Find the existing level by ID + const level = await models.Level.findByPk(id); + + if (!level) { + clearFileBuffers({ video, audio, image }); + return response(404, null, "Level not found", res); + } + + // Check if a level with the same title under the same topic already exists + if (title && topic_id) { + const existingLevel = await models.Level.findOne({ + where: { + title, + topic_id, + id: { [models.Sequelize.Op.ne]: id }, // Exclude the current level from the check + }, + }); + + if (existingLevel) { + clearFileBuffers({ video, audio, image }); + return response( + 409, + null, + "A level with this title already exists under this topic", + res + ); // 409 Conflict + } + } + + // Update level fields + if (title) level.title = title; + if (subject_id) level.subject_id = subject_id; + if (topic_id) level.topic_id = topic_id; + if (is_pretest !== undefined) level.is_pretest = is_pretest; + if (content) level.content = content; + if (youtube) level.youtube = youtube; + + // Handle video update + if (video) { + if (level.video) { + const oldVideoPath = path.join( + "public/uploads/level/video", + level.video + ); + if (fs.existsSync(oldVideoPath)) { + fs.unlinkSync(oldVideoPath); + } + } + level.video = saveFileToDisk( + video, + "video", + title || level.title, + topic_id || level.topic_id, + subject_id || level.subject_id + ); + } + + // Handle audio update + if (audio) { + if (level.audio) { + const oldAudioPath = path.join( + "public/uploads/level/audio", + level.audio + ); + if (fs.existsSync(oldAudioPath)) { + fs.unlinkSync(oldAudioPath); + } + } + level.audio = saveFileToDisk( + audio, + "audio", + title || level.title, + topic_id || level.topic_id, + subject_id || level.subject_id + ); + } + + // Handle image update + if (image) { + if (level.image) { + const oldImagePath = path.join( + "public/uploads/level/image", + level.image + ); + if (fs.existsSync(oldImagePath)) { + fs.unlinkSync(oldImagePath); + } + } + level.image = saveFileToDisk( + image, + "image", + title || level.title, + topic_id || level.topic_id, + subject_id || level.subject_id + ); + } + + await level.save(); + + response(200, level, "Level updated successfully", res); + } catch (error) { + console.log(error); + clearFileBuffers({ video, audio, image }); + return response(500, null, "Internal Server Error", res); + } +}; + +export const deleteLevelById = async (req, res) => { + const { id } = req.params; + + try { + // Find the existing level by ID + const level = await models.Level.findByPk(id); + + if (!level) { + return response(404, null, "Level not found", res); + } + + // Delete associated files from disk if they exist + const deleteFile = (filePath) => { + if (fs.existsSync(filePath)) { + fs.unlinkSync(filePath); + } + }; + + if (level.video) { + const videoPath = path.join("public/uploads/level/video", level.video); + deleteFile(videoPath); + } + + if (level.audio) { + const audioPath = path.join("public/uploads/level/audio", level.audio); + deleteFile(audioPath); + } + + if (level.image) { + const imagePath = path.join("public/uploads/level/image", level.image); + deleteFile(imagePath); + } + + // Delete the level from the database + await level.destroy(); + + response(200, null, "Level deleted successfully", res); + } catch (error) { + console.log(error); + return response(500, null, "Internal Server Error", res); + } +}; + +export const getRoutes = async (req, res) => { + try { + const levels = await models.Level.findAll({ + attributes: { + exclude: [ + "subject_id", + "topic_id", + "is_pretest", + "content", + "video", + "audio", + "image", + "youtube", + "ts_entri", + ], + }, + }); + + response(200, levels, "Success", res); + } catch (error) { + console.log(error); + res.status(500).json({ message: "Internal Server Error" }); + } +}; + +export const getRouteById = async (req, res) => { + try { + const { id } = req.params; + const level = await models.Level.findByPk(id, { + attributes: { + exclude: [ + "subject_id", + "topic_id", + "is_pretest", + "content", + "video", + "audio", + "image", + "youtube", + "ts_entri", + ], + }, + }); + + if (!level) { + return response(404, null, "Level not found", res); + } + + response(200, level, "Success", res); + } catch (error) { + console.log(error); + res.status(500).json({ message: "Internal Server Error" }); + } +}; + +export const updateRouteById = async (req, res) => { + const { id } = req.params; + const { route1, route2, route3, route4 } = req.body; + + try { + // Find the existing level by ID + const level = await models.Level.findByPk(id); + + if (!level) { + return response(404, null, "Level not found", res); + } + + // Update only the route fields + await level.update({ + route1: route1 !== undefined ? route1 : level.route1, + route2: route2 !== undefined ? route2 : level.route2, + route3: route3 !== undefined ? route3 : level.route3, + route4: route4 !== undefined ? route4 : level.route4, + }); + + response(200, level, "Routes updated successfully", res); + } catch (error) { + console.log(error); + return response(500, null, "Internal Server Error", res); + } +}; diff --git a/controllers/subject.js b/controllers/subject.js index 1b8d458..102be1e 100644 --- a/controllers/subject.js +++ b/controllers/subject.js @@ -2,6 +2,10 @@ import response from "../response.js"; import models from "../models/index.js"; import fs from "fs"; import path from "path"; +import { + clearFileBuffers, + saveFileToDisk, +} from "../middlewares/uploadSubject.js"; export const getSubjects = async (req, res) => { try { @@ -30,29 +34,42 @@ export const getSubjectById = async (req, res) => { }; export const createSubject = async (req, res) => { - const { name, description, icon, thumbnail } = req.body; + const { name, description } = req.body; + + // Files to be saved if everything else is okay + const { icon, thumbnail } = req.filesToSave || {}; // Validate name if (!name) { + clearFileBuffers({ icon, thumbnail }); return response(400, null, "Name is required", res); } // Validate description if (!description) { + clearFileBuffers({ icon, thumbnail }); return response(400, null, "Description is required", res); } try { + const iconFilename = icon + ? saveFileToDisk(icon, `${name}-icon`, name) + : null; + const thumbnailFilename = thumbnail + ? saveFileToDisk(thumbnail, `${name}-thumbnail`, name) + : null; + const newSubject = await models.Subject.create({ name, description, - icon, - thumbnail, + icon: iconFilename, + thumbnail: thumbnailFilename, }); response(201, newSubject, "Subject created successfully", res); } catch (error) { console.log(error); + clearFileBuffers({ icon, thumbnail }); res.status(500).json({ message: "Internal Server Error" }); } }; @@ -60,38 +77,45 @@ export const createSubject = async (req, res) => { export const updateSubjectById = async (req, res) => { const { id } = req.params; const { name, description } = req.body; - const icon = req.body.icon; - const thumbnail = req.body.thumbnail; + + // Files to be saved if everything else is okay + const { icon, thumbnail } = req.filesToSave || {}; try { const subject = await models.Subject.findByPk(id); if (!subject) { + clearFileBuffers({ icon, thumbnail }); return response(404, null, "Subject not found", res); } // Update subject fields if (name) subject.name = name; if (description) subject.description = description; + + // Handle icon update if (icon) { - // Remove old icon if it exists - if ( - subject.icon && - fs.existsSync(path.join("public/uploads", subject.icon)) - ) { - fs.unlinkSync(path.join("public/uploads", subject.icon)); + if (subject.icon) { + const oldIconPath = path.join("public/uploads/subject", subject.icon); + if (fs.existsSync(oldIconPath)) { + fs.unlinkSync(oldIconPath); + } } - subject.icon = icon; + subject.icon = saveFileToDisk(icon, `${name}-icon`, name); } + + // Handle thumbnail update if (thumbnail) { - // Remove old thumbnail if it exists - if ( - subject.thumbnail && - fs.existsSync(path.join("public/uploads", subject.thumbnail)) - ) { - fs.unlinkSync(path.join("public/uploads", subject.thumbnail)); + if (subject.thumbnail) { + const oldThumbnailPath = path.join( + "public/uploads/subject", + subject.thumbnail + ); + if (fs.existsSync(oldThumbnailPath)) { + fs.unlinkSync(oldThumbnailPath); + } } - subject.thumbnail = thumbnail; + subject.thumbnail = saveFileToDisk(thumbnail, `${name}-thumbnail`, name); } await subject.save(); @@ -99,6 +123,7 @@ export const updateSubjectById = async (req, res) => { response(200, subject, "Subject updated successfully", res); } catch (error) { console.log(error); + clearFileBuffers({ icon, thumbnail }); response(500, null, "Internal Server Error", res); } }; @@ -113,18 +138,23 @@ export const deleteSubjectById = async (req, res) => { return response(404, null, "Subject not found", res); } - // Remove associated files if they exist - if ( - subject.icon && - fs.existsSync(path.join("public/uploads", subject.icon)) - ) { - fs.unlinkSync(path.join("public/uploads", subject.icon)); + // Remove associated icon if it exists + if (subject.icon) { + const iconPath = path.join("public/uploads/subject", subject.icon); + if (fs.existsSync(iconPath)) { + fs.unlinkSync(iconPath); + } } - if ( - subject.thumbnail && - fs.existsSync(path.join("public/uploads", subject.thumbnail)) - ) { - fs.unlinkSync(path.join("public/uploads", subject.thumbnail)); + + // Remove associated thumbnail if it exists + if (subject.thumbnail) { + const thumbnailPath = path.join( + "public/uploads/subject", + subject.thumbnail + ); + if (fs.existsSync(thumbnailPath)) { + fs.unlinkSync(thumbnailPath); + } } await subject.destroy(); diff --git a/controllers/user.js b/controllers/user.js index 1443232..eb64407 100644 --- a/controllers/user.js +++ b/controllers/user.js @@ -4,7 +4,11 @@ import bcrypt from "bcrypt"; export const getUsers = async (req, res) => { try { - const users = await models.User.findAll(); + const users = await models.User.findAll({ + attributes: { + exclude: ["password"], + }, + }); response(200, users, "Success", res); } catch (error) { console.log(error); @@ -15,7 +19,11 @@ export const getUsers = async (req, res) => { export const getUserById = async (req, res) => { try { const { id } = req.params; - const user = await models.User.findByPk(id); + const user = await models.User.findByPk(id, { + attributes: { + exclude: ["password"], + }, + }); if (!user) { return response(404, null, "User not found", res); diff --git a/index.js b/index.js index ae53a7f..b9efeb5 100644 --- a/index.js +++ b/index.js @@ -11,6 +11,7 @@ const app = express(); app.use(cors()); app.use(cookieParser()); app.use(express.json()); +app.use(express.urlencoded({ extended: true })); app.use(router); // Serve static files from the uploads directory diff --git a/middlewares/authUser.js b/middlewares/authUser.js index 7b4ccd0..9e57e68 100644 --- a/middlewares/authUser.js +++ b/middlewares/authUser.js @@ -15,7 +15,11 @@ export const verifyLoginUser = async (req, res, next) => { const decoded = jwt.verify(accessToken, process.env.ACCESS_TOKEN_SECRET); // Cari user berdasarkan id yang ada di token - const user = await models.User.findByPk(decoded.id); + const user = await models.User.findByPk(decoded.id, { + attributes: { + exclude: ["password"], + }, + }); if (!user) { return res.status(404).json({ message: "User not found!" }); diff --git a/middlewares/checkLevel.js b/middlewares/checkLevel.js new file mode 100644 index 0000000..106487f --- /dev/null +++ b/middlewares/checkLevel.js @@ -0,0 +1,29 @@ +import models from "../models/index.js"; +import response from "../response.js"; + +export const checkMaxLevelsPerTopic = async (req, res, next) => { + const { topic_id } = req.body; + + try { + // Hitung jumlah level yang ada pada topic_id yang diberikan + const levelCount = await models.Level.count({ + where: { topic_id }, + }); + + // Periksa apakah jumlah level sudah mencapai 5 + if (levelCount >= 5) { + return response( + 400, + null, + "Cannot add more than 5 levels to a single topic", + res + ); + } + + // Lanjutkan ke middleware atau route handler berikutnya jika belum mencapai 5 + next(); + } catch (error) { + console.log(error); + return response(500, null, "Internal Server Error", res); + } +}; diff --git a/middlewares/uploadLevel.js b/middlewares/uploadLevel.js new file mode 100644 index 0000000..c157159 --- /dev/null +++ b/middlewares/uploadLevel.js @@ -0,0 +1,171 @@ +import multer from "multer"; +import crypto from "crypto"; +import path from "path"; +import fs from "fs"; +import response from "../response.js"; + +// Setup memory storage for Multer +const memoryStorage = multer.memoryStorage(); + +// Filter untuk membatasi tipe file dan ukuran file +const fileFilter = (req, file, cb) => { + const ext = path.extname(file.originalname).toLowerCase(); + + switch (file.fieldname) { + case "video": + if (ext === ".mp4") { + cb(null, true); + } else { + cb( + new Error( + "Invalid file type, only .mp4 files are allowed for video!" + ), + false + ); + } + break; + + case "audio": + if (ext === ".mp3") { + cb(null, true); + } else { + cb( + new Error( + "Invalid file type, only .mp3 files are allowed for audio!" + ), + false + ); + } + break; + + case "image": + if (ext === ".jpg" || ext === ".jpeg" || ext === ".png") { + cb(null, true); + } else { + cb( + new Error( + "Invalid file type, only .jpg, .jpeg, and .png files are allowed for image!" + ), + false + ); + } + break; + + default: + cb(new Error("Invalid file type!"), false); + } +}; + +// Set up Multer untuk menangani upload +const upload = multer({ + storage: memoryStorage, + fileFilter, + limits: { + fileSize: 100 * 1024 * 1024, // Total file size limit if needed + }, +}).fields([ + { name: "video", maxCount: 1 }, + { name: "audio", maxCount: 1 }, + { name: "image", maxCount: 1 }, +]); + +// Middleware untuk menangani upload dan pengecekan file size +const handleUpload = (req, res, next) => { + upload(req, res, (err) => { + if (err) { + return response(400, null, err.message, res); + } + + const files = req.files; + const video = files?.video ? files.video[0] : null; + const audio = files?.audio ? files.audio[0] : null; + const image = files?.image ? files.image[0] : null; + + try { + let validFiles = true; + let errorMessages = []; + + // Validate file sizes + if (video && video.size > 30 * 1024 * 1024) { + validFiles = false; + video.buffer = null; + errorMessages.push("Video file exceeds the size limit of 30MB"); + } + + if (audio && audio.size > 10 * 1024 * 1024) { + validFiles = false; + audio.buffer = null; + errorMessages.push("Audio file exceeds the size limit of 10MB"); + } + + if (image && image.size > 5 * 1024 * 1024) { + validFiles = false; + image.buffer = null; + errorMessages.push("Image file exceeds the size limit of 5MB"); + } + + if (validFiles) { + // Attach files to the request object for further processing + req.filesToSave = { video, audio, image }; + next(); + } else { + // Clear file buffers and return error response with specific messages + clearFileBuffers({ video, audio, image }); + return response(400, null, errorMessages.join("; "), res); + } + } catch (error) { + console.log(error); + clearFileBuffers({ video, audio, image }); + return response(500, null, "Internal Server Error", res); + } + }); +}; + +// Function to clear file buffers +export const clearFileBuffers = (files) => { + for (const file of Object.values(files)) { + if (file && file.buffer) { + file.buffer = null; + } + } +}; + +export const generateHash = (subjectId, filename, bufferLength) => { + return crypto + .createHash("md5") + .update(subjectId + filename + bufferLength) + .digest("hex"); +}; + +// Function to save files to disk +export const saveFileToDisk = (file, type, title, topicId, subjectId) => { + const formattedTitle = title.replace(/\s+/g, '').toLowerCase(); + const ext = path.extname(file.originalname); + const hash = generateHash(subjectId, file.originalname, file.buffer.length); + const filename = `${topicId}-${formattedTitle}-${type}-${hash}${ext}`; + + let folderPath; + switch (type) { + case "video": + folderPath = path.join("public/uploads/level/video"); + break; + case "audio": + folderPath = path.join("public/uploads/level/audio"); + break; + case "image": + folderPath = path.join("public/uploads/level/image"); + break; + default: + folderPath = path.join("public/uploads/level"); + } + + if (!fs.existsSync(folderPath)) { + fs.mkdirSync(folderPath, { recursive: true }); + } + + const filepath = path.join(folderPath, filename); + fs.writeFileSync(filepath, file.buffer); + return filename; +}; + +export default handleUpload; diff --git a/models/index.js b/models/index.js index ba2f623..4203e92 100644 --- a/models/index.js +++ b/models/index.js @@ -2,11 +2,18 @@ import { Sequelize } from "sequelize"; import UserModel from "./userModel.js"; import SubjectModel from "./subjectModel.js"; import TopicModel from "./topicModel.js"; +import LevelModel from "./levelModel.js"; + +// Impor operator Op +const Op = Sequelize.Op; const models = { User: UserModel(Sequelize.DataTypes), Subject: SubjectModel(Sequelize.DataTypes), Topic: TopicModel(Sequelize.DataTypes), + Level: LevelModel(Sequelize.DataTypes), + Sequelize, + Op, }; export default models; \ No newline at end of file diff --git a/models/levelModel.js b/models/levelModel.js new file mode 100644 index 0000000..383c1a2 --- /dev/null +++ b/models/levelModel.js @@ -0,0 +1,103 @@ +import db from "../database/db.js"; + +const LevelModel = (DataTypes) => { + const Levels = db.define( + "m_levels", + { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + validate: { + notEmpty: true, + }, + }, + title: { + type: DataTypes.STRING(100), + allowNull: false, + validate: { + notEmpty: true, + }, + }, + subject_id: { + type: DataTypes.INTEGER, + allowNull: false, + validate: { + notEmpty: true, + }, + references: { + model: 'm_subjects', // Name of the referenced table + key: 'id', // Key in the referenced table + }, + }, + topic_id: { + type: DataTypes.INTEGER, + allowNull: false, + validate: { + notEmpty: true, + }, + references: { + model: 'm_topics', // Name of the referenced table + key: 'id', // Key in the referenced table + }, + }, + is_pretest: { + type: DataTypes.TINYINT(1), + allowNull: true, + defaultValue: 0, + validate: { + min: 0, + max: 1, + }, + }, + content: { + type: DataTypes.STRING(200), + allowNull: true, + }, + video: { + type: DataTypes.STRING(200), + allowNull: true, + }, + audio: { + type: DataTypes.STRING(200), + allowNull: true, + }, + image: { + type: DataTypes.STRING(200), + allowNull: true, + }, + youtube: { + type: DataTypes.STRING(200), + allowNull: true, + }, + route1: { + type: DataTypes.INTEGER, + allowNull: false, + }, + route2: { + type: DataTypes.INTEGER, + allowNull: false, + }, + route3: { + type: DataTypes.INTEGER, + allowNull: false, + }, + route4: { + type: DataTypes.INTEGER, + allowNull: false, + }, + ts_entri: { + type: DataTypes.DATE, + allowNull: true, + defaultValue: DataTypes.NOW, + }, + }, + { + timestamps: false, // Disable Sequelize's automatic timestamp fields (createdAt, updatedAt) + tableName: "m_levels", // Ensure the table name matches the actual table name + } + ); + return Levels; +}; + +export default LevelModel; diff --git a/models/userModel.js b/models/userModel.js index 83c1c99..bb1669c 100644 --- a/models/userModel.js +++ b/models/userModel.js @@ -5,9 +5,10 @@ const UserModel = (DataTypes) => { "users", { id: { - type: DataTypes.BIGINT, + type: DataTypes.UUID, primaryKey: true, - autoIncrement: true, + defaultValue: DataTypes.UUIDV4, + allowNull: false, validate: { notEmpty: true, }, diff --git a/public/uploads/level/audio/14-level1-audio-bc8a8de59edc4085e9edff682505b158.mp3 b/public/uploads/level/audio/14-level1-audio-bc8a8de59edc4085e9edff682505b158.mp3 new file mode 100644 index 0000000..03b707c Binary files /dev/null and b/public/uploads/level/audio/14-level1-audio-bc8a8de59edc4085e9edff682505b158.mp3 differ diff --git a/public/uploads/level/image/14-level1-image-746ab602308a6ef95716b44cb76d0949.jpeg b/public/uploads/level/image/14-level1-image-746ab602308a6ef95716b44cb76d0949.jpeg new file mode 100644 index 0000000..b2db4e7 Binary files /dev/null and b/public/uploads/level/image/14-level1-image-746ab602308a6ef95716b44cb76d0949.jpeg differ diff --git a/public/uploads/level/video/14-level1-video-0b5c3cdc65ce23582fc08b433263ee8d.mp4 b/public/uploads/level/video/14-level1-video-0b5c3cdc65ce23582fc08b433263ee8d.mp4 new file mode 100644 index 0000000..1e71281 Binary files /dev/null and b/public/uploads/level/video/14-level1-video-0b5c3cdc65ce23582fc08b433263ee8d.mp4 differ diff --git a/response.js b/response.js index cd5ae0b..ccc7f58 100644 --- a/response.js +++ b/response.js @@ -2,11 +2,6 @@ const response = (statusCode, data, message, res) => { res.status(statusCode).json({ payload: data, message: message, - pagination: { - prev: "", - next: "", - max: "", - }, }); }; diff --git a/routes/index.js b/routes/index.js index 81a4379..888767c 100644 --- a/routes/index.js +++ b/routes/index.js @@ -3,11 +3,13 @@ import user_routes from "./user.js"; import auth_routes from "./auth.js"; import subject_routes from "./subject.js"; import topic_routes from "./topic.js"; +import level_routes from "./level.js"; const route = express(); route.use(user_routes); route.use(auth_routes); route.use(subject_routes); route.use(topic_routes); +route.use(level_routes); export default route; diff --git a/routes/level.js b/routes/level.js new file mode 100644 index 0000000..140a104 --- /dev/null +++ b/routes/level.js @@ -0,0 +1,30 @@ +import express from "express"; +import { getAllLevels, getAllLevelById, getLevels, getLevelById, createLevel, updateLevelById, deleteLevelById, getRoutes, getRouteById, updateRouteById } from "../controllers/level.js"; +import { verifyLoginUser, adminOnly, teacherOnly } from "../middlewares/authUser.js"; +import handleUpload from '../middlewares/uploadLevel.js'; +import {checkMaxLevelsPerTopic } from '../middlewares/checkLevel.js'; + + +const router = express.Router(); + +router.get("/levels", getAllLevels); + +router.get("/levels/:id", getAllLevelById); + +router.get("/level", getLevels); + +router.get("/level/:id", getLevelById); + +router.post("/level", handleUpload, checkMaxLevelsPerTopic, createLevel); + +router.put("/level/:id", handleUpload, updateLevelById); + +router.delete("/level/:id", deleteLevelById); + +router.get("/route", getRoutes); + +router.get("/route/:id", getRouteById); + +router.put("/route/:id", updateRouteById); + +export default router \ No newline at end of file diff --git a/routes/user.js b/routes/user.js index af30c89..f473bff 100644 --- a/routes/user.js +++ b/routes/user.js @@ -5,7 +5,7 @@ import { verifyLoginUser, adminOnly, teacherOnly } from "../middlewares/authUser const router = express.Router(); -router.get("/user", verifyLoginUser, teacherOnly, getUsers); +router.get("/user", verifyLoginUser, adminOnly, getUsers); router.get("/user/:id", getUserById);