first commit

This commit is contained in:
Isuru Bimsara
2026-08-14 22:46:02 +05:30
commit 81d0e65686
132 changed files with 24398 additions and 0 deletions
+56
View File
@@ -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.",
});
}
};
+132
View File
@@ -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,
});
}
};
+138
View File
@@ -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
});
}
};
+868
View File
@@ -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."
});
}
};
+219
View File
@@ -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.",
});
}
};
+131
View File
@@ -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) {}
};
+323
View File
@@ -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}`,
});
}
};