refactor: user model function and exercises
|
|
@ -13,46 +13,44 @@ const transporter = nodemailer.createTransport({
|
||||||
});
|
});
|
||||||
|
|
||||||
export const registerAdmin = async (req, res) => {
|
export const registerAdmin = async (req, res) => {
|
||||||
const { name, email, password, confirmPassword } = req.body;
|
const { NAME_USERS, EMAIL, PASSWORD, CONFIRM_PASSWORD } = req.body;
|
||||||
|
|
||||||
if (!name) {
|
if (!NAME_USERS) {
|
||||||
return response(400, null, "Name is required!", res);
|
return response(400, null, "Name is required!", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!email) {
|
if (!EMAIL) {
|
||||||
return response(400, null, "Email is required!", res);
|
return response(400, null, "Email is required!", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!password) {
|
if (!PASSWORD) {
|
||||||
return response(400, null, "Password is required!", res);
|
return response(400, null, "Password is required!", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!confirmPassword) {
|
if (!CONFIRM_PASSWORD) {
|
||||||
return response(400, null, "Confirm Password is required!", res);
|
return response(400, null, "Confirm Password is required!", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (password !== confirmPassword) {
|
if (PASSWORD !== CONFIRM_PASSWORD) {
|
||||||
return response(400, null, "Passwords do not match!", res);
|
return response(400, null, "Passwords do not match!", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const salt = await bcrypt.genSalt(10);
|
const salt = await bcrypt.genSalt(10);
|
||||||
const hashedPassword = await bcrypt.hash(password, salt);
|
const hashedPassword = await bcrypt.hash(PASSWORD, salt);
|
||||||
|
|
||||||
const newUser = await models.User.create({
|
const newUser = await models.User.create({
|
||||||
NAME_USERS: name,
|
NAME_USERS: NAME_USERS,
|
||||||
EMAIL: email,
|
EMAIL: EMAIL,
|
||||||
PASSWORD: hashedPassword,
|
PASSWORD: hashedPassword,
|
||||||
ROLE: "admin",
|
ROLE: "admin",
|
||||||
PICTURE: "default-avatar.jpeg",
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const adminResponse = {
|
const adminResponse = {
|
||||||
id: newUser.ID,
|
ID: newUser.ID,
|
||||||
name: newUser.NAME_USERS,
|
NAME_USERS: newUser.NAME_USERS,
|
||||||
email: newUser.EMAIL,
|
EMAIL: newUser.EMAIL,
|
||||||
role: newUser.ROLE,
|
ROLE: newUser.ROLE,
|
||||||
picture: newUser.PICTURE,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
response(200, adminResponse, "Admin registration successful", res);
|
response(200, adminResponse, "Admin registration successful", res);
|
||||||
|
|
@ -68,29 +66,29 @@ export const registerAdmin = async (req, res) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const registerTeacher = async (req, res) => {
|
export const registerTeacher = async (req, res) => {
|
||||||
const { name, email, nip, password, confirmPassword } = req.body;
|
const { NAME_USERS, EMAIL, NIP, PASSWORD, CONFIRM_PASSWORD } = req.body;
|
||||||
|
|
||||||
if (!name) {
|
if (!NAME_USERS) {
|
||||||
return response(400, null, "Name is required!", res);
|
return response(400, null, "Name is required!", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!email) {
|
if (!EMAIL) {
|
||||||
return response(400, null, "Email is required!", res);
|
return response(400, null, "Email is required!", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!nip) {
|
if (!NIP) {
|
||||||
return response(400, null, "NIP is required for teachers!", res);
|
return response(400, null, "NIP is required for teachers!", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!password) {
|
if (!PASSWORD) {
|
||||||
return response(400, null, "Password is required!", res);
|
return response(400, null, "Password is required!", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!confirmPassword) {
|
if (!CONFIRM_PASSWORD) {
|
||||||
return response(400, null, "Confirm Password is required!", res);
|
return response(400, null, "Confirm Password is required!", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (password !== confirmPassword) {
|
if (PASSWORD !== CONFIRM_PASSWORD) {
|
||||||
return response(400, null, "Passwords do not match!", res);
|
return response(400, null, "Passwords do not match!", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -98,15 +96,14 @@ export const registerTeacher = async (req, res) => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const salt = await bcrypt.genSalt(10);
|
const salt = await bcrypt.genSalt(10);
|
||||||
const hashedPassword = await bcrypt.hash(password, salt);
|
const hashedPassword = await bcrypt.hash(PASSWORD, salt);
|
||||||
|
|
||||||
const newUser = await models.User.create(
|
const newUser = await models.User.create(
|
||||||
{
|
{
|
||||||
NAME_USERS: name,
|
NAME_USERS: NAME_USERS,
|
||||||
EMAIL: email,
|
EMAIL: EMAIL,
|
||||||
PASSWORD: hashedPassword,
|
PASSWORD: hashedPassword,
|
||||||
ROLE: "teacher",
|
ROLE: "teacher",
|
||||||
PICTURE: "default-avatar.jpeg",
|
|
||||||
},
|
},
|
||||||
{ transaction }
|
{ transaction }
|
||||||
);
|
);
|
||||||
|
|
@ -114,7 +111,7 @@ export const registerTeacher = async (req, res) => {
|
||||||
await models.Teacher.create(
|
await models.Teacher.create(
|
||||||
{
|
{
|
||||||
ID: newUser.ID,
|
ID: newUser.ID,
|
||||||
NIP: nip,
|
NIP: NIP,
|
||||||
},
|
},
|
||||||
{ transaction }
|
{ transaction }
|
||||||
);
|
);
|
||||||
|
|
@ -122,12 +119,11 @@ export const registerTeacher = async (req, res) => {
|
||||||
await transaction.commit();
|
await transaction.commit();
|
||||||
|
|
||||||
const teacherResponse = {
|
const teacherResponse = {
|
||||||
id: newUser.ID,
|
ID: newUser.ID,
|
||||||
name: newUser.NAME_USERS,
|
NAME_USERS: newUser.NAME_USERS,
|
||||||
email: newUser.EMAIL,
|
EMAIL: newUser.EMAIL,
|
||||||
nip: nip,
|
NIP: NIP,
|
||||||
role: newUser.ROLE,
|
ROLE: newUser.ROLE,
|
||||||
picture: newUser.PICTURE,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
response(200, teacherResponse, "Teacher registration successful", res);
|
response(200, teacherResponse, "Teacher registration successful", res);
|
||||||
|
|
@ -152,29 +148,29 @@ export const registerTeacher = async (req, res) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const registerStudent = async (req, res) => {
|
export const registerStudent = async (req, res) => {
|
||||||
const { name, email, nisn, password, confirmPassword } = req.body;
|
const { NAME_USERS, EMAIL, NISN, PASSWORD, CONFIRM_PASSWORD } = req.body;
|
||||||
|
|
||||||
if (!name) {
|
if (!NAME_USERS) {
|
||||||
return response(400, null, "Name is required!", res);
|
return response(400, null, "Name is required!", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!email) {
|
if (!EMAIL) {
|
||||||
return response(400, null, "Email is required!", res);
|
return response(400, null, "Email is required!", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!nisn) {
|
if (!NISN) {
|
||||||
return response(400, null, "NISN is required for students!", res);
|
return response(400, null, "NISN is required for students!", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!password) {
|
if (!PASSWORD) {
|
||||||
return response(400, null, "Password is required!", res);
|
return response(400, null, "Password is required!", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!confirmPassword) {
|
if (!CONFIRM_PASSWORD) {
|
||||||
return response(400, null, "Confirm Password is required!", res);
|
return response(400, null, "Confirm Password is required!", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (password !== confirmPassword) {
|
if (PASSWORD !== CONFIRM_PASSWORD) {
|
||||||
return response(400, null, "Passwords do not match!", res);
|
return response(400, null, "Passwords do not match!", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -182,15 +178,14 @@ export const registerStudent = async (req, res) => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const salt = await bcrypt.genSalt(10);
|
const salt = await bcrypt.genSalt(10);
|
||||||
const hashedPassword = await bcrypt.hash(password, salt);
|
const hashedPassword = await bcrypt.hash(PASSWORD, salt);
|
||||||
|
|
||||||
const newUser = await models.User.create(
|
const newUser = await models.User.create(
|
||||||
{
|
{
|
||||||
NAME_USERS: name,
|
NAME_USERS: NAME_USERS,
|
||||||
EMAIL: email,
|
EMAIL: EMAIL,
|
||||||
PASSWORD: hashedPassword,
|
PASSWORD: hashedPassword,
|
||||||
ROLE: "student",
|
ROLE: "student",
|
||||||
PICTURE: "default-avatar.jpeg",
|
|
||||||
},
|
},
|
||||||
{ transaction }
|
{ transaction }
|
||||||
);
|
);
|
||||||
|
|
@ -198,7 +193,7 @@ export const registerStudent = async (req, res) => {
|
||||||
await models.Student.create(
|
await models.Student.create(
|
||||||
{
|
{
|
||||||
ID: newUser.ID,
|
ID: newUser.ID,
|
||||||
NISN: nisn,
|
NISN: NISN,
|
||||||
},
|
},
|
||||||
{ transaction }
|
{ transaction }
|
||||||
);
|
);
|
||||||
|
|
@ -206,12 +201,11 @@ export const registerStudent = async (req, res) => {
|
||||||
await transaction.commit();
|
await transaction.commit();
|
||||||
|
|
||||||
const studentResponse = {
|
const studentResponse = {
|
||||||
id: newUser.ID,
|
ID: newUser.ID,
|
||||||
name: newUser.NAME_USERS,
|
NAME_USERS: newUser.NAME_USERS,
|
||||||
email: newUser.EMAIL,
|
EMAIL: newUser.EMAIL,
|
||||||
nisn: nisn,
|
NISN: NISN,
|
||||||
role: newUser.ROLE,
|
ROLE: newUser.ROLE,
|
||||||
picture: newUser.PICTURE,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
response(200, studentResponse, "Student registration successful", res);
|
response(200, studentResponse, "Student registration successful", res);
|
||||||
|
|
@ -236,40 +230,42 @@ export const registerStudent = async (req, res) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const loginUser = async (req, res) => {
|
export const loginUser = async (req, res) => {
|
||||||
const { email, password } = req.body;
|
const { EMAIL, PASSWORD } = req.body;
|
||||||
|
|
||||||
if (!email) {
|
if (!EMAIL) {
|
||||||
return response(400, null, "Email is required!", res);
|
return response(400, null, "Email is required!", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!password) {
|
if (!PASSWORD) {
|
||||||
return response(400, null, "Password is required!", res);
|
return response(400, null, "Password is required!", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const user = await models.User.findOne({ where: { email } });
|
const user = await models.User.findOne({ where: { EMAIL } });
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return response(404, null, "User data not found!", res);
|
return response(404, null, "User data not found!", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
const validPassword = await bcrypt.compare(password, user.PASSWORD);
|
const validPassword = await bcrypt.compare(PASSWORD, user.PASSWORD);
|
||||||
if (!validPassword) {
|
if (!validPassword) {
|
||||||
return response(401, null, "The password you entered is incorrect!", res);
|
return response(401, null, "The password you entered is incorrect!", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
const accessToken = jwt.sign(
|
const accessToken = jwt.sign(
|
||||||
{ id: user.ID },
|
{ ID: user.ID,
|
||||||
|
ROLE: user.ROLE
|
||||||
|
},
|
||||||
process.env.ACCESS_TOKEN_SECRET,
|
process.env.ACCESS_TOKEN_SECRET,
|
||||||
{ expiresIn: "6h" }
|
{ expiresIn: "6h" }
|
||||||
);
|
);
|
||||||
|
|
||||||
const userResponse = {
|
const userResponse = {
|
||||||
id: user.ID,
|
ID: user.ID,
|
||||||
name: user.NAME_USERS,
|
NAME_USERS: user.NAME_USERS,
|
||||||
email: user.EMAIL,
|
EMAIL: user.EMAIL,
|
||||||
roles: user.ROLE,
|
ROLE: user.ROLE,
|
||||||
token: `Bearer ${accessToken}`,
|
TOKEN: `Bearer ${accessToken}`,
|
||||||
};
|
};
|
||||||
|
|
||||||
response(200, userResponse, "Login successful", res);
|
response(200, userResponse, "Login successful", res);
|
||||||
|
|
@ -284,14 +280,14 @@ export const logoutUser = (req, res) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const forgotPassword = async (req, res) => {
|
export const forgotPassword = async (req, res) => {
|
||||||
const { email } = req.body;
|
const { EMAIL } = req.body;
|
||||||
|
|
||||||
if (!email) {
|
if (!EMAIL) {
|
||||||
return response(400, null, "Email is required!", res);
|
return response(400, null, "Email is required!", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const user = await models.User.findOne({ where: { EMAIL: email } });
|
const user = await models.User.findOne({ where: { EMAIL: EMAIL } });
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
return response(404, null, "Email is not registered!", res);
|
return response(404, null, "Email is not registered!", res);
|
||||||
|
|
@ -327,26 +323,26 @@ export const forgotPassword = async (req, res) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const resetPassword = async (req, res) => {
|
export const resetPassword = async (req, res) => {
|
||||||
const { token, newPassword, confirmNewPassword } = req.body;
|
const { TOKEN, NEW_PASSWORD, CONFIRM_NEW_PASSWORD } = req.body;
|
||||||
|
|
||||||
if (!token) {
|
if (!TOKEN) {
|
||||||
return response(400, null, "Token is required!", res);
|
return response(400, null, "Token is required!", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!newPassword) {
|
if (!NEW_PASSWORD) {
|
||||||
return response(400, null, "New password is required!", res);
|
return response(400, null, "New password is required!", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!confirmNewPassword) {
|
if (!CONFIRM_NEW_PASSWORD) {
|
||||||
return response(400, null, "Confirm new password is required!", res);
|
return response(400, null, "Confirm new password is required!", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newPassword !== confirmNewPassword) {
|
if (NEW_PASSWORD !== CONFIRM_NEW_PASSWORD) {
|
||||||
return response(400, null, "Passwords do not match!", res);
|
return response(400, null, "Passwords do not match!", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const decoded = jwt.verify(token, process.env.RESET_PASSWORD_SECRET);
|
const decoded = jwt.verify(TOKEN, process.env.RESET_PASSWORD_SECRET);
|
||||||
const user = await models.User.findOne({ where: { ID: decoded.id } });
|
const user = await models.User.findOne({ where: { ID: decoded.id } });
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
|
|
@ -354,7 +350,7 @@ export const resetPassword = async (req, res) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const salt = await bcrypt.genSalt(10);
|
const salt = await bcrypt.genSalt(10);
|
||||||
const hashedPassword = await bcrypt.hash(newPassword, salt);
|
const hashedPassword = await bcrypt.hash(NEW_PASSWORD, salt);
|
||||||
|
|
||||||
user.PASSWORD = hashedPassword;
|
user.PASSWORD = hashedPassword;
|
||||||
await user.save();
|
await user.save();
|
||||||
|
|
|
||||||
|
|
@ -83,17 +83,11 @@ export const getExercisesForAdmin = async (req, res) => {
|
||||||
|
|
||||||
if (questionType === "MCQ" && exercise.multipleChoices.length > 0) {
|
if (questionType === "MCQ" && exercise.multipleChoices.length > 0) {
|
||||||
answerKey = exercise.multipleChoices[0].ANSWER_KEY;
|
answerKey = exercise.multipleChoices[0].ANSWER_KEY;
|
||||||
} else if (
|
} else if (questionType === "MPQ" && exercise.matchingPairs.length > 0) {
|
||||||
questionType === "MPQ" &&
|
|
||||||
exercise.matchingPairs.length > 0
|
|
||||||
) {
|
|
||||||
answerKey = exercise.matchingPairs
|
answerKey = exercise.matchingPairs
|
||||||
.map((pair) => `${pair.LEFT_PAIR}-${pair.RIGHT_PAIR}`)
|
.map((pair) => `${pair.LEFT_PAIR}-${pair.RIGHT_PAIR}`)
|
||||||
.join(", ");
|
.join(", ");
|
||||||
} else if (
|
} else if (questionType === "TFQ" && exercise.trueFalse.length > 0) {
|
||||||
questionType === "TFQ" &&
|
|
||||||
exercise.trueFalse.length > 0
|
|
||||||
) {
|
|
||||||
answerKey = exercise.trueFalse[0].IS_TRUE === 1 ? "true" : "false";
|
answerKey = exercise.trueFalse[0].IS_TRUE === 1 ? "true" : "false";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -168,6 +162,141 @@ export const getExerciseById = async (req, res) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getExerciseByLevelId = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { idLevel } = req.params;
|
||||||
|
|
||||||
|
const levelExists = await models.Level.findByPk(idLevel);
|
||||||
|
|
||||||
|
if (!levelExists) {
|
||||||
|
return response(404, null, "Level not found", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
const exercises = await models.Exercise.findAll({
|
||||||
|
where: { ID_LEVEL: idLevel },
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: models.MultipleChoices,
|
||||||
|
as: "multipleChoices",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: models.MatchingPairs,
|
||||||
|
as: "matchingPairs",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: models.TrueFalse,
|
||||||
|
as: "trueFalse",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!exercises || exercises.length === 0) {
|
||||||
|
return response(404, 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") {
|
||||||
|
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, formattedExercises, "Success", res);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
res.status(500).json({ message: "Internal Server Error" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// export const deleteExerciseById = async (req, res) => {
|
||||||
|
// const { id } = req.params;
|
||||||
|
// const transaction = await models.db.transaction();
|
||||||
|
|
||||||
|
// try {
|
||||||
|
// const exercise = await models.Exercise.findByPk(id, {
|
||||||
|
// include: [
|
||||||
|
// {
|
||||||
|
// model: models.MultipleChoices,
|
||||||
|
// as: "multipleChoices",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// model: models.MatchingPairs,
|
||||||
|
// as: "matchingPairs",
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// model: models.TrueFalse,
|
||||||
|
// as: "trueFalse",
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
|
// });
|
||||||
|
|
||||||
|
// if (!exercise) {
|
||||||
|
// await transaction.rollback();
|
||||||
|
// return response(404, null, "Exercise not found", res);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (exercise.AUDIO) {
|
||||||
|
// const audioPath = path.join(
|
||||||
|
// "public/uploads/exercise/audio",
|
||||||
|
// exercise.AUDIO
|
||||||
|
// );
|
||||||
|
// if (fs.existsSync(audioPath)) fs.unlinkSync(audioPath);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (exercise.IMAGE) {
|
||||||
|
// const imagePath = path.join(
|
||||||
|
// "public/uploads/exercise/image",
|
||||||
|
// exercise.IMAGE
|
||||||
|
// );
|
||||||
|
// if (fs.existsSync(imagePath)) fs.unlinkSync(imagePath);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const questionType = exercise.QUESTION_TYPE;
|
||||||
|
|
||||||
|
// if (questionType === "MCQ") {
|
||||||
|
// await models.MultipleChoices.destroy({
|
||||||
|
// where: { ID_ADMIN_EXERCISE: id },
|
||||||
|
// transaction,
|
||||||
|
// });
|
||||||
|
// } else if (questionType === "MPQ") {
|
||||||
|
// await models.MatchingPairs.destroy({
|
||||||
|
// where: { ID_ADMIN_EXERCISE: id },
|
||||||
|
// transaction,
|
||||||
|
// });
|
||||||
|
// } else if (questionType === "TFQ") {
|
||||||
|
// await models.TrueFalse.destroy({
|
||||||
|
// where: { ID_ADMIN_EXERCISE: id },
|
||||||
|
// transaction,
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// await exercise.destroy({ transaction });
|
||||||
|
|
||||||
|
// await transaction.commit();
|
||||||
|
|
||||||
|
// response(200, null, "Exercise and related data deleted successfully", res);
|
||||||
|
// } catch (error) {
|
||||||
|
// console.log(error);
|
||||||
|
// await transaction.rollback();
|
||||||
|
// response(500, null, "Internal Server Error", res);
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
export const deleteExerciseById = async (req, res) => {
|
export const deleteExerciseById = async (req, res) => {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const transaction = await models.db.transaction();
|
const transaction = await models.db.transaction();
|
||||||
|
|
@ -195,13 +324,10 @@ export const deleteExerciseById = async (req, res) => {
|
||||||
return response(404, null, "Exercise not found", res);
|
return response(404, null, "Exercise not found", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exercise.VIDEO) {
|
await models.StdExercise.destroy({
|
||||||
const videoPath = path.join(
|
where: { ID_ADMIN_EXERCISE: id },
|
||||||
"public/uploads/exercise/video",
|
transaction,
|
||||||
exercise.VIDEO
|
});
|
||||||
);
|
|
||||||
if (fs.existsSync(videoPath)) fs.unlinkSync(videoPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exercise.AUDIO) {
|
if (exercise.AUDIO) {
|
||||||
const audioPath = path.join(
|
const audioPath = path.join(
|
||||||
|
|
@ -254,7 +380,7 @@ export const deleteExerciseFileById = async (req, res) => {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const { fileType } = req.body;
|
const { fileType } = req.body;
|
||||||
|
|
||||||
if (!["audio", "video", "image"].includes(fileType)) {
|
if (!["audio", "image", "video"].includes(fileType)) {
|
||||||
return response(400, null, "Invalid file type specified", res);
|
return response(400, null, "Invalid file type specified", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -268,11 +394,7 @@ export const deleteExerciseFileById = async (req, res) => {
|
||||||
let filePath;
|
let filePath;
|
||||||
let fileName;
|
let fileName;
|
||||||
|
|
||||||
if (fileType === "video" && exercise.VIDEO) {
|
if (fileType === "audio" && exercise.AUDIO) {
|
||||||
fileName = exercise.VIDEO;
|
|
||||||
filePath = path.join("public/uploads/exercise/video", fileName);
|
|
||||||
exercise.VIDEO = null;
|
|
||||||
} else if (fileType === "audio" && exercise.AUDIO) {
|
|
||||||
fileName = exercise.AUDIO;
|
fileName = exercise.AUDIO;
|
||||||
filePath = path.join("public/uploads/exercise/audio", fileName);
|
filePath = path.join("public/uploads/exercise/audio", fileName);
|
||||||
exercise.AUDIO = null;
|
exercise.AUDIO = null;
|
||||||
|
|
@ -280,6 +402,8 @@ export const deleteExerciseFileById = async (req, res) => {
|
||||||
fileName = exercise.IMAGE;
|
fileName = exercise.IMAGE;
|
||||||
filePath = path.join("public/uploads/exercise/image", fileName);
|
filePath = path.join("public/uploads/exercise/image", fileName);
|
||||||
exercise.IMAGE = null;
|
exercise.IMAGE = null;
|
||||||
|
} else if (fileType === "video" && exercise.VIDEO) {
|
||||||
|
exercise.VIDEO = null;
|
||||||
} else {
|
} else {
|
||||||
return response(
|
return response(
|
||||||
404,
|
404,
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ export const getLevels = async (req, res) => {
|
||||||
response(200, levels, "Success", res);
|
response(200, levels, "Success", res);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
res.status(500).json({ message: "Internal Server Error" });
|
response(500, null, "Error retrieving levels data!", res);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -54,6 +54,74 @@ export const getLevelById = async (req, res) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
response(200, level, "Success", res);
|
response(200, level, "Success", res);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
response(500, null, "Internal Server Error", res);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getLevelsByTopicId = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { idTopic } = req.params;
|
||||||
|
const { ID } = req.user;
|
||||||
|
|
||||||
|
const topicExists = await models.Topic.findByPk(idTopic);
|
||||||
|
if (!topicExists) {
|
||||||
|
return response(404, null, "Topic not found", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
const levels = await models.Level.findAll({
|
||||||
|
where: {
|
||||||
|
ID_TOPIC: idTopic,
|
||||||
|
},
|
||||||
|
attributes: {
|
||||||
|
exclude: [
|
||||||
|
"ROUTE_1",
|
||||||
|
"ROUTE_2",
|
||||||
|
"ROUTE_3",
|
||||||
|
"ROUTE_4",
|
||||||
|
"ROUTE_5",
|
||||||
|
"ROUTE_6",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: models.StdLearning,
|
||||||
|
as: "stdLearning",
|
||||||
|
attributes: ["SCORE", "ID_STUDENT_LEARNING"],
|
||||||
|
where: {
|
||||||
|
ID: ID,
|
||||||
|
},
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!levels || levels.length === 0) {
|
||||||
|
return res
|
||||||
|
.status(404)
|
||||||
|
.json({ message: "No levels found for the given topic." });
|
||||||
|
}
|
||||||
|
|
||||||
|
const levelsWithScore = levels.map((level) => {
|
||||||
|
const SCORE =
|
||||||
|
level.stdLearning.length > 0 ? level.stdLearning[0].SCORE : 0;
|
||||||
|
const ID_STUDENT_LEARNING =
|
||||||
|
level.stdLearning.length > 0
|
||||||
|
? level.stdLearning[0].ID_STUDENT_LEARNING
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const levelJSON = level.toJSON();
|
||||||
|
delete levelJSON.stdLearning;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...levelJSON,
|
||||||
|
ID_STUDENT_LEARNING,
|
||||||
|
SCORE,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
res.status(200).json({ message: "Success", data: levelsWithScore });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
res.status(500).json({ message: "Internal Server Error" });
|
res.status(500).json({ message: "Internal Server Error" });
|
||||||
|
|
@ -61,21 +129,21 @@ export const getLevelById = async (req, res) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createLevel = async (req, res, next) => {
|
export const createLevel = async (req, res, next) => {
|
||||||
const { NAME_LEVEL, ID_SECTION, ID_TOPIC, CONTENT } = req.body;
|
const { NAME_LEVEL, ID_SECTION, ID_TOPIC, CONTENT, VIDEO } = req.body;
|
||||||
const { video, image, audio } = req.filesToSave || {};
|
const { AUDIO, IMAGE } = req.filesToSave || {};
|
||||||
|
|
||||||
if (!NAME_LEVEL) {
|
if (!NAME_LEVEL) {
|
||||||
clearFileBuffers({ video, image, audio });
|
clearFileBuffers({ AUDIO, IMAGE });
|
||||||
return response(400, null, "Level name is required", res);
|
return response(400, null, "Level name is required", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ID_SECTION) {
|
if (!ID_SECTION) {
|
||||||
clearFileBuffers({ video, image, audio });
|
clearFileBuffers({ AUDIO, IMAGE });
|
||||||
return response(400, null, "Section is required", res);
|
return response(400, null, "Section is required", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ID_TOPIC) {
|
if (!ID_TOPIC) {
|
||||||
clearFileBuffers({ video, image, audio });
|
clearFileBuffers({ AUDIO, IMAGE });
|
||||||
return response(400, null, "Topic is required", res);
|
return response(400, null, "Topic is required", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -85,7 +153,7 @@ export const createLevel = async (req, res, next) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!sectionWithTopic) {
|
if (!sectionWithTopic) {
|
||||||
clearFileBuffers({ video, image, audio });
|
clearFileBuffers({ AUDIO, IMAGE });
|
||||||
return response(
|
return response(
|
||||||
400,
|
400,
|
||||||
null,
|
null,
|
||||||
|
|
@ -99,7 +167,7 @@ export const createLevel = async (req, res, next) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (existingLevel) {
|
if (existingLevel) {
|
||||||
clearFileBuffers({ video, image, audio });
|
clearFileBuffers({ AUDIO, IMAGE });
|
||||||
return response(
|
return response(
|
||||||
409,
|
409,
|
||||||
null,
|
null,
|
||||||
|
|
@ -114,7 +182,7 @@ export const createLevel = async (req, res, next) => {
|
||||||
ID_TOPIC,
|
ID_TOPIC,
|
||||||
IS_PRETEST: req.body.IS_PRETEST || 0,
|
IS_PRETEST: req.body.IS_PRETEST || 0,
|
||||||
CONTENT,
|
CONTENT,
|
||||||
VIDEO: null,
|
VIDEO: VIDEO || null,
|
||||||
AUDIO: null,
|
AUDIO: null,
|
||||||
IMAGE: null,
|
IMAGE: null,
|
||||||
ROUTE_1: req.body.ROUTE_1,
|
ROUTE_1: req.body.ROUTE_1,
|
||||||
|
|
@ -127,17 +195,13 @@ export const createLevel = async (req, res, next) => {
|
||||||
|
|
||||||
req.body.newLevelId = newLevel.ID_LEVEL;
|
req.body.newLevelId = newLevel.ID_LEVEL;
|
||||||
|
|
||||||
const videoFilename = video
|
const audioFilename = AUDIO
|
||||||
? saveFileToDisk(video, "video", ID_TOPIC, ID_SECTION, newLevel.ID_LEVEL)
|
? saveFileToDisk(AUDIO, "AUDIO", ID_TOPIC, ID_SECTION, newLevel.ID_LEVEL)
|
||||||
: null;
|
: null;
|
||||||
const audioFilename = audio
|
const imageFilename = IMAGE
|
||||||
? saveFileToDisk(audio, "audio", ID_TOPIC, ID_SECTION, newLevel.ID_LEVEL)
|
? saveFileToDisk(IMAGE, "IMAGE", ID_TOPIC, ID_SECTION, newLevel.ID_LEVEL)
|
||||||
: null;
|
|
||||||
const imageFilename = image
|
|
||||||
? saveFileToDisk(image, "image", ID_TOPIC, ID_SECTION, newLevel.ID_LEVEL)
|
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
newLevel.VIDEO = videoFilename;
|
|
||||||
newLevel.AUDIO = audioFilename;
|
newLevel.AUDIO = audioFilename;
|
||||||
newLevel.IMAGE = imageFilename;
|
newLevel.IMAGE = imageFilename;
|
||||||
await newLevel.save();
|
await newLevel.save();
|
||||||
|
|
@ -147,22 +211,22 @@ export const createLevel = async (req, res, next) => {
|
||||||
response(201, newLevel, "Level created successfully", res);
|
response(201, newLevel, "Level created successfully", res);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
clearFileBuffers({ video, image, audio });
|
clearFileBuffers({ AUDIO, IMAGE });
|
||||||
return response(500, null, "Internal Server Error", res);
|
return response(500, null, "Internal Server Error", res);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateLevelById = async (req, res, next) => {
|
export const updateLevelById = async (req, res, next) => {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const { NAME_LEVEL, ID_SECTION, ID_TOPIC, CONTENT } = req.body;
|
const { NAME_LEVEL, ID_SECTION, ID_TOPIC, CONTENT, VIDEO } = req.body;
|
||||||
|
|
||||||
const { video, image, audio } = req.filesToSave || {};
|
const { AUDIO, IMAGE } = req.filesToSave || {};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const level = await models.Level.findByPk(id);
|
const level = await models.Level.findByPk(id);
|
||||||
|
|
||||||
if (!level) {
|
if (!level) {
|
||||||
clearFileBuffers({ video, image, audio });
|
clearFileBuffers({ AUDIO, IMAGE });
|
||||||
return response(404, null, "Level not found", res);
|
return response(404, null, "Level not found", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -171,7 +235,7 @@ export const updateLevelById = async (req, res, next) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!sectionWithTopic) {
|
if (!sectionWithTopic) {
|
||||||
clearFileBuffers({ video, image, audio });
|
clearFileBuffers({ AUDIO, IMAGE });
|
||||||
return response(
|
return response(
|
||||||
400,
|
400,
|
||||||
null,
|
null,
|
||||||
|
|
@ -190,7 +254,7 @@ export const updateLevelById = async (req, res, next) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (existingLevel) {
|
if (existingLevel) {
|
||||||
clearFileBuffers({ video, image, audio });
|
clearFileBuffers({ AUDIO, IMAGE });
|
||||||
return response(
|
return response(
|
||||||
409,
|
409,
|
||||||
null,
|
null,
|
||||||
|
|
@ -212,26 +276,11 @@ export const updateLevelById = async (req, res, next) => {
|
||||||
if (ID_TOPIC) level.ID_TOPIC = ID_TOPIC;
|
if (ID_TOPIC) level.ID_TOPIC = ID_TOPIC;
|
||||||
if (CONTENT) level.CONTENT = CONTENT;
|
if (CONTENT) level.CONTENT = CONTENT;
|
||||||
|
|
||||||
if (video) {
|
if (VIDEO) {
|
||||||
if (level.VIDEO) {
|
level.VIDEO = VIDEO;
|
||||||
const oldVideoPath = path.join(
|
|
||||||
"public/uploads/level/video",
|
|
||||||
level.VIDEO
|
|
||||||
);
|
|
||||||
if (fs.existsSync(oldVideoPath)) {
|
|
||||||
fs.unlinkSync(oldVideoPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
level.VIDEO = saveFileToDisk(
|
|
||||||
video,
|
|
||||||
"video",
|
|
||||||
ID_TOPIC || level.ID_TOPIC,
|
|
||||||
ID_SECTION || level.ID_SECTION,
|
|
||||||
level.ID_LEVEL
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audio) {
|
if (AUDIO) {
|
||||||
if (level.AUDIO) {
|
if (level.AUDIO) {
|
||||||
const oldAudioPath = path.join(
|
const oldAudioPath = path.join(
|
||||||
"public/uploads/level/audio",
|
"public/uploads/level/audio",
|
||||||
|
|
@ -242,15 +291,15 @@ export const updateLevelById = async (req, res, next) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
level.AUDIO = saveFileToDisk(
|
level.AUDIO = saveFileToDisk(
|
||||||
audio,
|
AUDIO,
|
||||||
"audio",
|
"AUDIO",
|
||||||
ID_TOPIC || level.ID_TOPIC,
|
ID_TOPIC || level.ID_TOPIC,
|
||||||
ID_SECTION || level.ID_SECTION,
|
ID_SECTION || level.ID_SECTION,
|
||||||
level.ID_LEVEL
|
level.ID_LEVEL
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (image) {
|
if (IMAGE) {
|
||||||
if (level.IMAGE) {
|
if (level.IMAGE) {
|
||||||
const oldImagePath = path.join(
|
const oldImagePath = path.join(
|
||||||
"public/uploads/level/image",
|
"public/uploads/level/image",
|
||||||
|
|
@ -261,8 +310,8 @@ export const updateLevelById = async (req, res, next) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
level.IMAGE = saveFileToDisk(
|
level.IMAGE = saveFileToDisk(
|
||||||
image,
|
IMAGE,
|
||||||
"image",
|
"IMAGE",
|
||||||
ID_TOPIC || level.ID_TOPIC,
|
ID_TOPIC || level.ID_TOPIC,
|
||||||
ID_SECTION || level.ID_SECTION,
|
ID_SECTION || level.ID_SECTION,
|
||||||
level.ID_LEVEL
|
level.ID_LEVEL
|
||||||
|
|
@ -278,7 +327,7 @@ export const updateLevelById = async (req, res, next) => {
|
||||||
response(200, level, "Level updated successfully", res);
|
response(200, level, "Level updated successfully", res);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
clearFileBuffers({ video, image, audio });
|
clearFileBuffers({ AUDIO, IMAGE });
|
||||||
return response(500, null, "Internal Server Error", res);
|
return response(500, null, "Internal Server Error", res);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -299,11 +348,6 @@ export const deleteLevelById = async (req, res, next) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (level.VIDEO) {
|
|
||||||
const videoPath = path.join("public/uploads/level/video", level.VIDEO);
|
|
||||||
deleteFile(videoPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (level.AUDIO) {
|
if (level.AUDIO) {
|
||||||
const audioPath = path.join("public/uploads/level/audio", level.AUDIO);
|
const audioPath = path.join("public/uploads/level/audio", level.AUDIO);
|
||||||
deleteFile(audioPath);
|
deleteFile(audioPath);
|
||||||
|
|
@ -327,6 +371,132 @@ export const deleteLevelById = async (req, res, next) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const unlockPreviousRoutes = async (req, res) => {
|
export const getPreviousLevel = async (req, res) => {
|
||||||
const { NEXT_LEARNING } = req.params;
|
try {
|
||||||
|
const { next_learning } = req.params;
|
||||||
|
const { ID } = req.user;
|
||||||
|
|
||||||
|
const currentLevel = await models.Level.findByPk(next_learning, {
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: models.StdLearning,
|
||||||
|
as: "stdLearning",
|
||||||
|
attributes: ["SCORE", "ID_STUDENT_LEARNING"],
|
||||||
|
where: {
|
||||||
|
ID: ID,
|
||||||
|
},
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
attributes: {
|
||||||
|
exclude: [
|
||||||
|
"ROUTE_1",
|
||||||
|
"ROUTE_2",
|
||||||
|
"ROUTE_3",
|
||||||
|
"ROUTE_4",
|
||||||
|
"ROUTE_5",
|
||||||
|
"ROUTE_6",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!currentLevel) {
|
||||||
|
return response(404, null, "Level not found", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { NAME_LEVEL, ID_TOPIC } = currentLevel;
|
||||||
|
const levelNumber = parseInt(NAME_LEVEL.replace("Level ", ""));
|
||||||
|
|
||||||
|
if (isNaN(levelNumber)) {
|
||||||
|
return response(400, null, "Invalid level format", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
const previousLevels = await models.Level.findAll({
|
||||||
|
where: {
|
||||||
|
ID_TOPIC: ID_TOPIC,
|
||||||
|
NAME_LEVEL: {
|
||||||
|
[models.Op.or]: [
|
||||||
|
{ [models.Op.like]: "Pretest" },
|
||||||
|
{ [models.Op.regexp]: `^Level [0-9]+$` },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
[models.Op.or]: [
|
||||||
|
{ IS_PRETEST: 1 },
|
||||||
|
{
|
||||||
|
NAME_LEVEL: {
|
||||||
|
[models.Op.lt]: `Level ${levelNumber}`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
order: [["NAME_LEVEL", "ASC"]],
|
||||||
|
attributes: {
|
||||||
|
exclude: [
|
||||||
|
"ROUTE_1",
|
||||||
|
"ROUTE_2",
|
||||||
|
"ROUTE_3",
|
||||||
|
"ROUTE_4",
|
||||||
|
"ROUTE_5",
|
||||||
|
"ROUTE_6",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: models.StdLearning,
|
||||||
|
as: "stdLearning",
|
||||||
|
attributes: ["SCORE", "ID_STUDENT_LEARNING"],
|
||||||
|
where: {
|
||||||
|
ID: ID,
|
||||||
|
},
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const previousLevelsWithScore = previousLevels.map((level) => {
|
||||||
|
const SCORE =
|
||||||
|
level.stdLearning.length > 0 ? level.stdLearning[0].SCORE : 0;
|
||||||
|
const ID_STUDENT_LEARNING =
|
||||||
|
level.stdLearning.length > 0
|
||||||
|
? level.stdLearning[0].ID_STUDENT_LEARNING
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const levelJSON = level.toJSON();
|
||||||
|
delete levelJSON.stdLearning;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...levelJSON,
|
||||||
|
ID_STUDENT_LEARNING,
|
||||||
|
SCORE,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const currentLevelWithScore = {
|
||||||
|
...currentLevel.toJSON(),
|
||||||
|
ID_STUDENT_LEARNING:
|
||||||
|
currentLevel.stdLearning.length > 0
|
||||||
|
? currentLevel.stdLearning[0].ID_STUDENT_LEARNING
|
||||||
|
: null,
|
||||||
|
SCORE:
|
||||||
|
currentLevel.stdLearning.length > 0
|
||||||
|
? currentLevel.stdLearning[0].SCORE
|
||||||
|
: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
delete currentLevelWithScore.stdLearning;
|
||||||
|
|
||||||
|
if (!previousLevelsWithScore.length && !currentLevelWithScore) {
|
||||||
|
return response(404, null, "No levels found", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = {
|
||||||
|
currentLevel: currentLevelWithScore,
|
||||||
|
previousLevels: previousLevelsWithScore,
|
||||||
|
};
|
||||||
|
|
||||||
|
response(200, result, "Success", res);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
res.status(500).json({ message: "Internal Server Error" });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -36,15 +36,15 @@ export const getSectionById = async (req, res) => {
|
||||||
export const createSection = async (req, res) => {
|
export const createSection = async (req, res) => {
|
||||||
const { NAME_SECTION, DESCRIPTION_SECTION } = req.body;
|
const { NAME_SECTION, DESCRIPTION_SECTION } = req.body;
|
||||||
|
|
||||||
const { thumbnail } = req.filesToSave || {};
|
const { THUMBNAIL } = req.filesToSave || {};
|
||||||
|
|
||||||
if (!NAME_SECTION) {
|
if (!NAME_SECTION) {
|
||||||
clearFileBuffers({ thumbnail });
|
clearFileBuffers({ THUMBNAIL });
|
||||||
return response(400, null, "Section name is required", res);
|
return response(400, null, "Section name is required", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!DESCRIPTION_SECTION) {
|
if (!DESCRIPTION_SECTION) {
|
||||||
clearFileBuffers({ thumbnail });
|
clearFileBuffers({ THUMBNAIL });
|
||||||
return response(400, null, "Description is required", res);
|
return response(400, null, "Description is required", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,8 +55,8 @@ export const createSection = async (req, res) => {
|
||||||
THUMBNAIL: null,
|
THUMBNAIL: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
const thumbnailFilename = thumbnail
|
const thumbnailFilename = THUMBNAIL
|
||||||
? saveFileToDisk(thumbnail, "thumbnail", newSection.ID_SECTION)
|
? saveFileToDisk(THUMBNAIL, "THUMBNAIL", newSection.ID_SECTION)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
newSection.THUMBNAIL = thumbnailFilename;
|
newSection.THUMBNAIL = thumbnailFilename;
|
||||||
|
|
@ -65,7 +65,7 @@ export const createSection = async (req, res) => {
|
||||||
response(201, newSection, "Section created successfully", res);
|
response(201, newSection, "Section created successfully", res);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
clearFileBuffers({ thumbnail });
|
clearFileBuffers({ THUMBNAIL });
|
||||||
response(500, null, "Internal Server Error", res);
|
response(500, null, "Internal Server Error", res);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -74,20 +74,20 @@ export const updateSectionById = async (req, res) => {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const { NAME_SECTION, DESCRIPTION_SECTION } = req.body;
|
const { NAME_SECTION, DESCRIPTION_SECTION } = req.body;
|
||||||
|
|
||||||
const { thumbnail } = req.filesToSave || {};
|
const { THUMBNAIL } = req.filesToSave || {};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const section = await models.Section.findByPk(id);
|
const section = await models.Section.findByPk(id);
|
||||||
|
|
||||||
if (!section) {
|
if (!section) {
|
||||||
clearFileBuffers({ thumbnail });
|
clearFileBuffers({ THUMBNAIL });
|
||||||
return response(404, null, "Section not found", res);
|
return response(404, null, "Section not found", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NAME_SECTION) section.NAME_SECTION = NAME_SECTION;
|
if (NAME_SECTION) section.NAME_SECTION = NAME_SECTION;
|
||||||
if (DESCRIPTION_SECTION) section.DESCRIPTION_SECTION = DESCRIPTION_SECTION;
|
if (DESCRIPTION_SECTION) section.DESCRIPTION_SECTION = DESCRIPTION_SECTION;
|
||||||
|
|
||||||
if (thumbnail) {
|
if (THUMBNAIL) {
|
||||||
if (section.THUMBNAIL) {
|
if (section.THUMBNAIL) {
|
||||||
const oldThumbnailPath = path.join(
|
const oldThumbnailPath = path.join(
|
||||||
"public/uploads/section",
|
"public/uploads/section",
|
||||||
|
|
@ -98,8 +98,8 @@ export const updateSectionById = async (req, res) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
section.THUMBNAIL = saveFileToDisk(
|
section.THUMBNAIL = saveFileToDisk(
|
||||||
thumbnail,
|
THUMBNAIL,
|
||||||
"thumbnail",
|
"THUMBNAIL",
|
||||||
section.ID_SECTION
|
section.ID_SECTION
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -109,7 +109,7 @@ export const updateSectionById = async (req, res) => {
|
||||||
response(200, section, "Section updated successfully", res);
|
response(200, section, "Section updated successfully", res);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
clearFileBuffers({ thumbnail });
|
clearFileBuffers({ THUMBNAIL });
|
||||||
response(500, null, "Internal Server Error", res);
|
response(500, null, "Internal Server Error", res);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,32 @@ export const getTopicById = async (req, res) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getTopicBySectionId = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { sectionId } = req.params;
|
||||||
|
|
||||||
|
const sectionExists = await models.Section.findByPk(sectionId);
|
||||||
|
if (!sectionExists) {
|
||||||
|
return response(404, null, "Section not found", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
const topics = await models.Topic.findAll({
|
||||||
|
where: { ID_SECTION: sectionId },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!topics || topics.length === 0) {
|
||||||
|
return response(404, null, "No topics found for this section", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
response(200, topics, "Success", res);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
response(500, null, "Internal Server Error", res);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export const createTopic = async (req, res) => {
|
export const createTopic = async (req, res) => {
|
||||||
const { ID_SECTION, NAME_TOPIC, DESCRIPTION_TOPIC } = req.body;
|
const { ID_SECTION, NAME_TOPIC, DESCRIPTION_TOPIC, OBJECTIVES } = req.body;
|
||||||
|
|
||||||
if (!ID_SECTION) {
|
if (!ID_SECTION) {
|
||||||
return response(400, null, "Section ID is required", res);
|
return response(400, null, "Section ID is required", res);
|
||||||
|
|
@ -39,7 +63,11 @@ export const createTopic = async (req, res) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!DESCRIPTION_TOPIC) {
|
if (!DESCRIPTION_TOPIC) {
|
||||||
return response(400, null, "Topic Description is required", res);
|
return response(400, null, "Topic description is required", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!OBJECTIVES) {
|
||||||
|
return response(400, null, "Topic objectives are required", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -52,6 +80,7 @@ export const createTopic = async (req, res) => {
|
||||||
ID_SECTION,
|
ID_SECTION,
|
||||||
NAME_TOPIC,
|
NAME_TOPIC,
|
||||||
DESCRIPTION_TOPIC,
|
DESCRIPTION_TOPIC,
|
||||||
|
OBJECTIVES,
|
||||||
});
|
});
|
||||||
|
|
||||||
response(201, newTopic, "Topic created successfully", res);
|
response(201, newTopic, "Topic created successfully", res);
|
||||||
|
|
@ -63,7 +92,7 @@ export const createTopic = async (req, res) => {
|
||||||
|
|
||||||
export const updateTopicById = async (req, res) => {
|
export const updateTopicById = async (req, res) => {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const { ID_SECTION, NAME_TOPIC, DESCRIPTION_TOPIC } = req.body;
|
const { ID_SECTION, NAME_TOPIC, DESCRIPTION_TOPIC, OBJECTIVES } = req.body;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const topic = await models.Topic.findByPk(id);
|
const topic = await models.Topic.findByPk(id);
|
||||||
|
|
@ -88,6 +117,10 @@ export const updateTopicById = async (req, res) => {
|
||||||
topic.DESCRIPTION_TOPIC = DESCRIPTION_TOPIC;
|
topic.DESCRIPTION_TOPIC = DESCRIPTION_TOPIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (OBJECTIVES) {
|
||||||
|
topic.OBJECTIVES = OBJECTIVES;
|
||||||
|
}
|
||||||
|
|
||||||
await topic.save();
|
await topic.save();
|
||||||
|
|
||||||
response(200, topic, "Topic updated successfully", res);
|
response(200, topic, "Topic updated successfully", res);
|
||||||
|
|
@ -115,3 +148,75 @@ export const deleteTopicById = async (req, res) => {
|
||||||
response(500, null, "Internal Server Error", res);
|
response(500, null, "Internal Server Error", res);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getCompletedTopics = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const user = req.user;
|
||||||
|
const userId = user.ID;
|
||||||
|
|
||||||
|
const completedLevels = await models.StdLearning.findAll({
|
||||||
|
where: {
|
||||||
|
ID: userId,
|
||||||
|
IS_PASS: 1,
|
||||||
|
},
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: models.Level,
|
||||||
|
as: "level",
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: models.Topic,
|
||||||
|
as: "levelTopic",
|
||||||
|
attributes: [
|
||||||
|
"ID_TOPIC",
|
||||||
|
"NAME_TOPIC",
|
||||||
|
"DESCRIPTION_TOPIC",
|
||||||
|
"OBJECTIVES",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!completedLevels.length) {
|
||||||
|
return response(404, null, "No completed topics found", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
const completedTopics = [];
|
||||||
|
for (const levelRecord of completedLevels) {
|
||||||
|
const level = levelRecord.level;
|
||||||
|
|
||||||
|
const level6 = await models.Level.findOne({
|
||||||
|
where: {
|
||||||
|
NAME_LEVEL: "Level 6",
|
||||||
|
ID_TOPIC: level.ID_TOPIC,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (level6 && level.ID_LEVEL === level6.ID_LEVEL) {
|
||||||
|
const topic = level.levelTopic;
|
||||||
|
completedTopics.push({
|
||||||
|
ID_TOPIC: topic.ID_TOPIC,
|
||||||
|
NAME_TOPIC: topic.NAME_TOPIC,
|
||||||
|
DESCRIPTION_TOPIC: topic.DESCRIPTION_TOPIC,
|
||||||
|
OBJECTIVES: topic.OBJECTIVES,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!completedTopics.length) {
|
||||||
|
return response(404, null, "No completed topics for Level 6 found", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
response(
|
||||||
|
200,
|
||||||
|
completedTopics,
|
||||||
|
"Completed topics fetched successfully",
|
||||||
|
res
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
response(500, null, "Internal Server Error", res);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import fs from "fs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
export const createMatchingPairsExercise = async (req, res) => {
|
export const createMatchingPairsExercise = async (req, res) => {
|
||||||
const { ID_LEVEL, TITLE, QUESTION, SCORE_WEIGHT } = req.body;
|
const { ID_LEVEL, TITLE, QUESTION, SCORE_WEIGHT, VIDEO } = req.body;
|
||||||
let PAIRS = req.body.PAIRS;
|
let PAIRS = req.body.PAIRS;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -19,7 +19,7 @@ export const createMatchingPairsExercise = async (req, res) => {
|
||||||
return response(400, null, "Invalid PAIRS format", res);
|
return response(400, null, "Invalid PAIRS format", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { video, image, audio } = req.filesToSave || {};
|
const { IMAGE, AUDIO } = req.filesToSave || {};
|
||||||
|
|
||||||
if (!ID_LEVEL) return response(400, null, "Level ID is required", res);
|
if (!ID_LEVEL) return response(400, null, "Level ID is required", res);
|
||||||
if (!QUESTION) return response(400, null, "Question is required", res);
|
if (!QUESTION) return response(400, null, "Question is required", res);
|
||||||
|
|
@ -33,7 +33,7 @@ export const createMatchingPairsExercise = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const level = await models.Level.findByPk(ID_LEVEL);
|
const level = await models.Level.findByPk(ID_LEVEL);
|
||||||
if (!level) {
|
if (!level) {
|
||||||
clearFileBuffers({ video, image, audio });
|
clearFileBuffers({ IMAGE, AUDIO });
|
||||||
return response(404, null, "Level not found", res);
|
return response(404, null, "Level not found", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,7 +49,7 @@ export const createMatchingPairsExercise = async (req, res) => {
|
||||||
where: { ID_LEVEL, TITLE: generatedTitle },
|
where: { ID_LEVEL, TITLE: generatedTitle },
|
||||||
});
|
});
|
||||||
if (existingExercise) {
|
if (existingExercise) {
|
||||||
clearFileBuffers({ video, image, audio });
|
clearFileBuffers({ IMAGE, AUDIO });
|
||||||
return response(
|
return response(
|
||||||
400,
|
400,
|
||||||
null,
|
null,
|
||||||
|
|
@ -66,23 +66,19 @@ export const createMatchingPairsExercise = async (req, res) => {
|
||||||
SCORE_WEIGHT,
|
SCORE_WEIGHT,
|
||||||
QUESTION_TYPE: "MPQ",
|
QUESTION_TYPE: "MPQ",
|
||||||
AUDIO: null,
|
AUDIO: null,
|
||||||
VIDEO: null,
|
VIDEO: VIDEO || null,
|
||||||
IMAGE: null,
|
IMAGE: null,
|
||||||
},
|
},
|
||||||
{ transaction }
|
{ transaction }
|
||||||
);
|
);
|
||||||
|
|
||||||
const videoFilename = video
|
const audioFilename = AUDIO
|
||||||
? saveFileToDisk(video, "video", ID_LEVEL, newExercise.ID_ADMIN_EXERCISE)
|
? saveFileToDisk(AUDIO, "AUDIO", ID_LEVEL, newExercise.ID_ADMIN_EXERCISE)
|
||||||
: null;
|
: null;
|
||||||
const audioFilename = audio
|
const imageFilename = IMAGE
|
||||||
? saveFileToDisk(audio, "audio", ID_LEVEL, newExercise.ID_ADMIN_EXERCISE)
|
? saveFileToDisk(IMAGE, "IMAGE", ID_LEVEL, newExercise.ID_ADMIN_EXERCISE)
|
||||||
: null;
|
|
||||||
const imageFilename = image
|
|
||||||
? saveFileToDisk(image, "image", ID_LEVEL, newExercise.ID_ADMIN_EXERCISE)
|
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
newExercise.VIDEO = videoFilename;
|
|
||||||
newExercise.AUDIO = audioFilename;
|
newExercise.AUDIO = audioFilename;
|
||||||
newExercise.IMAGE = imageFilename;
|
newExercise.IMAGE = imageFilename;
|
||||||
await newExercise.save({ transaction });
|
await newExercise.save({ transaction });
|
||||||
|
|
@ -111,14 +107,14 @@ export const createMatchingPairsExercise = async (req, res) => {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
await transaction.rollback();
|
await transaction.rollback();
|
||||||
clearFileBuffers({ video, image, audio });
|
clearFileBuffers({ IMAGE, AUDIO });
|
||||||
response(500, null, "Internal Server Error", res);
|
response(500, null, "Internal Server Error", res);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateMatchingPairsExerciseById = async (req, res) => {
|
export const updateMatchingPairsExerciseById = async (req, res) => {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const { ID_LEVEL, QUESTION, SCORE_WEIGHT } = req.body;
|
const { ID_LEVEL, QUESTION, SCORE_WEIGHT, VIDEO } = req.body;
|
||||||
let PAIRS = req.body.PAIRS;
|
let PAIRS = req.body.PAIRS;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -129,14 +125,14 @@ export const updateMatchingPairsExerciseById = async (req, res) => {
|
||||||
return response(400, null, "Invalid PAIRS format", res);
|
return response(400, null, "Invalid PAIRS format", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { video, image, audio } = req.filesToSave || {};
|
const { IMAGE, AUDIO } = req.filesToSave || {};
|
||||||
|
|
||||||
const transaction = await models.db.transaction();
|
const transaction = await models.db.transaction();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const exercise = await models.Exercise.findByPk(id, { transaction });
|
const exercise = await models.Exercise.findByPk(id, { transaction });
|
||||||
if (!exercise) {
|
if (!exercise) {
|
||||||
clearFileBuffers({ video, image, audio });
|
clearFileBuffers({ IMAGE, AUDIO });
|
||||||
await transaction.rollback();
|
await transaction.rollback();
|
||||||
return response(404, null, "Exercise not found", res);
|
return response(404, null, "Exercise not found", res);
|
||||||
}
|
}
|
||||||
|
|
@ -144,7 +140,7 @@ export const updateMatchingPairsExerciseById = async (req, res) => {
|
||||||
if (ID_LEVEL) {
|
if (ID_LEVEL) {
|
||||||
const level = await models.Level.findByPk(ID_LEVEL, { transaction });
|
const level = await models.Level.findByPk(ID_LEVEL, { transaction });
|
||||||
if (!level) {
|
if (!level) {
|
||||||
clearFileBuffers({ video, image, audio });
|
clearFileBuffers({ IMAGE, AUDIO });
|
||||||
await transaction.rollback();
|
await transaction.rollback();
|
||||||
return response(404, null, "Level not found", res);
|
return response(404, null, "Level not found", res);
|
||||||
}
|
}
|
||||||
|
|
@ -153,26 +149,9 @@ export const updateMatchingPairsExerciseById = async (req, res) => {
|
||||||
|
|
||||||
if (QUESTION) exercise.QUESTION = QUESTION;
|
if (QUESTION) exercise.QUESTION = QUESTION;
|
||||||
if (SCORE_WEIGHT) exercise.SCORE_WEIGHT = SCORE_WEIGHT;
|
if (SCORE_WEIGHT) exercise.SCORE_WEIGHT = SCORE_WEIGHT;
|
||||||
|
if (VIDEO) exercise.VIDEO = VIDEO;
|
||||||
|
|
||||||
if (video) {
|
if (AUDIO) {
|
||||||
if (exercise.VIDEO) {
|
|
||||||
const oldVideoPath = path.join(
|
|
||||||
"public/uploads/exercise/video",
|
|
||||||
exercise.VIDEO
|
|
||||||
);
|
|
||||||
if (fs.existsSync(oldVideoPath)) {
|
|
||||||
fs.unlinkSync(oldVideoPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exercise.VIDEO = saveFileToDisk(
|
|
||||||
video,
|
|
||||||
"video",
|
|
||||||
ID_LEVEL || exercise.ID_LEVEL,
|
|
||||||
exercise.ID_ADMIN_EXERCISE
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (audio) {
|
|
||||||
if (exercise.AUDIO) {
|
if (exercise.AUDIO) {
|
||||||
const oldAudioPath = path.join(
|
const oldAudioPath = path.join(
|
||||||
"public/uploads/exercise/audio",
|
"public/uploads/exercise/audio",
|
||||||
|
|
@ -183,14 +162,14 @@ export const updateMatchingPairsExerciseById = async (req, res) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exercise.AUDIO = saveFileToDisk(
|
exercise.AUDIO = saveFileToDisk(
|
||||||
audio,
|
AUDIO,
|
||||||
"audio",
|
"AUDIO",
|
||||||
ID_LEVEL || exercise.ID_LEVEL,
|
ID_LEVEL || exercise.ID_LEVEL,
|
||||||
exercise.ID_ADMIN_EXERCISE
|
exercise.ID_ADMIN_EXERCISE
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (image) {
|
if (IMAGE) {
|
||||||
if (exercise.IMAGE) {
|
if (exercise.IMAGE) {
|
||||||
const oldImagePath = path.join(
|
const oldImagePath = path.join(
|
||||||
"public/uploads/exercise/image",
|
"public/uploads/exercise/image",
|
||||||
|
|
@ -201,8 +180,8 @@ export const updateMatchingPairsExerciseById = async (req, res) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exercise.IMAGE = saveFileToDisk(
|
exercise.IMAGE = saveFileToDisk(
|
||||||
image,
|
IMAGE,
|
||||||
"image",
|
"IMAGE",
|
||||||
ID_LEVEL || exercise.ID_LEVEL,
|
ID_LEVEL || exercise.ID_LEVEL,
|
||||||
exercise.ID_ADMIN_EXERCISE
|
exercise.ID_ADMIN_EXERCISE
|
||||||
);
|
);
|
||||||
|
|
@ -267,7 +246,7 @@ export const updateMatchingPairsExerciseById = async (req, res) => {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
await transaction.rollback();
|
await transaction.rollback();
|
||||||
clearFileBuffers({ video, image, audio });
|
clearFileBuffers({ IMAGE, AUDIO });
|
||||||
response(500, null, "Internal Server Error", res);
|
response(500, null, "Internal Server Error", res);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -19,9 +19,10 @@ export const createMultipleChoicesExercise = async (req, res) => {
|
||||||
OPTION_E,
|
OPTION_E,
|
||||||
ANSWER_KEY,
|
ANSWER_KEY,
|
||||||
SCORE_WEIGHT,
|
SCORE_WEIGHT,
|
||||||
|
VIDEO,
|
||||||
} = req.body;
|
} = req.body;
|
||||||
|
|
||||||
const { video, image, audio } = req.filesToSave || {};
|
const { IMAGE, AUDIO } = req.filesToSave || {};
|
||||||
|
|
||||||
if (!ID_LEVEL) return response(400, null, "Level ID is required", res);
|
if (!ID_LEVEL) return response(400, null, "Level ID is required", res);
|
||||||
if (!QUESTION) return response(400, null, "Question is required", res);
|
if (!QUESTION) return response(400, null, "Question is required", res);
|
||||||
|
|
@ -39,7 +40,7 @@ export const createMultipleChoicesExercise = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const level = await models.Level.findByPk(ID_LEVEL);
|
const level = await models.Level.findByPk(ID_LEVEL);
|
||||||
if (!level) {
|
if (!level) {
|
||||||
clearFileBuffers({ video, image, audio });
|
clearFileBuffers({ IMAGE, AUDIO });
|
||||||
return response(404, null, "Level not found", res);
|
return response(404, null, "Level not found", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,7 +56,7 @@ export const createMultipleChoicesExercise = async (req, res) => {
|
||||||
where: { ID_LEVEL, TITLE: generatedTitle },
|
where: { ID_LEVEL, TITLE: generatedTitle },
|
||||||
});
|
});
|
||||||
if (existingExercise) {
|
if (existingExercise) {
|
||||||
clearFileBuffers({ video, image, audio });
|
clearFileBuffers({ IMAGE, AUDIO });
|
||||||
return response(
|
return response(
|
||||||
400,
|
400,
|
||||||
null,
|
null,
|
||||||
|
|
@ -72,23 +73,19 @@ export const createMultipleChoicesExercise = async (req, res) => {
|
||||||
SCORE_WEIGHT,
|
SCORE_WEIGHT,
|
||||||
QUESTION_TYPE: "MCQ",
|
QUESTION_TYPE: "MCQ",
|
||||||
AUDIO: null,
|
AUDIO: null,
|
||||||
VIDEO: null,
|
VIDEO: VIDEO || null,
|
||||||
IMAGE: null,
|
IMAGE: null,
|
||||||
},
|
},
|
||||||
{ transaction }
|
{ transaction }
|
||||||
);
|
);
|
||||||
|
|
||||||
const videoFilename = video
|
const audioFilename = AUDIO
|
||||||
? saveFileToDisk(video, "video", ID_LEVEL, newExercise.ID_ADMIN_EXERCISE)
|
? saveFileToDisk(AUDIO, "AUDIO", ID_LEVEL, newExercise.ID_ADMIN_EXERCISE)
|
||||||
: null;
|
: null;
|
||||||
const audioFilename = audio
|
const imageFilename = IMAGE
|
||||||
? saveFileToDisk(audio, "audio", ID_LEVEL, newExercise.ID_ADMIN_EXERCISE)
|
? saveFileToDisk(IMAGE, "IMAGE", ID_LEVEL, newExercise.ID_ADMIN_EXERCISE)
|
||||||
: null;
|
|
||||||
const imageFilename = image
|
|
||||||
? saveFileToDisk(image, "image", ID_LEVEL, newExercise.ID_ADMIN_EXERCISE)
|
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
newExercise.VIDEO = videoFilename;
|
|
||||||
newExercise.AUDIO = audioFilename;
|
newExercise.AUDIO = audioFilename;
|
||||||
newExercise.IMAGE = imageFilename;
|
newExercise.IMAGE = imageFilename;
|
||||||
await newExercise.save({ transaction });
|
await newExercise.save({ transaction });
|
||||||
|
|
@ -117,7 +114,7 @@ export const createMultipleChoicesExercise = async (req, res) => {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
await transaction.rollback();
|
await transaction.rollback();
|
||||||
clearFileBuffers({ video, image, audio });
|
clearFileBuffers({ IMAGE, AUDIO });
|
||||||
response(500, null, "Internal Server Error", res);
|
response(500, null, "Internal Server Error", res);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -134,9 +131,10 @@ export const updateMultipleChoicesExerciseById = async (req, res) => {
|
||||||
OPTION_E,
|
OPTION_E,
|
||||||
ANSWER_KEY,
|
ANSWER_KEY,
|
||||||
SCORE_WEIGHT,
|
SCORE_WEIGHT,
|
||||||
|
VIDEO,
|
||||||
} = req.body;
|
} = req.body;
|
||||||
|
|
||||||
const { video, image, audio } = req.filesToSave || {};
|
const { IMAGE, AUDIO } = req.filesToSave || {};
|
||||||
|
|
||||||
const transaction = await models.db.transaction();
|
const transaction = await models.db.transaction();
|
||||||
|
|
||||||
|
|
@ -144,7 +142,7 @@ export const updateMultipleChoicesExerciseById = async (req, res) => {
|
||||||
const exercise = await models.Exercise.findByPk(id, { transaction });
|
const exercise = await models.Exercise.findByPk(id, { transaction });
|
||||||
|
|
||||||
if (!exercise) {
|
if (!exercise) {
|
||||||
clearFileBuffers({ video, image, audio });
|
clearFileBuffers({ IMAGE, AUDIO });
|
||||||
await transaction.rollback();
|
await transaction.rollback();
|
||||||
return response(404, null, "Exercise not found", res);
|
return response(404, null, "Exercise not found", res);
|
||||||
}
|
}
|
||||||
|
|
@ -152,7 +150,7 @@ export const updateMultipleChoicesExerciseById = async (req, res) => {
|
||||||
if (ID_LEVEL) {
|
if (ID_LEVEL) {
|
||||||
const level = await models.Level.findByPk(ID_LEVEL, { transaction });
|
const level = await models.Level.findByPk(ID_LEVEL, { transaction });
|
||||||
if (!level) {
|
if (!level) {
|
||||||
clearFileBuffers({ video, image, audio });
|
clearFileBuffers({ IMAGE, AUDIO });
|
||||||
await transaction.rollback();
|
await transaction.rollback();
|
||||||
return response(404, null, "Level not found", res);
|
return response(404, null, "Level not found", res);
|
||||||
}
|
}
|
||||||
|
|
@ -161,26 +159,9 @@ export const updateMultipleChoicesExerciseById = async (req, res) => {
|
||||||
|
|
||||||
if (QUESTION) exercise.QUESTION = QUESTION;
|
if (QUESTION) exercise.QUESTION = QUESTION;
|
||||||
if (SCORE_WEIGHT) exercise.SCORE_WEIGHT = SCORE_WEIGHT;
|
if (SCORE_WEIGHT) exercise.SCORE_WEIGHT = SCORE_WEIGHT;
|
||||||
|
if (VIDEO) exercise.VIDEO = VIDEO;
|
||||||
|
|
||||||
if (video) {
|
if (AUDIO) {
|
||||||
if (exercise.VIDEO) {
|
|
||||||
const oldVideoPath = path.join(
|
|
||||||
"public/uploads/exercise/video",
|
|
||||||
exercise.VIDEO
|
|
||||||
);
|
|
||||||
if (fs.existsSync(oldVideoPath)) {
|
|
||||||
fs.unlinkSync(oldVideoPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exercise.VIDEO = saveFileToDisk(
|
|
||||||
video,
|
|
||||||
"video",
|
|
||||||
ID_LEVEL || exercise.ID_LEVEL,
|
|
||||||
exercise.ID_ADMIN_EXERCISE
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (audio) {
|
|
||||||
if (exercise.AUDIO) {
|
if (exercise.AUDIO) {
|
||||||
const oldAudioPath = path.join(
|
const oldAudioPath = path.join(
|
||||||
"public/uploads/exercise/audio",
|
"public/uploads/exercise/audio",
|
||||||
|
|
@ -191,14 +172,14 @@ export const updateMultipleChoicesExerciseById = async (req, res) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exercise.AUDIO = saveFileToDisk(
|
exercise.AUDIO = saveFileToDisk(
|
||||||
audio,
|
AUDIO,
|
||||||
"audio",
|
"AUDIO",
|
||||||
ID_LEVEL || exercise.ID_LEVEL,
|
ID_LEVEL || exercise.ID_LEVEL,
|
||||||
exercise.ID_ADMIN_EXERCISE
|
exercise.ID_ADMIN_EXERCISE
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (image) {
|
if (IMAGE) {
|
||||||
if (exercise.IMAGE) {
|
if (exercise.IMAGE) {
|
||||||
const oldImagePath = path.join(
|
const oldImagePath = path.join(
|
||||||
"public/uploads/exercise/image",
|
"public/uploads/exercise/image",
|
||||||
|
|
@ -209,8 +190,8 @@ export const updateMultipleChoicesExerciseById = async (req, res) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exercise.IMAGE = saveFileToDisk(
|
exercise.IMAGE = saveFileToDisk(
|
||||||
image,
|
IMAGE,
|
||||||
"image",
|
"IMAGE",
|
||||||
ID_LEVEL || exercise.ID_LEVEL,
|
ID_LEVEL || exercise.ID_LEVEL,
|
||||||
exercise.ID_ADMIN_EXERCISE
|
exercise.ID_ADMIN_EXERCISE
|
||||||
);
|
);
|
||||||
|
|
@ -224,7 +205,7 @@ export const updateMultipleChoicesExerciseById = async (req, res) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!multipleChoices) {
|
if (!multipleChoices) {
|
||||||
clearFileBuffers({ video, image, audio });
|
clearFileBuffers({ IMAGE, AUDIO });
|
||||||
await transaction.rollback();
|
await transaction.rollback();
|
||||||
return response(404, null, "Multiple Choices not found", res);
|
return response(404, null, "Multiple Choices not found", res);
|
||||||
}
|
}
|
||||||
|
|
@ -248,7 +229,7 @@ export const updateMultipleChoicesExerciseById = async (req, res) => {
|
||||||
response(200, payload, "Exercise updated successfully", res);
|
response(200, payload, "Exercise updated successfully", res);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
clearFileBuffers({ video, image, audio });
|
clearFileBuffers({ IMAGE, AUDIO });
|
||||||
await transaction.rollback();
|
await transaction.rollback();
|
||||||
response(500, null, "Internal Server Error", res);
|
response(500, null, "Internal Server Error", res);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,8 @@ import fs from "fs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
export const createTrueFalseExercise = async (req, res) => {
|
export const createTrueFalseExercise = async (req, res) => {
|
||||||
const { ID_LEVEL, TITLE, QUESTION, IS_TRUE, SCORE_WEIGHT } = req.body;
|
const { ID_LEVEL, TITLE, QUESTION, IS_TRUE, SCORE_WEIGHT, VIDEO } = req.body;
|
||||||
|
const { IMAGE, AUDIO } = req.filesToSave || {};
|
||||||
const { video, image, audio } = req.filesToSave || {};
|
|
||||||
|
|
||||||
if (!ID_LEVEL) return response(400, null, "Level ID is required", res);
|
if (!ID_LEVEL) return response(400, null, "Level ID is required", res);
|
||||||
if (!QUESTION) return response(400, null, "Question is required", res);
|
if (!QUESTION) return response(400, null, "Question is required", res);
|
||||||
|
|
@ -24,7 +23,7 @@ export const createTrueFalseExercise = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const level = await models.Level.findByPk(ID_LEVEL);
|
const level = await models.Level.findByPk(ID_LEVEL);
|
||||||
if (!level) {
|
if (!level) {
|
||||||
clearFileBuffers({ video, image, audio });
|
clearFileBuffers({ IMAGE, AUDIO });
|
||||||
return response(404, null, "Level not found", res);
|
return response(404, null, "Level not found", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,7 +39,7 @@ export const createTrueFalseExercise = async (req, res) => {
|
||||||
where: { ID_LEVEL, TITLE: generatedTitle },
|
where: { ID_LEVEL, TITLE: generatedTitle },
|
||||||
});
|
});
|
||||||
if (existingExercise) {
|
if (existingExercise) {
|
||||||
clearFileBuffers({ video, image, audio });
|
clearFileBuffers({ IMAGE, AUDIO });
|
||||||
return response(
|
return response(
|
||||||
400,
|
400,
|
||||||
null,
|
null,
|
||||||
|
|
@ -57,23 +56,19 @@ export const createTrueFalseExercise = async (req, res) => {
|
||||||
SCORE_WEIGHT,
|
SCORE_WEIGHT,
|
||||||
QUESTION_TYPE: "TFQ",
|
QUESTION_TYPE: "TFQ",
|
||||||
AUDIO: null,
|
AUDIO: null,
|
||||||
VIDEO: null,
|
VIDEO: VIDEO || null,
|
||||||
IMAGE: null,
|
IMAGE: null,
|
||||||
},
|
},
|
||||||
{ transaction }
|
{ transaction }
|
||||||
);
|
);
|
||||||
|
|
||||||
const videoFilename = video
|
const audioFilename = AUDIO
|
||||||
? saveFileToDisk(video, "video", ID_LEVEL, newExercise.ID_ADMIN_EXERCISE)
|
? saveFileToDisk(AUDIO, "AUDIO", ID_LEVEL, newExercise.ID_ADMIN_EXERCISE)
|
||||||
: null;
|
: null;
|
||||||
const audioFilename = audio
|
const imageFilename = IMAGE
|
||||||
? saveFileToDisk(audio, "audio", ID_LEVEL, newExercise.ID_ADMIN_EXERCISE)
|
? saveFileToDisk(IMAGE, "IMAGE", ID_LEVEL, newExercise.ID_ADMIN_EXERCISE)
|
||||||
: null;
|
|
||||||
const imageFilename = image
|
|
||||||
? saveFileToDisk(image, "image", ID_LEVEL, newExercise.ID_ADMIN_EXERCISE)
|
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
newExercise.VIDEO = videoFilename;
|
|
||||||
newExercise.AUDIO = audioFilename;
|
newExercise.AUDIO = audioFilename;
|
||||||
newExercise.IMAGE = imageFilename;
|
newExercise.IMAGE = imageFilename;
|
||||||
await newExercise.save({ transaction });
|
await newExercise.save({ transaction });
|
||||||
|
|
@ -97,16 +92,15 @@ export const createTrueFalseExercise = async (req, res) => {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
await transaction.rollback();
|
await transaction.rollback();
|
||||||
clearFileBuffers({ video, image, audio });
|
clearFileBuffers({ IMAGE, AUDIO });
|
||||||
response(500, null, "Internal Server Error", res);
|
response(500, null, "Internal Server Error", res);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateTrueFalseExerciseById = async (req, res) => {
|
export const updateTrueFalseExerciseById = async (req, res) => {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const { ID_LEVEL, QUESTION, IS_TRUE, SCORE_WEIGHT } = req.body;
|
const { ID_LEVEL, QUESTION, IS_TRUE, SCORE_WEIGHT, VIDEO } = req.body;
|
||||||
|
const { IMAGE, AUDIO } = req.filesToSave || {};
|
||||||
const { video, image, audio } = req.filesToSave || {};
|
|
||||||
|
|
||||||
const transaction = await models.db.transaction();
|
const transaction = await models.db.transaction();
|
||||||
|
|
||||||
|
|
@ -114,7 +108,7 @@ export const updateTrueFalseExerciseById = async (req, res) => {
|
||||||
const exercise = await models.Exercise.findByPk(id, { transaction });
|
const exercise = await models.Exercise.findByPk(id, { transaction });
|
||||||
|
|
||||||
if (!exercise) {
|
if (!exercise) {
|
||||||
clearFileBuffers({ video, image, audio });
|
clearFileBuffers({ IMAGE, AUDIO });
|
||||||
await transaction.rollback();
|
await transaction.rollback();
|
||||||
return response(404, null, "Exercise not found", res);
|
return response(404, null, "Exercise not found", res);
|
||||||
}
|
}
|
||||||
|
|
@ -122,7 +116,7 @@ export const updateTrueFalseExerciseById = async (req, res) => {
|
||||||
if (ID_LEVEL) {
|
if (ID_LEVEL) {
|
||||||
const level = await models.Level.findByPk(ID_LEVEL, { transaction });
|
const level = await models.Level.findByPk(ID_LEVEL, { transaction });
|
||||||
if (!level) {
|
if (!level) {
|
||||||
clearFileBuffers({ video, image, audio });
|
clearFileBuffers({ IMAGE, AUDIO });
|
||||||
await transaction.rollback();
|
await transaction.rollback();
|
||||||
return response(404, null, "Level not found", res);
|
return response(404, null, "Level not found", res);
|
||||||
}
|
}
|
||||||
|
|
@ -131,26 +125,9 @@ export const updateTrueFalseExerciseById = async (req, res) => {
|
||||||
|
|
||||||
if (QUESTION) exercise.QUESTION = QUESTION;
|
if (QUESTION) exercise.QUESTION = QUESTION;
|
||||||
if (SCORE_WEIGHT) exercise.SCORE_WEIGHT = SCORE_WEIGHT;
|
if (SCORE_WEIGHT) exercise.SCORE_WEIGHT = SCORE_WEIGHT;
|
||||||
|
if (VIDEO) exercise.VIDEO = VIDEO;
|
||||||
|
|
||||||
if (video) {
|
if (AUDIO) {
|
||||||
if (exercise.VIDEO) {
|
|
||||||
const oldVideoPath = path.join(
|
|
||||||
"public/uploads/exercise/video",
|
|
||||||
exercise.VIDEO
|
|
||||||
);
|
|
||||||
if (fs.existsSync(oldVideoPath)) {
|
|
||||||
fs.unlinkSync(oldVideoPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exercise.VIDEO = saveFileToDisk(
|
|
||||||
video,
|
|
||||||
"video",
|
|
||||||
ID_LEVEL || exercise.ID_LEVEL,
|
|
||||||
exercise.ID_ADMIN_EXERCISE
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (audio) {
|
|
||||||
if (exercise.AUDIO) {
|
if (exercise.AUDIO) {
|
||||||
const oldAudioPath = path.join(
|
const oldAudioPath = path.join(
|
||||||
"public/uploads/exercise/audio",
|
"public/uploads/exercise/audio",
|
||||||
|
|
@ -161,14 +138,14 @@ export const updateTrueFalseExerciseById = async (req, res) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exercise.AUDIO = saveFileToDisk(
|
exercise.AUDIO = saveFileToDisk(
|
||||||
audio,
|
AUDIO,
|
||||||
"audio",
|
"AUDIO",
|
||||||
ID_LEVEL || exercise.ID_LEVEL,
|
ID_LEVEL || exercise.ID_LEVEL,
|
||||||
exercise.ID_ADMIN_EXERCISE
|
exercise.ID_ADMIN_EXERCISE
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (image) {
|
if (IMAGE) {
|
||||||
if (exercise.IMAGE) {
|
if (exercise.IMAGE) {
|
||||||
const oldImagePath = path.join(
|
const oldImagePath = path.join(
|
||||||
"public/uploads/exercise/image",
|
"public/uploads/exercise/image",
|
||||||
|
|
@ -179,8 +156,8 @@ export const updateTrueFalseExerciseById = async (req, res) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
exercise.IMAGE = saveFileToDisk(
|
exercise.IMAGE = saveFileToDisk(
|
||||||
image,
|
IMAGE,
|
||||||
"image",
|
"IMAGE",
|
||||||
ID_LEVEL || exercise.ID_LEVEL,
|
ID_LEVEL || exercise.ID_LEVEL,
|
||||||
exercise.ID_ADMIN_EXERCISE
|
exercise.ID_ADMIN_EXERCISE
|
||||||
);
|
);
|
||||||
|
|
@ -194,7 +171,7 @@ export const updateTrueFalseExerciseById = async (req, res) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!trueFalse) {
|
if (!trueFalse) {
|
||||||
clearFileBuffers({ video, image, audio });
|
clearFileBuffers({ IMAGE, AUDIO });
|
||||||
await transaction.rollback();
|
await transaction.rollback();
|
||||||
return response(404, null, "True/False exercise not found", res);
|
return response(404, null, "True/False exercise not found", res);
|
||||||
}
|
}
|
||||||
|
|
@ -215,8 +192,8 @@ export const updateTrueFalseExerciseById = async (req, res) => {
|
||||||
response(200, payload, "True/False exercise updated successfully", res);
|
response(200, payload, "True/False exercise updated successfully", res);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
clearFileBuffers({ video, image, audio });
|
clearFileBuffers({ IMAGE, AUDIO });
|
||||||
await transaction.rollback();
|
await transaction.rollback();
|
||||||
response(500, null, "Internal Server Error", res);
|
response(500, null, "Internal Server Error", res);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -129,7 +129,7 @@ export const getUserById = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
|
|
||||||
const user = await models.User.findByPk(id, {
|
const userWithDetails = await models.User.findByPk(id, {
|
||||||
attributes: {
|
attributes: {
|
||||||
exclude: ["PASSWORD"],
|
exclude: ["PASSWORD"],
|
||||||
},
|
},
|
||||||
|
|
@ -143,32 +143,45 @@ export const getUserById = async (req, res) => {
|
||||||
model: models.Student,
|
model: models.Student,
|
||||||
as: "students",
|
as: "students",
|
||||||
attributes: ["NISN"],
|
attributes: ["NISN"],
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: models.Class,
|
||||||
|
as: "studentClass",
|
||||||
|
attributes: ["NAME_CLASS"],
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user) {
|
if (!userWithDetails) {
|
||||||
return response(404, null, "User not found", res);
|
return response(404, null, "User not found", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
let additionalField = null;
|
let additionalField = null;
|
||||||
if (user.ROLE === "teacher") {
|
if (userWithDetails.ROLE === "teacher") {
|
||||||
additionalField = { NIP: user.teachers.NIP };
|
additionalField = { NIP: userWithDetails.teachers.NIP };
|
||||||
} else if (user.ROLE === "student") {
|
} else if (userWithDetails.ROLE === "student") {
|
||||||
additionalField = { NISN: user.students.NISN };
|
additionalField = {
|
||||||
|
NISN: userWithDetails.students.NISN,
|
||||||
|
NAME_CLASS: userWithDetails.students.studentClass
|
||||||
|
? userWithDetails.students.studentClass.NAME_CLASS
|
||||||
|
: null,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const responseObject = {
|
const responseObject = {
|
||||||
ID: user.ID,
|
ID: userWithDetails.ID,
|
||||||
NAME_USERS: user.NAME_USERS,
|
NAME_USERS: userWithDetails.NAME_USERS,
|
||||||
EMAIL: user.EMAIL,
|
EMAIL: userWithDetails.EMAIL,
|
||||||
ROLE: user.ROLE,
|
ROLE: userWithDetails.ROLE,
|
||||||
...additionalField,
|
...additionalField,
|
||||||
|
PICTURE: userWithDetails.PICTURE,
|
||||||
};
|
};
|
||||||
|
|
||||||
response(200, responseObject, "Success", res);
|
response(200, responseObject, "Success", res);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.error(error);
|
||||||
response(500, null, "Error retrieving user data!", res);
|
response(500, null, "Error retrieving user data!", res);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -177,7 +190,7 @@ export const getUserByName = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { name } = req.params;
|
const { name } = req.params;
|
||||||
|
|
||||||
const user = await models.User.findOne({
|
const userWithDetails = await models.User.findOne({
|
||||||
where: { NAME_USERS: name },
|
where: { NAME_USERS: name },
|
||||||
attributes: {
|
attributes: {
|
||||||
exclude: ["PASSWORD"],
|
exclude: ["PASSWORD"],
|
||||||
|
|
@ -192,44 +205,57 @@ export const getUserByName = async (req, res) => {
|
||||||
model: models.Student,
|
model: models.Student,
|
||||||
as: "students",
|
as: "students",
|
||||||
attributes: ["NISN"],
|
attributes: ["NISN"],
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: models.Class,
|
||||||
|
as: "studentClass",
|
||||||
|
attributes: ["NAME_CLASS"],
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user) {
|
if (!userWithDetails) {
|
||||||
return response(404, null, "User not found", res);
|
return response(404, null, "User not found", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
let additionalField = null;
|
let additionalField = null;
|
||||||
if (user.ROLE === "teacher" ) {
|
if (userWithDetails.ROLE === "teacher") {
|
||||||
additionalField = { NIP: user.teachers.NIP };
|
additionalField = { NIP: userWithDetails.teachers.NIP };
|
||||||
} else if (user.ROLE === "student") {
|
} else if (userWithDetails.ROLE === "student") {
|
||||||
additionalField = { NISN: user.students.NISN };
|
additionalField = {
|
||||||
|
NISN: userWithDetails.students.NISN,
|
||||||
|
NAME_CLASS: userWithDetails.students.studentClass
|
||||||
|
? userWithDetails.students.studentClass.NAME_CLASS
|
||||||
|
: null,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const responseObject = {
|
const responseObject = {
|
||||||
ID: user.ID,
|
ID: userWithDetails.ID,
|
||||||
NAME_USERS: user.NAME_USERS,
|
NAME_USERS: userWithDetails.NAME_USERS,
|
||||||
EMAIL: user.EMAIL,
|
EMAIL: userWithDetails.EMAIL,
|
||||||
ROLE: user.ROLE,
|
ROLE: userWithDetails.ROLE,
|
||||||
...additionalField,
|
...additionalField,
|
||||||
|
PICTURE: userWithDetails.PICTURE,
|
||||||
};
|
};
|
||||||
|
|
||||||
return response(200, responseObject, "Success", res);
|
response(200, responseObject, "Success", res);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.error(error);
|
||||||
return response(500, null, "Error retrieving user data!", res);
|
response(500, null, "Error retrieving user data!", res);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const updateUserById = async (req, res) => {
|
export const updateUserById = async (req, res) => {
|
||||||
const transaction = await models.db.transaction();
|
const transaction = await models.db.transaction();
|
||||||
|
|
||||||
const { picture } = req.filesToSave || {};
|
const { PICTURE } = req.filesToSave || {};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const { name, email, nip, nisn } = req.body;
|
const { NAME_USERS, EMAIL, NIP, NISN } = req.body;
|
||||||
|
|
||||||
const user = await models.User.findByPk(id, {
|
const user = await models.User.findByPk(id, {
|
||||||
include: [
|
include: [
|
||||||
|
|
@ -248,77 +274,77 @@ export const updateUserById = async (req, res) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
clearFileBuffers({ picture });
|
clearFileBuffers({ PICTURE });
|
||||||
await transaction.rollback();
|
await transaction.rollback();
|
||||||
return response(404, null, "User not found", res);
|
return response(404, null, "User not found", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user.ROLE === "teacher" && nisn) {
|
if (user.ROLE === "teacher" && NISN) {
|
||||||
clearFileBuffers({ picture });
|
clearFileBuffers({ PICTURE });
|
||||||
await transaction.rollback();
|
await transaction.rollback();
|
||||||
return response(400, null, "Role is teacher, but NISN is provided", res);
|
return response(400, null, "Role is teacher, but NISN is provided", res);
|
||||||
}
|
}
|
||||||
if (user.ROLE === "student" && nip) {
|
if (user.ROLE === "student" && NIP) {
|
||||||
clearFileBuffers({ picture });
|
clearFileBuffers({ PICTURE });
|
||||||
await transaction.rollback();
|
await transaction.rollback();
|
||||||
return response(400, null, "Role is student, but NIP is provided", res);
|
return response(400, null, "Role is student, but NIP is provided", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (email && email !== user.EMAIL) {
|
if (EMAIL && EMAIL !== user.EMAIL) {
|
||||||
const emailExists = await models.User.findOne({
|
const emailExists = await models.User.findOne({
|
||||||
where: { EMAIL: email },
|
where: { EMAIL: EMAIL },
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
if (emailExists) {
|
if (emailExists) {
|
||||||
clearFileBuffers({ picture });
|
clearFileBuffers({ PICTURE });
|
||||||
await transaction.rollback();
|
await transaction.rollback();
|
||||||
return response(400, null, "Email already in use", res);
|
return response(400, null, "Email already in use", res);
|
||||||
}
|
}
|
||||||
user.EMAIL = email;
|
user.EMAIL = EMAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
user.NAME_USERS = name || user.NAME_USERS;
|
user.NAME_USERS = NAME_USERS || user.NAME_USERS;
|
||||||
|
|
||||||
if (user.ROLE === "teacher" && nip) {
|
if (user.ROLE === "teacher" && NIP) {
|
||||||
let teacher = await models.Teacher.findOne({
|
let teacher = await models.Teacher.findOne({
|
||||||
where: { ID: id },
|
where: { ID: id },
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
if (teacher) {
|
if (teacher) {
|
||||||
teacher.NIP = nip;
|
teacher.NIP = NIP;
|
||||||
await teacher.save({ transaction });
|
await teacher.save({ transaction });
|
||||||
} else {
|
} else {
|
||||||
teacher = await models.Teacher.create(
|
teacher = await models.Teacher.create(
|
||||||
{ ID: id, NIP: nip },
|
{ ID: id, NIP: NIP },
|
||||||
{ transaction }
|
{ transaction }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user.ROLE === "student" && nisn) {
|
if (user.ROLE === "student" && NISN) {
|
||||||
let student = await models.Student.findOne({
|
let student = await models.Student.findOne({
|
||||||
where: { ID: id },
|
where: { ID: id },
|
||||||
transaction,
|
transaction,
|
||||||
});
|
});
|
||||||
if (student) {
|
if (student) {
|
||||||
student.NISN = nisn;
|
student.NISN = NISN;
|
||||||
await student.save({ transaction });
|
await student.save({ transaction });
|
||||||
} else {
|
} else {
|
||||||
student = await models.Student.create(
|
student = await models.Student.create(
|
||||||
{ ID: id, NISN: nisn },
|
{ ID: id, NISN: NISN },
|
||||||
{ transaction }
|
{ transaction }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (picture) {
|
if (PICTURE) {
|
||||||
if (user.PICTURE) {
|
if (user.PICTURE) {
|
||||||
const oldPicturePath = path.join("public/uploads/avatar", user.PICTURE);
|
const oldPicturePath = path.join("public/uploads/avatar", user.PICTURE);
|
||||||
if (fs.existsSync(oldPicturePath)) {
|
if (fs.existsSync(oldPicturePath)) {
|
||||||
fs.unlinkSync(oldPicturePath);
|
fs.unlinkSync(oldPicturePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
user.PICTURE = saveFileToDisk(picture, user.ID, user.NAME_USERS);
|
user.PICTURE = saveFileToDisk(PICTURE, user.ID, user.NAME_USERS);
|
||||||
}
|
}
|
||||||
|
|
||||||
await user.save({ transaction });
|
await user.save({ transaction });
|
||||||
|
|
@ -341,9 +367,23 @@ export const updateUserById = async (req, res) => {
|
||||||
|
|
||||||
await transaction.commit();
|
await transaction.commit();
|
||||||
|
|
||||||
return response(200, user, "User updated successfully", res);
|
let userResponse = {
|
||||||
|
ID: user.ID,
|
||||||
|
NAME_USERS: user.NAME_USERS,
|
||||||
|
EMAIL: user.EMAIL,
|
||||||
|
ROLE: user.ROLE,
|
||||||
|
PICTURE: user.PICTURE,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (user.ROLE === "student" && user.students) {
|
||||||
|
userResponse.NISN = user.students.NISN;
|
||||||
|
} else if (user.ROLE === "teacher" && user.teachers) {
|
||||||
|
userResponse.NIP = user.teachers.NIP;
|
||||||
|
}
|
||||||
|
|
||||||
|
return response(200, userResponse, "User updated successfully", res);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
clearFileBuffers({ picture });
|
clearFileBuffers({ PICTURE });
|
||||||
await transaction.rollback();
|
await transaction.rollback();
|
||||||
console.log(error);
|
console.log(error);
|
||||||
return response(500, null, "Internal Server Error", res);
|
return response(500, null, "Internal Server Error", res);
|
||||||
|
|
@ -353,13 +393,13 @@ export const updateUserById = async (req, res) => {
|
||||||
export const updateUserPasswordById = async (req, res) => {
|
export const updateUserPasswordById = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const { oldPassword, password, confirmPassword } = req.body;
|
const { OLD_PASSWORD, PASSWORD, CONFIRM_PASSWORD } = req.body;
|
||||||
|
|
||||||
if (!oldPassword || !password || !confirmPassword) {
|
if (!OLD_PASSWORD || !PASSWORD || !CONFIRM_PASSWORD) {
|
||||||
return response(400, null, "All fields must be filled.", res);
|
return response(400, null, "All fields must be filled.", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (password !== confirmPassword) {
|
if (PASSWORD !== CONFIRM_PASSWORD) {
|
||||||
return response(
|
return response(
|
||||||
400,
|
400,
|
||||||
null,
|
null,
|
||||||
|
|
@ -373,13 +413,13 @@ export const updateUserPasswordById = async (req, res) => {
|
||||||
return response(404, null, "User not found.", res);
|
return response(404, null, "User not found.", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
const isMatch = await bcrypt.compare(oldPassword, user.PASSWORD);
|
const isMatch = await bcrypt.compare(OLD_PASSWORD, user.PASSWORD);
|
||||||
if (!isMatch) {
|
if (!isMatch) {
|
||||||
return response(400, null, "Incorrect old password.", res);
|
return response(400, null, "Incorrect old password.", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
const salt = await bcrypt.genSalt(10);
|
const salt = await bcrypt.genSalt(10);
|
||||||
const hashedPassword = await bcrypt.hash(password, salt);
|
const hashedPassword = await bcrypt.hash(PASSWORD, salt);
|
||||||
|
|
||||||
user.PASSWORD = hashedPassword;
|
user.PASSWORD = hashedPassword;
|
||||||
await user.save();
|
await user.save();
|
||||||
|
|
@ -422,23 +462,29 @@ export const deleteUserById = async (req, res) => {
|
||||||
|
|
||||||
export const getMe = async (req, res) => {
|
export const getMe = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const user = req.user; // User object from verifyLoginUser middleware
|
const user = req.user;
|
||||||
|
|
||||||
// Retrieve teacher or student details based on the user's role
|
|
||||||
const userWithDetails = await models.User.findByPk(user.ID, {
|
const userWithDetails = await models.User.findByPk(user.ID, {
|
||||||
attributes: {
|
attributes: {
|
||||||
exclude: ["PASSWORD"], // Exclude sensitive information
|
exclude: ["PASSWORD"],
|
||||||
},
|
},
|
||||||
include: [
|
include: [
|
||||||
{
|
{
|
||||||
model: models.Teacher,
|
model: models.Teacher,
|
||||||
as: "teachers",
|
as: "teachers",
|
||||||
attributes: ["NIP"], // Include NIP for teacher
|
attributes: ["NIP"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
model: models.Student,
|
model: models.Student,
|
||||||
as: "students",
|
as: "students",
|
||||||
attributes: ["NISN"], // Include NISN for student
|
attributes: ["NISN"],
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: models.Class,
|
||||||
|
as: "studentClass",
|
||||||
|
attributes: ["NAME_CLASS"],
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
@ -447,27 +493,30 @@ export const getMe = async (req, res) => {
|
||||||
return response(404, null, "User not found", res);
|
return response(404, null, "User not found", res);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine additional field based on user role
|
|
||||||
let additionalField = null;
|
let additionalField = null;
|
||||||
if (userWithDetails.ROLE === "teacher") {
|
if (userWithDetails.ROLE === "teacher") {
|
||||||
additionalField = { NIP: userWithDetails.teachers.NIP };
|
additionalField = { NIP: userWithDetails.teachers.NIP };
|
||||||
} else if (userWithDetails.ROLE === "student") {
|
} else if (userWithDetails.ROLE === "student") {
|
||||||
additionalField = { NISN: userWithDetails.students.NISN };
|
additionalField = {
|
||||||
|
NISN: userWithDetails.students.NISN,
|
||||||
|
NAME_CLASS: userWithDetails.students.studentClass
|
||||||
|
? userWithDetails.students.studentClass.NAME_CLASS
|
||||||
|
: null,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct the response object
|
|
||||||
const responseObject = {
|
const responseObject = {
|
||||||
ID: userWithDetails.ID,
|
ID: userWithDetails.ID,
|
||||||
NAME_USERS: userWithDetails.NAME_USERS,
|
NAME_USERS: userWithDetails.NAME_USERS,
|
||||||
EMAIL: userWithDetails.EMAIL,
|
EMAIL: userWithDetails.EMAIL,
|
||||||
ROLE: userWithDetails.ROLE,
|
ROLE: userWithDetails.ROLE,
|
||||||
...additionalField,
|
...additionalField,
|
||||||
|
PICTURE: userWithDetails.PICTURE,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Send the response
|
|
||||||
response(200, responseObject, "Success", res);
|
response(200, responseObject, "Success", res);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
response(500, null, "Error retrieving user data!", res);
|
response(500, null, "Error retrieving user data!", res);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -10,20 +10,7 @@ const fileFilter = (req, file, cb) => {
|
||||||
const ext = path.extname(file.originalname).toLowerCase();
|
const ext = path.extname(file.originalname).toLowerCase();
|
||||||
|
|
||||||
switch (file.fieldname) {
|
switch (file.fieldname) {
|
||||||
case "video":
|
case "AUDIO":
|
||||||
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") {
|
if (ext === ".mp3") {
|
||||||
cb(null, true);
|
cb(null, true);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -36,7 +23,7 @@ const fileFilter = (req, file, cb) => {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "image":
|
case "IMAGE":
|
||||||
if (ext === ".jpg" || ext === ".jpeg" || ext === ".png") {
|
if (ext === ".jpg" || ext === ".jpeg" || ext === ".png") {
|
||||||
cb(null, true);
|
cb(null, true);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -61,9 +48,8 @@ const upload = multer({
|
||||||
fileSize: 100 * 1024 * 1024,
|
fileSize: 100 * 1024 * 1024,
|
||||||
},
|
},
|
||||||
}).fields([
|
}).fields([
|
||||||
{ name: "video", maxCount: 1 },
|
{ name: "AUDIO", maxCount: 1 },
|
||||||
{ name: "audio", maxCount: 1 },
|
{ name: "IMAGE", maxCount: 1 },
|
||||||
{ name: "image", maxCount: 1 },
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const handleUpload = (req, res, next) => {
|
const handleUpload = (req, res, next) => {
|
||||||
|
|
@ -73,42 +59,35 @@ const handleUpload = (req, res, next) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const files = req.files;
|
const files = req.files;
|
||||||
const video = files?.video ? files.video[0] : null;
|
const AUDIO = files?.AUDIO ? files.AUDIO[0] : null;
|
||||||
const audio = files?.audio ? files.audio[0] : null;
|
const IMAGE = files?.IMAGE ? files.IMAGE[0] : null;
|
||||||
const image = files?.image ? files.image[0] : null;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let validFiles = true;
|
let validFiles = true;
|
||||||
let errorMessages = [];
|
let errorMessages = [];
|
||||||
|
|
||||||
if (video && video.size > 30 * 1024 * 1024) {
|
if (AUDIO && AUDIO.size > 10 * 1024 * 1024) {
|
||||||
validFiles = false;
|
validFiles = false;
|
||||||
video.buffer = null;
|
AUDIO.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");
|
errorMessages.push("Audio file exceeds the size limit of 10MB");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (image && image.size > 5 * 1024 * 1024) {
|
if (IMAGE && IMAGE.size > 5 * 1024 * 1024) {
|
||||||
validFiles = false;
|
validFiles = false;
|
||||||
image.buffer = null;
|
IMAGE.buffer = null;
|
||||||
errorMessages.push("Image file exceeds the size limit of 5MB");
|
errorMessages.push("Image file exceeds the size limit of 5MB");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (validFiles) {
|
if (validFiles) {
|
||||||
req.filesToSave = { video, audio, image };
|
req.filesToSave = { AUDIO, IMAGE };
|
||||||
next();
|
next();
|
||||||
} else {
|
} else {
|
||||||
clearFileBuffers({ video, audio, image });
|
clearFileBuffers({ AUDIO, IMAGE });
|
||||||
return response(400, null, errorMessages.join("; "), res);
|
return response(400, null, errorMessages.join("; "), res);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
clearFileBuffers({ video, audio, image });
|
clearFileBuffers({ AUDIO, IMAGE });
|
||||||
return response(500, null, "Internal Server Error", res);
|
return response(500, null, "Internal Server Error", res);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -136,13 +115,10 @@ export const saveFileToDisk = (file, type, topicId, sectionId, levelId) => {
|
||||||
|
|
||||||
let folderPath;
|
let folderPath;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "video":
|
case "AUDIO":
|
||||||
folderPath = path.join("public/uploads/level/video");
|
|
||||||
break;
|
|
||||||
case "audio":
|
|
||||||
folderPath = path.join("public/uploads/level/audio");
|
folderPath = path.join("public/uploads/level/audio");
|
||||||
break;
|
break;
|
||||||
case "image":
|
case "IMAGE":
|
||||||
folderPath = path.join("public/uploads/level/image");
|
folderPath = path.join("public/uploads/level/image");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ export const verifyLoginUser = async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const decoded = jwt.verify(token, process.env.ACCESS_TOKEN_SECRET);
|
const decoded = jwt.verify(token, process.env.ACCESS_TOKEN_SECRET);
|
||||||
|
|
||||||
const user = await models.User.findByPk(decoded.id, {
|
const user = await models.User.findByPk(decoded.ID, {
|
||||||
attributes: {
|
attributes: {
|
||||||
exclude: ["PASSWORD"],
|
exclude: ["PASSWORD"],
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ const upload = multer({
|
||||||
storage: memoryStorage,
|
storage: memoryStorage,
|
||||||
fileFilter,
|
fileFilter,
|
||||||
limits: { fileSize: 5 * 1024 * 1024 },
|
limits: { fileSize: 5 * 1024 * 1024 },
|
||||||
}).fields([{ name: "picture", maxCount: 1 }]);
|
}).fields([{ name: "PICTURE", maxCount: 1 }]);
|
||||||
|
|
||||||
const handleUpload = (req, res, next) => {
|
const handleUpload = (req, res, next) => {
|
||||||
upload(req, res, (err) => {
|
upload(req, res, (err) => {
|
||||||
|
|
@ -33,23 +33,23 @@ const handleUpload = (req, res, next) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const files = req.files;
|
const files = req.files;
|
||||||
const picture = files?.picture ? files.picture[0] : null;
|
const PICTURE = files?.PICTURE ? files.PICTURE[0] : null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let validFiles = true;
|
let validFiles = true;
|
||||||
let errorMessages = [];
|
let errorMessages = [];
|
||||||
|
|
||||||
if (picture && picture.size > 5 * 1024 * 1024) {
|
if (PICTURE && PICTURE.size > 5 * 1024 * 1024) {
|
||||||
validFiles = false;
|
validFiles = false;
|
||||||
picture.buffer = null;
|
PICTURE.buffer = null;
|
||||||
errorMessages.push("Picture file exceeds the size limit of 5MB");
|
errorMessages.push("Picture file exceeds the size limit of 5MB");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (validFiles) {
|
if (validFiles) {
|
||||||
req.filesToSave = { picture };
|
req.filesToSave = { PICTURE };
|
||||||
next();
|
next();
|
||||||
} else {
|
} else {
|
||||||
clearFileBuffers({ picture });
|
clearFileBuffers({ PICTURE });
|
||||||
return response(400, null, errorMessages.join(", "), res);
|
return response(400, null, errorMessages.join(", "), res);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -10,20 +10,7 @@ const fileFilter = (req, file, cb) => {
|
||||||
const ext = path.extname(file.originalname).toLowerCase();
|
const ext = path.extname(file.originalname).toLowerCase();
|
||||||
|
|
||||||
switch (file.fieldname) {
|
switch (file.fieldname) {
|
||||||
case "video":
|
case "AUDIO":
|
||||||
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") {
|
if (ext === ".mp3") {
|
||||||
cb(null, true);
|
cb(null, true);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -36,7 +23,7 @@ const fileFilter = (req, file, cb) => {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "image":
|
case "IMAGE":
|
||||||
if (ext === ".jpg" || ext === ".jpeg" || ext === ".png") {
|
if (ext === ".jpg" || ext === ".jpeg" || ext === ".png") {
|
||||||
cb(null, true);
|
cb(null, true);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -61,9 +48,8 @@ const upload = multer({
|
||||||
fileSize: 100 * 1024 * 1024,
|
fileSize: 100 * 1024 * 1024,
|
||||||
},
|
},
|
||||||
}).fields([
|
}).fields([
|
||||||
{ name: "video", maxCount: 1 },
|
{ name: "AUDIO", maxCount: 1 },
|
||||||
{ name: "audio", maxCount: 1 },
|
{ name: "IMAGE", maxCount: 1 },
|
||||||
{ name: "image", maxCount: 1 },
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const handleUpload = (req, res, next) => {
|
const handleUpload = (req, res, next) => {
|
||||||
|
|
@ -73,42 +59,35 @@ const handleUpload = (req, res, next) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const files = req.files;
|
const files = req.files;
|
||||||
const video = files?.video ? files.video[0] : null;
|
const AUDIO = files?.AUDIO ? files.AUDIO[0] : null;
|
||||||
const audio = files?.audio ? files.audio[0] : null;
|
const IMAGE = files?.IMAGE ? files.IMAGE[0] : null;
|
||||||
const image = files?.image ? files.image[0] : null;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let validFiles = true;
|
let validFiles = true;
|
||||||
let errorMessages = [];
|
let errorMessages = [];
|
||||||
|
|
||||||
if (video && video.size > 30 * 1024 * 1024) {
|
if (AUDIO && AUDIO.size > 10 * 1024 * 1024) {
|
||||||
validFiles = false;
|
validFiles = false;
|
||||||
video.buffer = null;
|
AUDIO.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");
|
errorMessages.push("Audio file exceeds the size limit of 10MB");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (image && image.size > 5 * 1024 * 1024) {
|
if (IMAGE && IMAGE.size > 5 * 1024 * 1024) {
|
||||||
validFiles = false;
|
validFiles = false;
|
||||||
image.buffer = null;
|
IMAGE.buffer = null;
|
||||||
errorMessages.push("Image file exceeds the size limit of 5MB");
|
errorMessages.push("Image file exceeds the size limit of 5MB");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (validFiles) {
|
if (validFiles) {
|
||||||
req.filesToSave = { video, audio, image };
|
req.filesToSave = { AUDIO, IMAGE };
|
||||||
next();
|
next();
|
||||||
} else {
|
} else {
|
||||||
clearFileBuffers({ video, audio, image });
|
clearFileBuffers({ AUDIO, IMAGE });
|
||||||
return response(400, null, errorMessages.join("; "), res);
|
return response(400, null, errorMessages.join("; "), res);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
clearFileBuffers({ video, audio, image });
|
clearFileBuffers({ video, AUDIO, IMAGE });
|
||||||
return response(500, null, "Internal Server Error", res);
|
return response(500, null, "Internal Server Error", res);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -136,13 +115,10 @@ export const saveFileToDisk = (file, type, levelId, exerciseId) => {
|
||||||
|
|
||||||
let folderPath;
|
let folderPath;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "video":
|
case "AUDIO":
|
||||||
folderPath = path.join("public/uploads/exercise/video");
|
|
||||||
break;
|
|
||||||
case "audio":
|
|
||||||
folderPath = path.join("public/uploads/exercise/audio");
|
folderPath = path.join("public/uploads/exercise/audio");
|
||||||
break;
|
break;
|
||||||
case "image":
|
case "IMAGE":
|
||||||
folderPath = path.join("public/uploads/exercise/image");
|
folderPath = path.join("public/uploads/exercise/image");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ const upload = multer({
|
||||||
storage: memoryStorage,
|
storage: memoryStorage,
|
||||||
fileFilter,
|
fileFilter,
|
||||||
limits: { fileSize: 5 * 1024 * 1024 },
|
limits: { fileSize: 5 * 1024 * 1024 },
|
||||||
}).fields([{ name: "thumbnail", maxCount: 1 }]);
|
}).fields([{ name: "THUMBNAIL", maxCount: 1 }]);
|
||||||
|
|
||||||
const handleUpload = (req, res, next) => {
|
const handleUpload = (req, res, next) => {
|
||||||
upload(req, res, (err) => {
|
upload(req, res, (err) => {
|
||||||
|
|
@ -33,28 +33,28 @@ const handleUpload = (req, res, next) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const files = req.files;
|
const files = req.files;
|
||||||
const thumbnail = files?.thumbnail ? files.thumbnail[0] : null;
|
const THUMBNAIL = files?.THUMBNAIL ? files.THUMBNAIL[0] : null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let validFiles = true;
|
let validFiles = true;
|
||||||
let errorMessages = [];
|
let errorMessages = [];
|
||||||
|
|
||||||
if (thumbnail && thumbnail.size > 5 * 1024 * 1024) {
|
if (THUMBNAIL && THUMBNAIL.size > 5 * 1024 * 1024) {
|
||||||
validFiles = false;
|
validFiles = false;
|
||||||
thumbnail.buffer = null;
|
THUMBNAIL.buffer = null;
|
||||||
errorMessages.push("Thumbnail file exceeds the size limit of 5MB");
|
errorMessages.push("Thumbnail file exceeds the size limit of 5MB");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (validFiles) {
|
if (validFiles) {
|
||||||
req.filesToSave = { thumbnail };
|
req.filesToSave = { THUMBNAIL };
|
||||||
next();
|
next();
|
||||||
} else {
|
} else {
|
||||||
clearFileBuffers({ thumbnail });
|
clearFileBuffers({ THUMBNAIL });
|
||||||
return response(400, null, errorMessages.join(", "), res);
|
return response(400, null, errorMessages.join(", "), res);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
clearFileBuffers({ thumbnail });
|
clearFileBuffers({ THUMBNAIL });
|
||||||
return response(500, null, "Internal Server Error", res);
|
return response(500, null, "Internal Server Error", res);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,13 @@ const TopicModel = (DataTypes) => {
|
||||||
notEmpty: true,
|
notEmpty: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
OBJECTIVES: {
|
||||||
|
type: DataTypes.STRING(1024),
|
||||||
|
allowNull: false,
|
||||||
|
validate: {
|
||||||
|
notEmpty: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
TIME_TOPIC: {
|
TIME_TOPIC: {
|
||||||
type: DataTypes.DATE,
|
type: DataTypes.DATE,
|
||||||
allowNull: true,
|
allowNull: true,
|
||||||
|
|
|
||||||
BIN
public/uploads/avatar/user-8087dbf0bf98571ae2d8db93f96c51b3.jpeg
Normal file
|
After Width: | Height: | Size: 7.7 KiB |
BIN
public/uploads/avatar/user-899f0c3a99d44dc1c8ba0ef5b9313b9d.jpeg
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
public/uploads/avatar/user-d3b55e105e3f6aaae08de98c6d4fb98d.jpeg
Normal file
|
After Width: | Height: | Size: 7.7 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
|
@ -1,5 +1,5 @@
|
||||||
import express from "express";
|
import express from "express";
|
||||||
import { getExercises, getExercisesForAdmin, getExerciseById, deleteExerciseById, deleteExerciseFileById } from "../../controllers/contentControllers/exercise.js";
|
import { getExercises, getExercisesForAdmin, getExerciseById, getExerciseByLevelId, deleteExerciseById, deleteExerciseFileById } from "../../controllers/contentControllers/exercise.js";
|
||||||
import { createMultipleChoicesExercise, updateMultipleChoicesExerciseById } from "../../controllers/exerciseTypesControllers/multipleChoices.js";
|
import { createMultipleChoicesExercise, updateMultipleChoicesExerciseById } from "../../controllers/exerciseTypesControllers/multipleChoices.js";
|
||||||
import { createMatchingPairsExercise, updateMatchingPairsExerciseById } from "../../controllers/exerciseTypesControllers/matchingPairs.js";
|
import { createMatchingPairsExercise, updateMatchingPairsExerciseById } from "../../controllers/exerciseTypesControllers/matchingPairs.js";
|
||||||
import { createTrueFalseExercise, updateTrueFalseExerciseById } from "../../controllers/exerciseTypesControllers/trueFalse.js";
|
import { createTrueFalseExercise, updateTrueFalseExerciseById } from "../../controllers/exerciseTypesControllers/trueFalse.js";
|
||||||
|
|
@ -13,6 +13,8 @@ router.get("/exercise", verifyLoginUser, getExercises);
|
||||||
|
|
||||||
router.get("/exercise/admin", verifyLoginUser, adminOnly, getExercisesForAdmin);
|
router.get("/exercise/admin", verifyLoginUser, adminOnly, getExercisesForAdmin);
|
||||||
|
|
||||||
|
router.get("/exercise/level/:idLevel", verifyLoginUser, getExerciseByLevelId);
|
||||||
|
|
||||||
router.get("/exercise/:id", verifyLoginUser, getExerciseById);
|
router.get("/exercise/:id", verifyLoginUser, getExerciseById);
|
||||||
|
|
||||||
router.post("/exercise/multiple-choices", verifyLoginUser, adminOnly, handleUpload, createMultipleChoicesExercise);
|
router.post("/exercise/multiple-choices", verifyLoginUser, adminOnly, handleUpload, createMultipleChoicesExercise);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import express from "express";
|
import express from "express";
|
||||||
// import { getAllLevels, getAllLevelById, getLevels, getLevelById, createLevel, updateLevelById, deleteLevelById, getRoutes, getRouteById, updateRouteById } from "../controllers/level.js";
|
import { getLevels, getLevelById, getLevelsByTopicId, createLevel, updateLevelById, deleteLevelById, getPreviousLevel } from "../../controllers/contentControllers/level.js";
|
||||||
import { getLevels, getLevelById, createLevel, updateLevelById, deleteLevelById } from "../../controllers/contentControllers/level.js";
|
|
||||||
import { verifyLoginUser, adminOnly } from "../../middlewares/User/authUser.js";
|
import { verifyLoginUser, adminOnly } from "../../middlewares/User/authUser.js";
|
||||||
import handleUpload from '../../middlewares/Level/uploadLevel.js';
|
import handleUpload from '../../middlewares/Level/uploadLevel.js';
|
||||||
import {checkLevelsPerTopic, autoCalculateRoutes, getSectionAndTopicByLevelId } from '../../middlewares/Level/checkLevel.js';
|
import {checkLevelsPerTopic, autoCalculateRoutes, getSectionAndTopicByLevelId } from '../../middlewares/Level/checkLevel.js';
|
||||||
|
|
@ -8,14 +7,14 @@ import {checkLevelsPerTopic, autoCalculateRoutes, getSectionAndTopicByLevelId }
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
// router.get("/levels", verifyLoginUser, adminOnly, getAllLevels);
|
|
||||||
|
|
||||||
// router.get("/levels/:id", verifyLoginUser, adminOnly, getAllLevelById);
|
|
||||||
|
|
||||||
router.get("/level", verifyLoginUser, getLevels);
|
router.get("/level", verifyLoginUser, getLevels);
|
||||||
|
|
||||||
|
router.get("/level/topic/:idTopic", verifyLoginUser, getLevelsByTopicId);
|
||||||
|
|
||||||
router.get("/level/:id", verifyLoginUser, getLevelById);
|
router.get("/level/:id", verifyLoginUser, getLevelById);
|
||||||
|
|
||||||
|
router.get("/previous/level/:next_learning", verifyLoginUser, getPreviousLevel);
|
||||||
|
|
||||||
router.post("/level", verifyLoginUser, adminOnly, handleUpload, checkLevelsPerTopic, autoCalculateRoutes, createLevel);
|
router.post("/level", verifyLoginUser, adminOnly, handleUpload, checkLevelsPerTopic, autoCalculateRoutes, createLevel);
|
||||||
|
|
||||||
router.put("/level/:id", verifyLoginUser, adminOnly, handleUpload, getSectionAndTopicByLevelId, autoCalculateRoutes, updateLevelById);
|
router.put("/level/:id", verifyLoginUser, adminOnly, handleUpload, getSectionAndTopicByLevelId, autoCalculateRoutes, updateLevelById);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import express from "express";
|
import express from "express";
|
||||||
import { getTopics, getTopicById, createTopic, updateTopicById, deleteTopicById } from "../../controllers/contentControllers/topic.js";
|
import { getTopics, getTopicById, getTopicBySectionId, createTopic, updateTopicById, deleteTopicById, getCompletedTopics } from "../../controllers/contentControllers/topic.js";
|
||||||
import { verifyLoginUser, adminOnly } from "../../middlewares/User/authUser.js";
|
import { verifyLoginUser, adminOnly } from "../../middlewares/User/authUser.js";
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -7,6 +7,10 @@ const router = express.Router();
|
||||||
|
|
||||||
router.get("/topic", verifyLoginUser, getTopics);
|
router.get("/topic", verifyLoginUser, getTopics);
|
||||||
|
|
||||||
|
router.get("/topic/complete", verifyLoginUser, getCompletedTopics);
|
||||||
|
|
||||||
|
router.get("/topic/section/:sectionId", verifyLoginUser, getTopicBySectionId);
|
||||||
|
|
||||||
router.get("/topic/:id", verifyLoginUser, getTopicById);
|
router.get("/topic/:id", verifyLoginUser, getTopicById);
|
||||||
|
|
||||||
router.post("/topic", verifyLoginUser, adminOnly, createTopic);
|
router.post("/topic", verifyLoginUser, adminOnly, createTopic);
|
||||||
|
|
|
||||||
|
|
@ -20,9 +20,9 @@ router.get("/user/name/:name", verifyLoginUser, adminOrTeacherOnly, getUserByNam
|
||||||
|
|
||||||
router.get("/getMe", verifyLoginUser, getMe);
|
router.get("/getMe", verifyLoginUser, getMe);
|
||||||
|
|
||||||
router.post("/user/update/:id", verifyLoginUser, handleUpload, updateUserById);
|
router.put("/user/update/:id", verifyLoginUser, handleUpload, updateUserById);
|
||||||
|
|
||||||
router.post("/user/update/password/:id", verifyLoginUser, updateUserPasswordById);
|
router.put("/user/update/password/:id", verifyLoginUser, updateUserPasswordById);
|
||||||
|
|
||||||
router.delete("/user/delete/:id", verifyLoginUser, adminOnly, deleteUserById);
|
router.delete("/user/delete/:id", verifyLoginUser, adminOnly, deleteUserById);
|
||||||
|
|
||||||
|
|
|
||||||