first commit
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Niolla
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is proprietary and confidential.
|
||||
* Unauthorized copying, modification, distribution, or use
|
||||
* of this file, via any medium, is strictly prohibited.
|
||||
*/
|
||||
|
||||
// app/controllers/activity.controller.js
|
||||
|
||||
const db = require("../models");
|
||||
const Activity = db.UserActivity;
|
||||
|
||||
// Get all user activities
|
||||
exports.getUserActivities = async (req, res) => {
|
||||
try {
|
||||
const avt = await Activity.findAll({
|
||||
order: [["createdAt", "DESC"]],
|
||||
});
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: "User activities retrieved successfully",
|
||||
data: avt,
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "An error occurred while fetching user activities.",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Get activity by User ID
|
||||
exports.getActivitiesByUserId = async (req, res) => {
|
||||
const userId = req.params.userId;
|
||||
|
||||
try {
|
||||
const avt = await Activity.findAll({
|
||||
where: { user_id: userId },
|
||||
order: [["createdAt", "DESC"]],
|
||||
});
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: "User activities retrieved successfully",
|
||||
data: avt,
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "An error occurred while fetching user activities.",
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Niolla
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is proprietary and confidential.
|
||||
* Unauthorized copying, modification, distribution, or use
|
||||
* of this file, via any medium, is strictly prohibited.
|
||||
*/
|
||||
|
||||
// app/controllers/auth.controller.js
|
||||
|
||||
const { checkPassword } = require("../utils/hashPassword.util");
|
||||
const { sendMail } = require("../utils/mail.util");
|
||||
const { generateOTP, validateOTP } = require("../utils/otp.util");
|
||||
const {getCachedUser,clearUserCache} = require("../utils/cache.util");
|
||||
const { generateToken } = require("../utils/jwt.util");
|
||||
const { log } = require("../utils/consoleLog.utill");
|
||||
|
||||
const appName = process.env.APP_NAME || "Niolla";
|
||||
|
||||
// Login Step 1: Request OTP
|
||||
exports.loginReq = async (req, res) => {
|
||||
try {
|
||||
const { email, password } = req.body;
|
||||
|
||||
const user = await getCachedUser(email);
|
||||
if (!user) {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ success: false, message: "User Not Found" });
|
||||
}
|
||||
|
||||
const passwordIsValid = await checkPassword(password, user.password);
|
||||
if (!passwordIsValid) {
|
||||
return res
|
||||
.status(401)
|
||||
.send({ success: false, message: "Invalid Password" });
|
||||
}
|
||||
|
||||
const otp = generateOTP(email);
|
||||
|
||||
await sendMail({
|
||||
to: email,
|
||||
subject: `OTP for Your ${appName} Account`,
|
||||
templateName: "otp",
|
||||
templateVars: {
|
||||
firstName: user.firstName,
|
||||
otp: otp,
|
||||
},
|
||||
text: `Hello ${user.firstName}, your otp is ${otp}`,
|
||||
});
|
||||
|
||||
log(`OTP for ${email}: ${otp}`);
|
||||
log(`OTP sent to ${email} successfully.`);
|
||||
|
||||
res.status(201).send({ success: true, message: "OTP Sent Successfully" });
|
||||
} catch (error) {
|
||||
log("Error occurred while sending OTP:", error);
|
||||
res.status(500).send({ success: false, message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// Login Step 2: Verify OTP and issue JWT
|
||||
exports.login = async (req, res) => {
|
||||
try {
|
||||
const { email, otp } = req.body;
|
||||
|
||||
const user = await getCachedUser(email);
|
||||
|
||||
if (!user) {
|
||||
return res
|
||||
.status(404)
|
||||
.send({ success: false, message: "User Not Found" });
|
||||
}
|
||||
|
||||
const isValidOTP = validateOTP(email, otp);
|
||||
|
||||
if (!isValidOTP) {
|
||||
return res
|
||||
.status(401)
|
||||
.send({ success: false, message: "Invalid or Expired OTP" });
|
||||
}
|
||||
|
||||
// Generate JWT token
|
||||
const token = generateToken({
|
||||
id: user.id,
|
||||
firstName: user.firstName,
|
||||
lastName: user.lastName,
|
||||
email: user.email,
|
||||
role: user.role,
|
||||
accountType: user.accountType,
|
||||
});
|
||||
|
||||
// 3. Set JWT as HttpOnly cookie
|
||||
res.cookie("access_token", token, {
|
||||
httpOnly: true, // JS cannot access
|
||||
secure: process.env.NODE_ENV === "production", // HTTPS only in prod
|
||||
sameSite: process.env.NODE_ENV === "production" ? "None" : "Lax",
|
||||
maxAge: 24 * 60 * 60 * 1000, // 1 day
|
||||
});
|
||||
|
||||
log(`JWT issued for ${email}`);
|
||||
clearUserCache(email);
|
||||
|
||||
res.status(200).send({
|
||||
success: true,
|
||||
message: "Login Successful",
|
||||
data: {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
firstName: user.firstName,
|
||||
lastName: user.lastName,
|
||||
role: user.role,
|
||||
accountType: user.accountType,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
log("Error occurred during login:", error);
|
||||
res.status(500).send({ success: false, message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
// Logout: Clear the JWT cookie
|
||||
exports.logout = (req, res) => {
|
||||
res.clearCookie("access_token", {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
sameSite: process.env.NODE_ENV === "production" ? "None" : "Lax",
|
||||
});
|
||||
|
||||
res.json({ success: true, message: "Logged out successfully" });
|
||||
};
|
||||
@@ -0,0 +1,417 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Niolla
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is proprietary and confidential.
|
||||
* Unauthorized copying, modification, distribution, or use
|
||||
* of this file, via any medium, is strictly prohibited.
|
||||
*/
|
||||
|
||||
// app/controllers/generateDocument.controller.js
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const documentQueue = require("../queues/document.queue");
|
||||
const { createDocumentData } = require("../utils/document.utill");
|
||||
const {
|
||||
generateId,
|
||||
generateDocumentReferenceNo,
|
||||
} = require("../utils/idGen.util");
|
||||
|
||||
const { GetObjectCommand, DeleteObjectCommand } = require("@aws-sdk/client-s3");
|
||||
|
||||
const {log} = require("../utils/consoleLog.utill");
|
||||
|
||||
const s3 = require("../config/s3.config");
|
||||
const db = require("../models");
|
||||
const Document = db.Document;
|
||||
const DocumentType = db.DocumentType;
|
||||
|
||||
const normalizeDocumentData = (value) => {
|
||||
if (!value) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (typeof value === "string") {
|
||||
try {
|
||||
return JSON.parse(value);
|
||||
} catch (error) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get available document types
|
||||
*/
|
||||
exports.getAvailableDocumentTypes = async (req, res) => {
|
||||
try {
|
||||
|
||||
const doc_types = await DocumentType.findAll({
|
||||
attributes: ["doc_type_name"],
|
||||
});
|
||||
|
||||
const types = doc_types.map((t) => t.doc_type_name);
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
availableDocumentTypes: types,
|
||||
note: "Use these document type names in your requests (case-insensitive)",
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
log("Error fetching document types:", error.message);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Get Saved documents
|
||||
exports.getSavedDocuments = async (req, res) => {
|
||||
try {
|
||||
const { documentType } = req.body;
|
||||
|
||||
if (!documentType) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "documentType query parameter is required",
|
||||
});
|
||||
}
|
||||
|
||||
// Fetch saved documents based on documentType
|
||||
const savedDocuments = await Document.findAll({
|
||||
where: { doc_type: documentType.toUpperCase() },
|
||||
order: [["createdAt", "DESC"]],
|
||||
exclude: ["data"],
|
||||
});
|
||||
|
||||
// extract only necessary fields to return
|
||||
const formattedDocuments = savedDocuments.map((doc) => ({
|
||||
doc_id: doc.doc_id,
|
||||
reference_no: doc.reference_no,
|
||||
doc_type: doc.doc_type,
|
||||
status: doc.status,
|
||||
createdAt: doc.createdAt,
|
||||
updatedAt: doc.updatedAt,
|
||||
}));
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
savedDocuments: formattedDocuments,
|
||||
});
|
||||
} catch (error) {
|
||||
log("Error fetching saved documents:", error.message);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Get specific document data by doc_id
|
||||
exports.getDocumentData = async (req, res) => {
|
||||
try {
|
||||
const { docId } = req.params;
|
||||
|
||||
if (!docId) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "docId parameter is required",
|
||||
});
|
||||
}
|
||||
|
||||
const document = await Document.findOne({
|
||||
where: { doc_id: docId },
|
||||
});
|
||||
|
||||
if (!document) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "Document not found",
|
||||
});
|
||||
}
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
document: {
|
||||
doc_id: document.doc_id,
|
||||
reference_no: document.reference_no,
|
||||
doc_type: document.doc_type,
|
||||
data: document.data,
|
||||
status: document.status,
|
||||
createdAt: document.createdAt,
|
||||
updatedAt: document.updatedAt,
|
||||
},
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
log("Error fetching document data:", error.message);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: error.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
exports.generateReferenceNo = async (req, res) => {
|
||||
try {
|
||||
const { documentType } = req.params;
|
||||
|
||||
if (!documentType) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "documentType is required",
|
||||
});
|
||||
}
|
||||
|
||||
// Generate reference number
|
||||
const reference_no = await generateDocumentReferenceNo(documentType);
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
reference_no,
|
||||
});
|
||||
} catch (error) {
|
||||
log("Error generating reference number:", error.message);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.generateDraftDocument = async (req, res) => {
|
||||
try {
|
||||
const { document } = req.body;
|
||||
const documentData = normalizeDocumentData(req.body.documentData || req.body.data || req.body);
|
||||
|
||||
// Validation
|
||||
if (!document || !documentData) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "document and documentData are required",
|
||||
});
|
||||
}
|
||||
|
||||
let documentDetails;
|
||||
|
||||
if (document !== "PRECOST") {
|
||||
// Save document details before generating
|
||||
documentDetails = await createDocumentData(
|
||||
document,
|
||||
documentData,
|
||||
"DRAFT",
|
||||
);
|
||||
} else {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message:
|
||||
"Invalid document type, This document type is not allowed to be generated as draft",
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(201).json({
|
||||
success: true,
|
||||
message: "Draft document created successfully",
|
||||
documentDetails,
|
||||
});
|
||||
} catch (error) {
|
||||
log("Error generating draft document:", error.message);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate document asynchronously
|
||||
* Returns jobId immediately
|
||||
*/
|
||||
exports.generateDocument = async (req, res) => {
|
||||
try {
|
||||
const { document, documentType } = req.body;
|
||||
const documentData = normalizeDocumentData(req.body.documentData || req.body.data || req.body);
|
||||
|
||||
// Validation
|
||||
if (!document || !documentType || !documentData) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "document, documentType, and documentData are required",
|
||||
});
|
||||
}
|
||||
|
||||
console.log(
|
||||
`📨 generateDocument request: document="${document}", documentType="${documentType}"`,
|
||||
);
|
||||
|
||||
if (document !== "PRECOST") {
|
||||
// Save document details before generating
|
||||
// If doc_id exists, finalize (update existing); otherwise create as DRAFT
|
||||
const status = documentData.doc_id ? "FINAL" : "DRAFT";
|
||||
await createDocumentData(document, documentData, status);
|
||||
}
|
||||
|
||||
// Add job to queue
|
||||
const job = await documentQueue.add("generate-document", {
|
||||
document,
|
||||
documentType,
|
||||
data: documentData,
|
||||
});
|
||||
|
||||
log(
|
||||
`📋 Document generation job queued: ${job.id} (document="${document}", type="${documentType}")`,
|
||||
);
|
||||
|
||||
return res.status(202).json({
|
||||
success: true,
|
||||
message: "Document generation started",
|
||||
jobId: job.id,
|
||||
});
|
||||
} catch (error) {
|
||||
log("Error queuing document generation:", error.message);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get job status and result
|
||||
*/
|
||||
exports.getJobStatus = async (req, res) => {
|
||||
try {
|
||||
const { jobId } = req.params;
|
||||
|
||||
if (!jobId) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "jobId is required",
|
||||
});
|
||||
}
|
||||
|
||||
// Get job from queue
|
||||
const job = await documentQueue.getJob(jobId);
|
||||
|
||||
if (!job) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "Job not found",
|
||||
});
|
||||
}
|
||||
|
||||
// Get job state
|
||||
const state = await job.getState();
|
||||
const result = job.returnvalue;
|
||||
const failedReason = job.failedReason;
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
jobId: job.id,
|
||||
state, // "waiting" | "active" | "completed" | "failed" | "delayed"
|
||||
result: state === "completed" ? result : null,
|
||||
error: state === "failed" ? failedReason : null,
|
||||
attempts: job.attemptsMade,
|
||||
stacktrace: job.stacktrace,
|
||||
});
|
||||
} catch (error) {
|
||||
log("Error fetching job status:", error.message);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Download generated document
|
||||
*/
|
||||
exports.downloadDocument = async (req, res) => {
|
||||
try {
|
||||
const { uuid } = req.params;
|
||||
|
||||
const key = `uploads/${uuid}.pdf`;
|
||||
|
||||
const command = new GetObjectCommand({
|
||||
Bucket: process.env.AWS_S3_BUCKET_NAME,
|
||||
Key: key,
|
||||
});
|
||||
|
||||
const response = await s3.send(command);
|
||||
|
||||
res.setHeader(
|
||||
"Content-Type",
|
||||
response.ContentType || "application/octet-stream",
|
||||
);
|
||||
|
||||
res.setHeader("Content-Disposition", `attachment; filename="${uuid}.pdf"`);
|
||||
|
||||
response.Body.pipe(res);
|
||||
|
||||
res.on("finish", async () => {
|
||||
try {
|
||||
await s3.send(
|
||||
new DeleteObjectCommand({
|
||||
Bucket: process.env.AWS_S3_BUCKET_NAME,
|
||||
Key: key,
|
||||
}),
|
||||
);
|
||||
|
||||
log(`Deleted from S3: ${key}`);
|
||||
} catch (err) {
|
||||
log(`Failed to delete ${key}:`, err.message);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
log("Download error:", error.message);
|
||||
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "Document not found",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancel/delete a job
|
||||
*/
|
||||
exports.cancelJob = async (req, res) => {
|
||||
try {
|
||||
const { jobId } = req.params;
|
||||
|
||||
if (!jobId) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "jobId is required",
|
||||
});
|
||||
}
|
||||
|
||||
const job = await documentQueue.getJob(jobId);
|
||||
|
||||
if (!job) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "Job not found",
|
||||
});
|
||||
}
|
||||
|
||||
await job.remove();
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
message: "Job cancelled successfully",
|
||||
jobId,
|
||||
});
|
||||
} catch (error) {
|
||||
log("Error cancelling job:", error.message);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Niolla
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is proprietary and confidential.
|
||||
* Unauthorized copying, modification, distribution, or use
|
||||
* of this file, via any medium, is strictly prohibited.
|
||||
*/
|
||||
|
||||
// app/controllers/notification.controller.js
|
||||
|
||||
const db = require("../models");
|
||||
const Notification = db.notification;
|
||||
const UserNotification = db.userNotification;
|
||||
|
||||
const log = require("../utils/consoleLog.utill").log;
|
||||
|
||||
// Controller for managing notifications
|
||||
exports.createNotification = async (req, res) => {
|
||||
try {
|
||||
const { notificationHeadline, notificationDescription, notificationType } =
|
||||
req.body;
|
||||
|
||||
const id = Date.now().toString(); // Generate a unique ID based on the current timestamp
|
||||
const notification_id = `notif_${id}`; // Prefix the ID with "notif_"
|
||||
|
||||
// Create a new notification
|
||||
const newNotification = await Notification.create({
|
||||
notification_id,
|
||||
notificationHeadline,
|
||||
notificationDescription,
|
||||
notificationType,
|
||||
dateCreated: new Date(),
|
||||
});
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
message: "Notification created successfully",
|
||||
notification: newNotification,
|
||||
});
|
||||
} catch (error) {
|
||||
log("Error creating notification:", error.message);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "Internal server error",
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Mark a notification as read for a user
|
||||
exports.markAsRead = async (req, res) => {
|
||||
try {
|
||||
const { userId, notificationId } = req.params;
|
||||
|
||||
// Find the user notification entry
|
||||
const userNotification = await UserNotification.findOne({
|
||||
where: { user_id: userId, notification_id: notificationId },
|
||||
});
|
||||
|
||||
if (!userNotification) {
|
||||
return res.status(404).json({ success: false, error: "User notification not found" });
|
||||
}
|
||||
|
||||
// Mark the notification as read
|
||||
userNotification.isRead = true;
|
||||
await userNotification.save();
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: "Notification marked as read",
|
||||
});
|
||||
}
|
||||
|
||||
catch (error) {
|
||||
log("Error marking notification as read:", error.message);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "Internal server error",
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Get announcements for all users
|
||||
exports.getAnnouncements = async (req, res) => {
|
||||
try {
|
||||
|
||||
// Fetch all announcements
|
||||
const announcements = await Notification.findAll({
|
||||
where: { notificationType: "ANNOUNCEMENT", isActive: true },
|
||||
});
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
announcements,
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
log("Error fetching announcements:", error.message);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "Internal server error",
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Get all notifications for a user
|
||||
exports.getUserNotifications = async (req, res) => {
|
||||
try {
|
||||
const { userId } = req.params;
|
||||
|
||||
// Fetch notifications for the user
|
||||
const notifications = await UserNotification.findAll({
|
||||
where: { user_id: userId, isRead: false },
|
||||
include: [
|
||||
{
|
||||
model: Notification,
|
||||
as: "notification",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
notifications,
|
||||
});
|
||||
}
|
||||
|
||||
catch (error) {
|
||||
log("Error fetching user notifications:", error.message);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "Internal server error",
|
||||
error: error.message
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,868 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Niolla
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is proprietary and confidential.
|
||||
* Unauthorized copying, modification, distribution, or use
|
||||
* of this file, via any medium, is strictly prohibited.
|
||||
*/
|
||||
|
||||
// app/controllers/permission.controller.js
|
||||
|
||||
|
||||
// PERMISSIONS:
|
||||
// 1. createPermission --> Done
|
||||
// 2. getAllPermissions --> Done
|
||||
// 3. getPermissionById --> Done
|
||||
// 4. updatePermission --> Done
|
||||
// 5. deletePermission --> Done
|
||||
|
||||
// ROLES:
|
||||
// 1. createRole --> Done
|
||||
// 2. createRolePermission --> Done
|
||||
// 3. getAllRoles --> Done
|
||||
// 4. getRoleById --> Done
|
||||
// 5. updateRole --> Done
|
||||
// 6. deleteRole --> Done
|
||||
|
||||
// USER PERMISSIONS:
|
||||
// 1. getUserPermissions --> Done
|
||||
|
||||
const db = require("../models");
|
||||
const User = db.User;
|
||||
const Role = db.roles;
|
||||
const Permission = db.permission;
|
||||
const RolePermission = db.rolePermission;
|
||||
const UserPermission = db.userPermission;
|
||||
|
||||
// Create a new permission
|
||||
exports.createPermission = async (req, res) => {
|
||||
try {
|
||||
const { permissionName, permissionDescription, page, module, action } = req.body;
|
||||
if (!permissionName || !page || !module || !action) {
|
||||
return res.status(400).json({ message: "Permission name, page, module, and action are required" });
|
||||
}
|
||||
|
||||
const permissionId = `${module}.${page}.${action}`;
|
||||
|
||||
// Check if permission already exists
|
||||
const existingPermission = await Permission.findOne({ where: { permission_id: permissionId } });
|
||||
if (existingPermission) {
|
||||
return res.status(400).json({ message: "Permission already exists" });
|
||||
}
|
||||
|
||||
// Create the new permission
|
||||
const newPermission = await Permission.create({
|
||||
permission_id: permissionId,
|
||||
permissionName,
|
||||
permissionDescription,
|
||||
page,
|
||||
module,
|
||||
action
|
||||
});
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
data: newPermission
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error creating permission:", error);
|
||||
res.status(500).json({ message: "Internal server error" });
|
||||
}
|
||||
};
|
||||
|
||||
// Bulk create permissions
|
||||
exports.bulkCreatePermissions = async (req, res) => {
|
||||
try {
|
||||
const { permissions } = req.body;
|
||||
|
||||
if (!permissions || !Array.isArray(permissions) || permissions.length === 0) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Permissions array is required and must not be empty"
|
||||
});
|
||||
}
|
||||
|
||||
const results = {
|
||||
created: [],
|
||||
failed: []
|
||||
};
|
||||
|
||||
for (const item of permissions) {
|
||||
try {
|
||||
const { permissionName, permissionDescription, page, module, action } = item;
|
||||
|
||||
if (!permissionName || !page || !module || !action) {
|
||||
results.failed.push({
|
||||
...item,
|
||||
error: "Permission name, page, module, and action are required"
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
const permissionId = `${module}.${page}.${action}`;
|
||||
|
||||
// Check if permission already exists
|
||||
const existingPermission = await Permission.findOne({ where: { permission_id: permissionId } });
|
||||
if (existingPermission) {
|
||||
results.failed.push({
|
||||
...item,
|
||||
error: "Permission already exists"
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
const newPermission = await Permission.create({
|
||||
permission_id: permissionId,
|
||||
permissionName,
|
||||
permissionDescription,
|
||||
page,
|
||||
module,
|
||||
action
|
||||
});
|
||||
|
||||
results.created.push(newPermission);
|
||||
|
||||
} catch (itemError) {
|
||||
results.failed.push({
|
||||
...item,
|
||||
error: itemError.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
res.status(201).json({
|
||||
success: results.failed.length === 0,
|
||||
data: results
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error bulk creating permissions:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "An error occurred while bulk creating permissions."
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Get all permissions
|
||||
exports.getAllPermissions = async (req, res) => {
|
||||
try {
|
||||
const permissions = await Permission.findAll();
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: permissions
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error fetching permissions:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "An error occurred while fetching permissions."
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Get permission by ID
|
||||
exports.getPermissionById = async (req, res) => {
|
||||
try {
|
||||
const { permissionId } = req.params;
|
||||
|
||||
const permission = await Permission.findByPk(permissionId);
|
||||
if (!permission) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "Permission not found"
|
||||
});
|
||||
}
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: permission
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error fetching permission:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "An error occurred while fetching permission."
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Update permission
|
||||
exports.updatePermission = async (req, res) => {
|
||||
try {
|
||||
const { permissionId } = req.params;
|
||||
const { permissionName, permissionDescription, page, module, action } = req.body;
|
||||
|
||||
const permission = await Permission.findByPk(permissionId);
|
||||
if (!permission) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "Permission not found"
|
||||
});
|
||||
}
|
||||
|
||||
// Update permission fields
|
||||
await permission.update({
|
||||
permissionName: permissionName || permission.permissionName,
|
||||
permissionDescription: permissionDescription || permission.permissionDescription,
|
||||
page: page || permission.page,
|
||||
module: module || permission.module,
|
||||
action: action || permission.action
|
||||
});
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: permission,
|
||||
message: "Permission updated successfully"
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error updating permission:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "An error occurred while updating permission."
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Delete permission
|
||||
exports.deletePermission = async (req, res) => {
|
||||
try {
|
||||
const { permissionId } = req.params;
|
||||
|
||||
const permission = await Permission.findByPk(permissionId);
|
||||
if (!permission) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "Permission not found"
|
||||
});
|
||||
}
|
||||
|
||||
// Delete associated user and role permissions first
|
||||
await UserPermission.destroy({ where: { permission_id: permissionId } });
|
||||
await RolePermission.destroy({ where: { permission_id: permissionId } });
|
||||
|
||||
// Delete the permission
|
||||
await permission.destroy();
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: "Permission deleted successfully"
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error deleting permission:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "An error occurred while deleting permission."
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Create a new role
|
||||
exports.createRole = async (req, res) => {
|
||||
try {
|
||||
const { roleName, roleDescription } = req.body;
|
||||
|
||||
if (!roleName) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Role name is required"
|
||||
});
|
||||
}
|
||||
|
||||
// Generate role ID
|
||||
const roleId = `role_${Date.now()}`;
|
||||
|
||||
const newRole = await Role.create({
|
||||
role_id: roleId,
|
||||
roleName,
|
||||
roleDescription
|
||||
});
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
data: newRole
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error creating role:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "An error occurred while creating role."
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Create role-permission assignment
|
||||
exports.createRolePermission = async (req, res) => {
|
||||
try {
|
||||
const { role_id, permission_id } = req.body;
|
||||
|
||||
if (!role_id || !permission_id) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Role ID and Permission ID are required"
|
||||
});
|
||||
}
|
||||
|
||||
// Verify role and permission exist
|
||||
const role = await Role.findByPk(role_id);
|
||||
if (!role) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "Role not found"
|
||||
});
|
||||
}
|
||||
|
||||
const permission = await Permission.findByPk(permission_id);
|
||||
if (!permission) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "Permission not found"
|
||||
});
|
||||
}
|
||||
|
||||
// Check if assignment already exists
|
||||
const existingAssignment = await RolePermission.findOne({
|
||||
where: { role_id, permission_id }
|
||||
});
|
||||
if (existingAssignment) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Role permission already exists"
|
||||
});
|
||||
}
|
||||
|
||||
const rpId = `rp_${Date.now()}`;
|
||||
const newRolePermission = await RolePermission.create({
|
||||
rp_id: rpId,
|
||||
role_id,
|
||||
permission_id
|
||||
});
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
data: newRolePermission
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error creating role permission:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "An error occurred while creating role permission."
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Bulk create role permissions
|
||||
exports.bulkCreateRolePermissions = async (req, res) => {
|
||||
try {
|
||||
const { role_id, permission_ids } = req.body;
|
||||
|
||||
if (!role_id || !permission_ids || !Array.isArray(permission_ids) || permission_ids.length === 0) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Role ID and permission_ids array are required and must not be empty"
|
||||
});
|
||||
}
|
||||
|
||||
// Verify role exists
|
||||
const role = await Role.findByPk(role_id);
|
||||
if (!role) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "Role not found"
|
||||
});
|
||||
}
|
||||
|
||||
const results = {
|
||||
created: [],
|
||||
failed: []
|
||||
};
|
||||
|
||||
for (const permission_id of permission_ids) {
|
||||
try {
|
||||
if (!permission_id) {
|
||||
results.failed.push({
|
||||
role_id,
|
||||
permission_id,
|
||||
error: "Permission ID is required"
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
// Verify permission exists
|
||||
const permission = await Permission.findByPk(permission_id);
|
||||
if (!permission) {
|
||||
results.failed.push({
|
||||
role_id,
|
||||
permission_id,
|
||||
error: "Permission not found"
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if assignment already exists
|
||||
const existingAssignment = await RolePermission.findOne({
|
||||
where: { role_id, permission_id }
|
||||
});
|
||||
if (existingAssignment) {
|
||||
results.failed.push({
|
||||
role_id,
|
||||
permission_id,
|
||||
error: "Role permission already exists"
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
const rpId = `rp_${Date.now()}`;
|
||||
const newRolePermission = await RolePermission.create({
|
||||
rp_id: rpId,
|
||||
role_id,
|
||||
permission_id
|
||||
});
|
||||
|
||||
results.created.push(newRolePermission);
|
||||
|
||||
} catch (itemError) {
|
||||
results.failed.push({
|
||||
role_id,
|
||||
permission_id,
|
||||
error: itemError.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
res.status(201).json({
|
||||
success: results.failed.length === 0,
|
||||
data: results
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error bulk creating role permissions:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "An error occurred while bulk creating role permissions."
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Get all roles
|
||||
exports.getAllRoles = async (req, res) => {
|
||||
try {
|
||||
const roles = await Role.findAll({
|
||||
include: [
|
||||
{
|
||||
model: RolePermission,
|
||||
as: "rolePermissions",
|
||||
include: [
|
||||
{
|
||||
model: Permission,
|
||||
as: "permission",
|
||||
attributes: ["permission_id", "permissionName", "page", "module", "action"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: roles
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error fetching roles:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "An error occurred while fetching roles."
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Get role by ID
|
||||
exports.getRoleById = async (req, res) => {
|
||||
try {
|
||||
const { roleId } = req.params;
|
||||
|
||||
const role = await Role.findByPk(roleId, {
|
||||
include: [
|
||||
{
|
||||
model: RolePermission,
|
||||
as: "rolePermissions",
|
||||
include: [
|
||||
{
|
||||
model: Permission,
|
||||
as: "permission",
|
||||
attributes: ["permission_id", "permissionName", "page", "module", "action"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
if (!role) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "Role not found"
|
||||
});
|
||||
}
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: role
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error fetching role:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "An error occurred while fetching role."
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Update role
|
||||
exports.updateRole = async (req, res) => {
|
||||
try {
|
||||
const { roleId } = req.params;
|
||||
const { roleName, roleDescription } = req.body;
|
||||
|
||||
const role = await Role.findByPk(roleId);
|
||||
if (!role) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "Role not found"
|
||||
});
|
||||
}
|
||||
|
||||
// Update role fields
|
||||
await role.update({
|
||||
roleName: roleName || role.roleName,
|
||||
roleDescription: roleDescription || role.roleDescription
|
||||
});
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: role,
|
||||
message: "Role updated successfully"
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error updating role:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "An error occurred while updating role."
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Delete role
|
||||
exports.deleteRole = async (req, res) => {
|
||||
try {
|
||||
const { roleId } = req.params;
|
||||
|
||||
const role = await Role.findByPk(roleId);
|
||||
if (!role) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "Role not found"
|
||||
});
|
||||
}
|
||||
|
||||
// Delete associated role permissions first
|
||||
await RolePermission.destroy({ where: { role_id: roleId } });
|
||||
|
||||
// Delete the role
|
||||
await role.destroy();
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: "Role deleted successfully"
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error deleting role:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "An error occurred while deleting role."
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Get user permissions
|
||||
exports.getUserPermissions = async (req, res) => {
|
||||
try {
|
||||
const userId = req.params.userId;
|
||||
|
||||
// Fetch user permissions
|
||||
const userPermissions = await UserPermission.findAll({
|
||||
where: { user_id: userId },
|
||||
include: [
|
||||
{
|
||||
model: Permission,
|
||||
as: "permission",
|
||||
attributes: ["permission_id", "permissionName", "page", "module", "action"]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: userPermissions
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error fetching user permissions:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "An error occurred while fetching user permissions."
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Create user permission
|
||||
exports.createUserPermission = async (req, res) => {
|
||||
try {
|
||||
const { user_id, permission_id } = req.body;
|
||||
|
||||
if (!user_id || !permission_id) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "User ID and Permission ID are required"
|
||||
});
|
||||
}
|
||||
|
||||
// Verify user and permission exist
|
||||
const user = await User.findByPk(user_id);
|
||||
if (!user) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "User not found"
|
||||
});
|
||||
}
|
||||
|
||||
const permission = await Permission.findByPk(permission_id);
|
||||
if (!permission) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "Permission not found"
|
||||
});
|
||||
}
|
||||
|
||||
// Check if assignment already exists
|
||||
const existingAssignment = await UserPermission.findOne({
|
||||
where: { user_id, permission_id }
|
||||
});
|
||||
if (existingAssignment) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "User permission already exists"
|
||||
});
|
||||
}
|
||||
|
||||
const newUserPermission = await UserPermission.create({
|
||||
user_id,
|
||||
permission_id
|
||||
});
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
data: newUserPermission
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error creating user permission:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "An error occurred while creating user permission."
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Bulk create user permissions
|
||||
exports.bulkCreateUserPermissions = async (req, res) => {
|
||||
try {
|
||||
const { user_id, permission_ids } = req.body;
|
||||
|
||||
if (!user_id || !permission_ids || !Array.isArray(permission_ids) || permission_ids.length === 0) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "User ID and permission_ids array are required and must not be empty"
|
||||
});
|
||||
}
|
||||
|
||||
// Verify user exists
|
||||
const user = await User.findByPk(user_id);
|
||||
if (!user) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "User not found"
|
||||
});
|
||||
}
|
||||
|
||||
const results = {
|
||||
created: [],
|
||||
failed: []
|
||||
};
|
||||
|
||||
for (const permission_id of permission_ids) {
|
||||
try {
|
||||
if (!permission_id) {
|
||||
results.failed.push({
|
||||
user_id,
|
||||
permission_id,
|
||||
error: "Permission ID is required"
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
// Verify permission exists
|
||||
const permission = await Permission.findByPk(permission_id);
|
||||
if (!permission) {
|
||||
results.failed.push({
|
||||
user_id,
|
||||
permission_id,
|
||||
error: "Permission not found"
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if assignment already exists
|
||||
const existingAssignment = await UserPermission.findOne({
|
||||
where: { user_id, permission_id }
|
||||
});
|
||||
if (existingAssignment) {
|
||||
results.failed.push({
|
||||
user_id,
|
||||
permission_id,
|
||||
error: "User permission already exists"
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
const newUserPermission = await UserPermission.create({
|
||||
user_id,
|
||||
permission_id
|
||||
});
|
||||
|
||||
results.created.push(newUserPermission);
|
||||
|
||||
} catch (itemError) {
|
||||
results.failed.push({
|
||||
user_id,
|
||||
permission_id,
|
||||
error: itemError.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
res.status(201).json({
|
||||
success: results.failed.length === 0,
|
||||
data: results
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error bulk creating user permissions:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "An error occurred while bulk creating user permissions."
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Update user permission
|
||||
exports.updateUserPermission = async (req, res) => {
|
||||
try {
|
||||
const { upId } = req.params;
|
||||
const { permission_id } = req.body;
|
||||
|
||||
if (!permission_id) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Permission ID is required"
|
||||
});
|
||||
}
|
||||
|
||||
const userPermission = await UserPermission.findByPk(upId);
|
||||
if (!userPermission) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "User permission not found"
|
||||
});
|
||||
}
|
||||
|
||||
// Verify permission exists
|
||||
const permission = await Permission.findByPk(permission_id);
|
||||
if (!permission) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "Permission not found"
|
||||
});
|
||||
}
|
||||
|
||||
// Check if new permission assignment already exists for this user
|
||||
const existingAssignment = await UserPermission.findOne({
|
||||
where: {
|
||||
user_id: userPermission.user_id,
|
||||
permission_id
|
||||
}
|
||||
});
|
||||
if (existingAssignment && existingAssignment.up_id !== upId) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "This permission is already assigned to this user"
|
||||
});
|
||||
}
|
||||
|
||||
await userPermission.update({ permission_id });
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
data: userPermission,
|
||||
message: "User permission updated successfully"
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error updating user permission:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "An error occurred while updating user permission."
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Delete user permission
|
||||
exports.deleteUserPermission = async (req, res) => {
|
||||
try {
|
||||
const { upId } = req.params;
|
||||
|
||||
const userPermission = await UserPermission.findByPk(upId);
|
||||
if (!userPermission) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "User permission not found"
|
||||
});
|
||||
}
|
||||
|
||||
await userPermission.destroy();
|
||||
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
message: "User permission deleted successfully"
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error deleting user permission:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "An error occurred while deleting user permission."
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,219 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Niolla
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is proprietary and confidential.
|
||||
* Unauthorized copying, modification, distribution, or use
|
||||
* of this file, via any medium, is strictly prohibited.
|
||||
*/
|
||||
|
||||
// app/controllers/profile.controller.js
|
||||
|
||||
const db = require("../models");
|
||||
const Profile = db.Profile;
|
||||
const Upload = db.Upload;
|
||||
const User = db.User;
|
||||
|
||||
const { generateTodoId } = require("../utils/idGen.util");
|
||||
const { getSignedFileUrl } = require("../utils/s3Upload.utill");
|
||||
const {
|
||||
generateResetToken,
|
||||
resetPassword,
|
||||
} = require("../utils/passwordReset.utill");
|
||||
const { sendMail } = require("../utils/mail.util");
|
||||
const { logActivity } = require("../services/activity.service");
|
||||
const { hashPassword, checkPassword } = require("../utils/hashPassword.util");
|
||||
const { log } = require("../utils/consoleLog.utill");
|
||||
|
||||
// Get Profile avatar by user ID
|
||||
exports.getProfileAvatar = async (req, res) => {
|
||||
try {
|
||||
const userId = req.params.userId;
|
||||
const profile = await Profile.findOne({ where: { user_id: userId } });
|
||||
if (!profile) {
|
||||
return res.status(404).json({ error: "Profile not found" });
|
||||
}
|
||||
|
||||
const uploadRecord = await Upload.findByPk(profile.profilePicture_id);
|
||||
|
||||
const fileUrl = await getSignedFileUrl(uploadRecord.file_path);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
userId: profile.userId,
|
||||
profilePictureUrl: fileUrl,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error fetching profile:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "Internal server error",
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Get profile background image by user ID
|
||||
exports.getProfileBackgroundImage = async (req, res) => {
|
||||
try {
|
||||
const userId = req.params.userId;
|
||||
const profile = await Profile.findOne({ where: { user_id: userId } });
|
||||
if (!profile) {
|
||||
return res
|
||||
.status(404)
|
||||
.json({ success: false, message: "Profile not found" });
|
||||
}
|
||||
|
||||
const uploadRecord = await Upload.findByPk(profile.backgroundImage_id);
|
||||
|
||||
const fileUrl = await getSignedFileUrl(uploadRecord.file_path);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
userId: profile.userId,
|
||||
backgroundImageUrl: fileUrl,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error fetching profile background image:", error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: "Internal server error",
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Request password reset token
|
||||
exports.requestPasswordReset = async (req, res) => {
|
||||
try {
|
||||
const { email } = req.body;
|
||||
|
||||
const user = await User.findOne({
|
||||
where: { email },
|
||||
});
|
||||
|
||||
if (user) {
|
||||
const token = await generateResetToken(user.id);
|
||||
const resetLink = `${process.env.FRONTEND_URL}/reset-password?token=${token}&email=${encodeURIComponent(email)}`;
|
||||
|
||||
await sendMail({
|
||||
to: user.email,
|
||||
subject: "Password Reset",
|
||||
templateName: "passwordReset",
|
||||
templateVars: {
|
||||
firstName: user.firstName,
|
||||
resetLink,
|
||||
expiryTime: process.env.PASSWORD_RESET_EXPIRY_TIME || "10 minutes",
|
||||
},
|
||||
});
|
||||
|
||||
const activityUser = {
|
||||
id: 1,
|
||||
firstName: "System",
|
||||
};
|
||||
|
||||
await logActivity({
|
||||
user: activityUser,
|
||||
description: "Requested password reset for email: " + email,
|
||||
type: "PASSWORD_RESET_REQUEST",
|
||||
module: "Authentication",
|
||||
});
|
||||
}
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
message:
|
||||
"If an account exists with this email address, a password reset email has been sent.",
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "Internal server error",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Reset password using token
|
||||
exports.resetPassword = async (req, res) => {
|
||||
try {
|
||||
const { email, token, newPassword } = req.body;
|
||||
|
||||
// Validate email
|
||||
const user = await User.findOne({
|
||||
where: { email },
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Invalid email address.",
|
||||
});
|
||||
}
|
||||
|
||||
await resetPassword(user.id, token, newPassword);
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
message: "Password reset successfully.",
|
||||
});
|
||||
} catch (error) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: error.message || "Invalid or expired token.",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Change Password
|
||||
exports.changePassword = async (req, res) => {
|
||||
try {
|
||||
const { currentPassword, newPassword } = req.body;
|
||||
|
||||
const user = req.user;
|
||||
|
||||
log(`User:`, user);
|
||||
|
||||
const foundUser = await User.findOne({ where: { id: user.id } });
|
||||
|
||||
if (!foundUser) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "User not found.",
|
||||
});
|
||||
}
|
||||
|
||||
const isMatch = await checkPassword(currentPassword, foundUser.password);
|
||||
|
||||
if (!isMatch) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Current password is incorrect.",
|
||||
});
|
||||
}
|
||||
|
||||
foundUser.password = await hashPassword(newPassword);
|
||||
await foundUser.save();
|
||||
await logActivity({
|
||||
user: req.user,
|
||||
description: "Changed password",
|
||||
type: "PASSWORD_CHANGE",
|
||||
module: "Authentication",
|
||||
});
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
message: "Password changed successfully.",
|
||||
});
|
||||
} catch (error) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: error.message || "Failed to change password.",
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,131 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Niolla
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is proprietary and confidential.
|
||||
* Unauthorized copying, modification, distribution, or use
|
||||
* of this file, via any medium, is strictly prohibited.
|
||||
*/
|
||||
|
||||
// app/controllers/activity.controller.js
|
||||
|
||||
const { uploadToS3, getSignedFileUrl } = require("../utils/s3Upload.utill");
|
||||
const { log } = require("../utils/consoleLog.utill");
|
||||
|
||||
const db = require("../models");
|
||||
const Upload = db.Upload;
|
||||
const Assets = db.Assets;
|
||||
|
||||
// Constants
|
||||
const MAX_IMAGE_SIZE = 3 * 1024 * 1024; // 3MB
|
||||
const MAX_PDF_SIZE = 5 * 1024 * 1024; // 5MB
|
||||
|
||||
const isValidFileType = (mimetype) => {
|
||||
return mimetype.startsWith("image/") || mimetype === "application/pdf";
|
||||
};
|
||||
|
||||
const isValidFileSize = (mimetype, size) => {
|
||||
if (mimetype.startsWith("image/")) return size <= MAX_IMAGE_SIZE;
|
||||
if (mimetype === "application/pdf") return size <= MAX_PDF_SIZE;
|
||||
return false;
|
||||
};
|
||||
|
||||
exports.uploadFile = async (req, res) => {
|
||||
try {
|
||||
// 1. Check file exists
|
||||
if (!req.file) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "No file uploaded",
|
||||
});
|
||||
}
|
||||
|
||||
const { mimetype, size, originalname } = req.file;
|
||||
|
||||
// 2. Validate file type
|
||||
if (!isValidFileType(mimetype)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: "Only images and PDFs are allowed",
|
||||
});
|
||||
}
|
||||
|
||||
// 3. Validate file size
|
||||
if (!isValidFileSize(mimetype, size)) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: mimetype.startsWith("image/")
|
||||
? "Image too large (max 1MB)"
|
||||
: "PDF too large (max 5MB)",
|
||||
});
|
||||
}
|
||||
|
||||
log("File validation passed:", {
|
||||
mimetype,
|
||||
size,
|
||||
originalname,
|
||||
use_for: req.body.use_for,
|
||||
});
|
||||
|
||||
// 4. Upload to S3
|
||||
const fileKey = await uploadToS3(req.file, req.body.use_for);
|
||||
|
||||
const fileUrl = await getSignedFileUrl(fileKey);
|
||||
|
||||
// 5. Save to DB
|
||||
const uploadData = {
|
||||
file_path: fileKey,
|
||||
file_type: mimetype,
|
||||
file_size: size,
|
||||
original_name: originalname,
|
||||
use_for: req.body.use_for || null,
|
||||
uploaded_by: req.user?.id || null,
|
||||
};
|
||||
|
||||
const newUpload = await Upload.create(uploadData);
|
||||
|
||||
newUpload.dataValues.file_url = fileUrl; // Add URL to response
|
||||
|
||||
// 6. Response
|
||||
return res.status(201).json({
|
||||
success: true,
|
||||
message: "File uploaded successfully",
|
||||
data: newUpload,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Upload Controller Error:", error);
|
||||
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
message: "Failed to upload file",
|
||||
error: process.env.NODE_ENV === "development" ? error.message : undefined,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Get Uploaded File URL
|
||||
exports.getFileUrl = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
|
||||
const uploadRecord = await Upload.findByPk(id);
|
||||
|
||||
if (!uploadRecord) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: "File not found",
|
||||
});
|
||||
}
|
||||
|
||||
const fileUrl = await getSignedFileUrl(uploadRecord.file_path);
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
message: "File URL retrieved successfully",
|
||||
data: {
|
||||
id: uploadRecord.id,
|
||||
file_url: fileUrl,
|
||||
},
|
||||
});
|
||||
} catch (error) {}
|
||||
};
|
||||
@@ -0,0 +1,323 @@
|
||||
/**
|
||||
* Copyright (c) 2026 Niolla
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is proprietary and confidential.
|
||||
* Unauthorized copying, modification, distribution, or use
|
||||
* of this file, via any medium, is strictly prohibited.
|
||||
*/
|
||||
|
||||
// app/controllers/user.controller.js
|
||||
|
||||
const db = require("../models");
|
||||
const { hashPassword } = require("../utils/hashPassword.util");
|
||||
const { generateUserId, generateId } = require("../utils/idGen.util");
|
||||
const { logActivity } = require("../services/activity.service");
|
||||
const { sendMail } = require("../utils/mail.util");
|
||||
const User = db.User;
|
||||
const Profile = db.Profile;
|
||||
|
||||
// Create a new user
|
||||
exports.createNewUser = async (req, res) => {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
|
||||
try {
|
||||
const {
|
||||
firstName,
|
||||
lastName,
|
||||
email,
|
||||
role,
|
||||
roleID,
|
||||
accountType,
|
||||
department,
|
||||
} = req.body;
|
||||
|
||||
const hashedPassword = await hashPassword(process.env.DEFAULT_PASSWORD);
|
||||
const userID = generateUserId();
|
||||
|
||||
const userExists = await User.findOne({ where: { email } });
|
||||
if (userExists) {
|
||||
await transaction.rollback();
|
||||
|
||||
return res.status(400).send({
|
||||
success: false,
|
||||
message: "User with this email already exists",
|
||||
});
|
||||
}
|
||||
|
||||
const newUser = await User.create(
|
||||
{
|
||||
id: userID,
|
||||
firstName,
|
||||
lastName,
|
||||
email,
|
||||
password: hashedPassword,
|
||||
accountType,
|
||||
role,
|
||||
roleID: roleID || "N/A",
|
||||
department: department || null,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
const newProfile = await Profile.create(
|
||||
{
|
||||
profile_id: generateId(),
|
||||
user_id: newUser.id,
|
||||
theme: "light",
|
||||
notificationsEnabled: true,
|
||||
profilePicture_id: "N/A",
|
||||
backgroundImage_id: "N/A",
|
||||
dob: null,
|
||||
phone_number: null,
|
||||
},
|
||||
{ transaction },
|
||||
);
|
||||
|
||||
await sendMail({
|
||||
to: email,
|
||||
subject: "Welcome - Ocenic Titan",
|
||||
templateName: "welcome",
|
||||
templateVars: {
|
||||
firstName: firstName,
|
||||
email: email,
|
||||
password: process.env.DEFAULT_PASSWORD,
|
||||
},
|
||||
text: `Hello ${firstName}, your account has been created successfully.`,
|
||||
});
|
||||
|
||||
await transaction.commit();
|
||||
|
||||
await logActivity({
|
||||
user: req.user,
|
||||
description: `Created New User with ID: ${newUser.id}`,
|
||||
type: "CREATE_USER",
|
||||
module: "User Management",
|
||||
});
|
||||
|
||||
res.status(201).send({
|
||||
success: true,
|
||||
message: "User Created Successfully",
|
||||
data: {
|
||||
id: newUser.id,
|
||||
firstName: newUser.firstName,
|
||||
lastName: newUser.lastName,
|
||||
email: newUser.email,
|
||||
accountType: newUser.accountType,
|
||||
role: newUser.role,
|
||||
department: newUser.department,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
|
||||
res.status(500).send({
|
||||
success: false,
|
||||
message: "Failed to create user",
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Get users with pagination (20 per page)
|
||||
exports.getAllUsers = async (req, res) => {
|
||||
try {
|
||||
const page = parseInt(req.query.page) || 1; // default page = 1
|
||||
const limit = 20;
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
const { count, rows: users } = await User.findAndCountAll({
|
||||
attributes: { exclude: ["password"] },
|
||||
limit,
|
||||
offset,
|
||||
order: [["createdAt", "DESC"]], // optional sorting
|
||||
});
|
||||
|
||||
res.status(200).send({
|
||||
success: true,
|
||||
data: users,
|
||||
pagination: {
|
||||
totalUsers: count,
|
||||
totalPages: Math.ceil(count / limit),
|
||||
currentPage: page,
|
||||
pageSize: limit,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).send({
|
||||
success: false,
|
||||
message: "Failed to retrieve users",
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Get user details by ID
|
||||
exports.getUserById = async (req, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const user = await User.findOne({
|
||||
where: { id },
|
||||
attributes: { exclude: ["password"] },
|
||||
include: [{ model: Profile, as: "profile" }],
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).send({
|
||||
success: false,
|
||||
message: "User not found",
|
||||
});
|
||||
}
|
||||
|
||||
res.status(200).send({
|
||||
success: true,
|
||||
data: user,
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).send({
|
||||
success: false,
|
||||
message: "Failed to retrieve user",
|
||||
error: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Update user details
|
||||
exports.updateUser = async (req, res) => {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const {
|
||||
firstName,
|
||||
lastName,
|
||||
email,
|
||||
role,
|
||||
roleID,
|
||||
accountType,
|
||||
department,
|
||||
theme,
|
||||
notificationsEnabled,
|
||||
profilePicture_id,
|
||||
backgroundImage_id,
|
||||
dob,
|
||||
phone_number,
|
||||
} = req.body;
|
||||
const user = await User.findOne({
|
||||
where: { id },
|
||||
});
|
||||
const UserProfile = await Profile.findOne({
|
||||
where: { user_id: id },
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).send({
|
||||
success: false,
|
||||
message: "User not found",
|
||||
});
|
||||
}
|
||||
|
||||
if (!UserProfile) {
|
||||
return res.status(404).send({
|
||||
success: false,
|
||||
message: "User profile not found",
|
||||
});
|
||||
}
|
||||
|
||||
user.firstName = firstName || user.firstName;
|
||||
user.lastName = lastName || user.lastName;
|
||||
user.role = role || user.role;
|
||||
user.roleID = roleID || user.roleID || "N/A";
|
||||
user.accountType = accountType || user.accountType;
|
||||
user.department = department || user.department;
|
||||
|
||||
UserProfile.theme = theme || UserProfile.theme;
|
||||
UserProfile.notificationsEnabled =
|
||||
notificationsEnabled !== undefined
|
||||
? notificationsEnabled
|
||||
: UserProfile.notificationsEnabled;
|
||||
UserProfile.profilePicture_id =
|
||||
profilePicture_id || UserProfile.profilePicture_id || "N/A";
|
||||
UserProfile.backgroundImage_id =
|
||||
backgroundImage_id || UserProfile.backgroundImage_id || "N/A";
|
||||
UserProfile.dob = dob || UserProfile.dob;
|
||||
UserProfile.phone_number = phone_number || UserProfile.phone_number;
|
||||
|
||||
await UserProfile.save({ transaction });
|
||||
await user.save({ transaction });
|
||||
await transaction.commit();
|
||||
|
||||
await logActivity({
|
||||
user: req.user,
|
||||
description: `Updated User with ID: ${user.id}`,
|
||||
type: "UPDATE_USER",
|
||||
module: "User Management",
|
||||
});
|
||||
|
||||
const data = {
|
||||
id: user.id,
|
||||
firstName: user.firstName,
|
||||
lastName: user.lastName,
|
||||
email: user.email,
|
||||
accountType: user.accountType,
|
||||
role: user.role,
|
||||
department: user.department,
|
||||
profile: {
|
||||
theme: UserProfile.theme,
|
||||
notificationsEnabled: UserProfile.notificationsEnabled,
|
||||
profilePicture_id: UserProfile.profilePicture_id,
|
||||
backgroundImage_id: UserProfile.backgroundImage_id,
|
||||
dob: UserProfile.dob,
|
||||
phone_number: UserProfile.phone_number,
|
||||
},
|
||||
};
|
||||
|
||||
res.status(200).send({
|
||||
success: true,
|
||||
message: "User updated successfully",
|
||||
data,
|
||||
});
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
res.status(500).send({
|
||||
success: false,
|
||||
message: `Error: ${error.message}`,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Delete user
|
||||
exports.deleteUser = async (req, res) => {
|
||||
const transaction = await db.sequelize.transaction();
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const user = await User.findOne({
|
||||
where: { id },
|
||||
});
|
||||
if (!user) {
|
||||
return res.status(404).send({
|
||||
success: false,
|
||||
message: "User not found",
|
||||
});
|
||||
}
|
||||
await user.destroy({ transaction });
|
||||
await transaction.commit();
|
||||
|
||||
await logActivity({
|
||||
user: req.user,
|
||||
description: `Deleted User with ID: ${user.id}`,
|
||||
type: "DELETE_USER",
|
||||
module: "User Management",
|
||||
});
|
||||
|
||||
res.status(200).send({
|
||||
success: true,
|
||||
message: "User deleted successfully",
|
||||
});
|
||||
} catch (error) {
|
||||
await transaction.rollback();
|
||||
res.status(500).send({
|
||||
success: false,
|
||||
message: `Error: ${error.message}`,
|
||||
});
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user