Duffer Derek
import { createHash } from "crypto";
import Log from "../configs/logger.js";
import refreshToken from "../database/models/refreshToken.js";
import usersOtp from "../database/models/usersOtp.js";
import { getAdminDashBoard } from "../services/api/dashBoard.js";
import { getSubTasks } from "../services/api/subTask.js";
import {
addPodioComment,
deleteComments,
getPartnerFiles,
getSubtaskComments,
insertOrUpdateComments,
udpatePodioFiles,
} from "../services/api/subtaskComments.js";
import {
checkAndDeleteSubTask,
deleteSharedTaskandSubTask,
getAndDeleteSubTask,
getTasks,
item,
updateOrCreateSubTask,
updateTask,
} from "../services/api/tasks.js";
import { expireOtp } from "../services/api/users.js";
import { generateToken } from "../services/tokenHandler.js";
import { fetchUserByEmailOrID, getUserByPhone } from "./controller.js";
import { sendSms } from "../services/sms.js";
import { getOfficePartnerDashboard } from "../services/api/officePartner.js";
import { createOrUpdateProductLine, deleteProductLine } from "../services/api/productLine.js";
import { updateInquiry } from "../services/api/inquiry.js";
import { updateOfficePartnerTask } from "../services/api/task.js";
export default {
getOtp: async (req, res, next) => {
try {
const { phone, user } = req.body;
let otp = Math.floor(100000 + Math.random() * 900000);
if (phone === "91629615" || phone === "91629616" || phone == "91629617") {
otp = 123456;
} else {
await sendSms(phone, otp);
}
let data = { phone: phone, otp: otp, status: 0 };
const [instance, isCreated] = await usersOtp.findOrCreate({
where: { phone: phone },
defaults: data,
});
if (!isCreated) {
await usersOtp.update(data, {
where: { phone: phone },
});
}
expireOtp(phone);
res.status(200).json({
status: 200,
message: "OTP has been send to your phone",
});
} catch (err) {
Log.error(err);
next(err);
}
},
verifyOtp: async (req, res, next) => {
try {
const { phone, otp } = req.body;
const otpResult = await usersOtp.findOne({
where: { phone: phone, otp: otp, status: 0 },
});
if (!otpResult) {
throw { errors: { otp: "Invalid OTP" }, status: 400 };
}
usersOtp.update({ status: 1 }, { where: { phone: phone, otp: otp } });
const user = await getUserByPhone(phone);
const access_token = generateToken({ id: user.id });
const refresh_token = generateToken({ id: user.id }, false);
const md5Refresh = createHash("md5").update(refresh_token).digest("hex");
// Storing refresh token in MD5 format
const result = await refreshToken.create({
user_id: user.id,
token: md5Refresh,
});
if (!result.id) {
throw new Error("Failed to whitelist the refresh token.");
}
res.status(200).json({
status: 200,
message: "OTP has been verified.",
user: user,
access_token: access_token,
refresh_token: refresh_token,
});
} catch (error) {
Log.error(error);
res.status(400).json(error);
}
},
getDashboard: async (req, res, next) => {
try {
const { filters } = req.body;
const user = await fetchUserByEmailOrID(req.user_id, false);
let response = {};
if (user.type == 1) {
response = await getAdminDashBoard(filters, user.type);
} else if (user.type == 3) {
const officePartnerResponse = await getOfficePartnerDashboard(user.item_id, filters || {});
response.tasks = {
assigned: officePartnerResponse.assigned,
};
response.filters = officePartnerResponse.filters;
} else {
filters.user_item_id = user.item_id;
response.tasks = await getSubTasks(filters);
}
res.status(200).json({
status: 200,
data: response,
});
} catch (error) {
Log.error(error);
next(error);
}
},
getTask: async (req, res, next) => {
try {
const { task_id } = req.body;
let response = await item(task_id);
res.status(200).json({
status: 200,
data: response,
});
} catch (error) {
Log.error(error);
next(error);
}
},
addOrUpdateTasks: async (req, res, next) => {
try {
const { sub_tasks, task_data, filter } = req.body;
// Update the main task
await updateTask(task_data);
if (task_data.unassigned == true) {
await getAndDeleteSubTask(task_data);
} else {
// Check if subtasks are present
if (sub_tasks && sub_tasks.tasks && sub_tasks.tasks.data) {
// Iterate over each subtask
const shared_partner = {
id: null,
name: null,
type: null,
email: null,
phone: null,
};
for (const item of sub_tasks.tasks.data) {
if (
!(item.delete == true && item.shared == 1) &&
!(new Date(task_data.start_date) > new Date(item.date)) &&
!(new Date(task_data.end_date) < new Date(item.date))
) {
if (task_data.user_id === item.user_id && item.shared == 1) {
let data = {
item_id: task_data.item_id,
user_item_id: item.user_id,
date: item.date,
};
await deleteSharedTaskandSubTask(data);
} else {
let subTaskdata = {
date: item.date,
notes: item.notes,
isHoliday: item.isHoliday,
time: item.time,
user_item_id: item.user_id,
item_id: sub_tasks.tasks.item_id,
shared_sub_task_time:
item.delete !== true ? item.shared_time : null,
shared: item.shared,
};
if (item && item.user !== undefined && item.user !== null) {
if (typeof item.user === "object" && item.user !== null) {
subTaskdata.shared_partner =
item.delete !== true ? item.user : shared_partner;
} else {
try {
const userJson = JSON.parse(
item.user.replace(/^"|"$/g, "")
);
subTaskdata.shared_partner =
item.delete !== true ? userJson : shared_partner;
} catch (error) {
console.error("Error parsing user JSON:", error);
}
}
}
await updateOrCreateSubTask(subTaskdata);
}
} else {
let subTaskdata = {
date: item.date,
item_id: sub_tasks.tasks.item_id,
shared: item.shared,
};
await checkAndDeleteSubTask(subTaskdata);
}
}
}
}
let response = {};
response.tasks = await getTasks(filter, 1);
res.status(200).json({
status: 200,
message: "Subtask has been updated!",
data: response,
});
} catch (error) {
// Log and handle errors
console.error(error);
res.status(500).json({
status: 500,
message: error,
});
}
},
updatePartnerTask: async (req, res, next) => {
try {
const {
sub_task_id,
partner_comments,
item_id,
deletion,
user_name,
date,
} = req.body;
if (partner_comments != "") {
await addPodioComment(item_id, partner_comments);
}
const response = await insertOrUpdateComments(
req,
deletion,
user_name,
date
);
setTimeout(async () => {
let newPartnerFiles = await getPartnerFiles(sub_task_id);
res.status(200).json({
data: newPartnerFiles,
status: 200,
message: "Files uploaded successfully.",
});
}, 3000);
} catch (error) {
console.error("Error uploading files:", error);
res.status(500).json({ message: "Internal server error." });
}
},
deleteSubTaskComments: async (req, res, next) => {
try {
let { id, sub_task_id, item_id } = req.body;
await deleteComments(id, sub_task_id);
await udpatePodioFiles(sub_task_id, item_id);
let newComments = await getSubtaskComments(sub_task_id);
let data = [
{
previewUrl: "",
inputValue: "",
fileError: false,
noteError: false,
addButton: true,
id: 0,
},
];
if (newComments.length > 0) {
data = newComments;
}
res.status(200).json({
data: data,
status: 200,
message: "Comment has been deleted",
});
} catch (error) {
console.error("Error uploading files:", error);
res.status(500).json({ message: "Internal server error." });
}
},
createOrUpdateProductLine: async (req, res, next) => {
try {
const productLineData = req.body;
// Validate required fields
if (!productLineData.product || !productLineData.product_title) {
return res.status(400).json({
status: 400,
message: "Product and product_title are required fields",
});
}
// Validate that either task_item_id or inquiry_item_id is provided (but not both)
if (!productLineData.task_item_id && !productLineData.inquiry_item_id) {
return res.status(400).json({
status: 400,
message: "Either task_item_id or inquiry_item_id must be provided",
});
}
if (productLineData.task_item_id && productLineData.inquiry_item_id) {
return res.status(400).json({
status: 400,
message: "Cannot provide both task_item_id and inquiry_item_id",
});
}
// If is_updated is true, item_id is required
if (productLineData.is_updated && !productLineData.item_id) {
return res.status(400).json({
status: 400,
message: "item_id is required when is_updated is true",
});
}
const result = await createOrUpdateProductLine(productLineData);
res.status(200).json({
status: 200,
message: productLineData.is_updated
? "Product line updated successfully"
: "Product line created successfully",
data: result,
});
} catch (error) {
Log.error("Error in createOrUpdateProductLine:", error);
res.status(500).json({
status: 500,
message: error.message || "Internal server error",
});
}
},
deleteProductLine: async (req, res, next) => {
try {
const { item_id } = req.body;
// Validate required fields
if (!item_id) {
return res.status(400).json({
status: 400,
message: "item_id is required",
});
}
const result = await deleteProductLine(item_id);
res.status(200).json({
status: 200,
message: "Product line deleted successfully",
data: result,
});
} catch (error) {
Log.error("Error in deleteProductLine:", error);
res.status(500).json({
status: 500,
message: error.message || "Internal server error",
});
}
},
updateInquiry: async (req, res, next) => {
try {
let inquiryData = req.body;
// If FormData is sent, parse JSON string fields
// FormData sends all fields as strings, so JSON objects need to be parsed
// Check for files in multiple formats (array or object)
const hasFilesInReq = req.files && (
(Array.isArray(req.files) && req.files.length > 0) ||
(typeof req.files === 'object' && Object.keys(req.files).length > 0)
);
if (hasFilesInReq) {
// Parse JSON string fields from FormData
const fieldsToParse = [
'housing_type', 'followup_status', 'status', 'painterkanon_employee',
'region', 'rooms_to_paint', 'partner', 'inspection_date'
];
fieldsToParse.forEach(field => {
if (inquiryData[field] && typeof inquiryData[field] === 'string') {
try {
inquiryData[field] = JSON.parse(inquiryData[field]);
} catch (e) {
// If parsing fails, keep original value
Log.warn(`Failed to parse ${field} from FormData:`, e);
}
}
});
// Parse item_id if it's a string
if (inquiryData.item_id && typeof inquiryData.item_id === 'string') {
inquiryData.item_id = parseInt(inquiryData.item_id, 10);
}
}
Log.info("=== updateInquiry Controller ===");
Log.info("req exists:", !!req);
Log.info("req.files:", req.files);
Log.info("req.files type:", Array.isArray(req.files) ? 'array' : typeof req.files);
Log.info("req.files length:", (req.files && req.files.length) || 0);
if (req.files && req.files.length > 0) {
Log.info("Files received:", req.files.map(f => ({
fieldname: f.fieldname,
originalname: f.originalname,
encoding: f.encoding,
mimetype: f.mimetype,
size: f.size,
buffer: f.buffer ? `Buffer(${f.buffer.length} bytes)` : 'no buffer'
})));
}
Log.info("inquiryData.item_id:", inquiryData.item_id);
Log.info("inquiryData keys:", Object.keys(inquiryData));
Log.info("inquiryData.deletion:", inquiryData.deletion);
// Validate required fields
if (!inquiryData.item_id) {
return res.status(400).json({
status: 400,
message: "item_id is required",
});
}
// Pass req to service for file handling
const result = await updateInquiry(inquiryData, req);
res.status(200).json({
status: 200,
message: "Inquiry updated successfully",
data: result,
});
} catch (error) {
Log.error("Error in updateInquiry:", error);
res.status(500).json({
status: 500,
message: error.message || "Internal server error",
});
}
},
updateOfficePartnerTask: async (req, res, next) => {
try {
let taskData = req.body;
// If FormData is sent, parse JSON string fields
// FormData sends all fields as strings, so JSON objects need to be parsed
// Check for files in multiple formats (array or object)
const hasFilesInReq = req.files && (
(Array.isArray(req.files) && req.files.length > 0) ||
(typeof req.files === 'object' && Object.keys(req.files).length > 0)
);
// Check if there's a deletion - this means FormData is being used
const hasDeletion = taskData.deletion && (
(typeof taskData.deletion === 'string' && taskData.deletion.trim() !== '' && taskData.deletion !== 'null' && taskData.deletion !== 'undefined') ||
(typeof taskData.deletion === 'object' && Object.keys(taskData.deletion).length > 0)
);
// If FormData is used (has files or deletion), parse JSON fields
if (hasFilesInReq || hasDeletion) {
// Parse JSON string fields from FormData
const fieldsToParse = [
'housing_type', 'status', 'customer_has_paid', 'subcontractor_has_paid',
'type', 'product_type', 'task_performed_by', 'partner', 'generate_pdf',
'confirmation_email', 'send_sms', 'send_complaint_images_to_painter', 'material_order',
'start_date', 'date'
];
fieldsToParse.forEach(field => {
if (taskData[field] && typeof taskData[field] === 'string') {
try {
const parsed = JSON.parse(taskData[field]);
// If parsed result is empty (null, empty object, empty array, or empty string), remove it
// This means the field wasn't provided in FormData and shouldn't be processed
if (parsed === null ||
parsed === '' ||
(typeof parsed === 'object' && !Array.isArray(parsed) && Object.keys(parsed).length === 0) ||
(Array.isArray(parsed) && parsed.length === 0)) {
delete taskData[field];
} else {
taskData[field] = parsed;
}
} catch (e) {
// If parsing fails, check if it's an empty string and remove it
if (taskData[field].trim() === '' || taskData[field].trim() === 'null' || taskData[field].trim() === 'undefined') {
delete taskData[field];
} else {
Log.warn(`Failed to parse ${field} from FormData:`, e);
}
}
} else if (taskData[field] === '' || taskData[field] === 'null' || taskData[field] === 'undefined') {
// Remove empty strings
delete taskData[field];
}
});
// Parse item_id if it's a string
if (taskData.item_id && typeof taskData.item_id === 'string') {
taskData.item_id = parseInt(taskData.item_id, 10);
}
}
Log.info("=== updateOfficePartnerTask Controller ===");
Log.info("req exists:", !!req);
Log.info("req.files:", req.files);
Log.info("req.files type:", Array.isArray(req.files) ? 'array' : typeof req.files);
Log.info("req.files length:", (req.files && req.files.length) || 0);
if (req.files && req.files.length > 0) {
Log.info("Files received:", req.files.map(f => ({
fieldname: f.fieldname,
originalname: f.originalname,
encoding: f.encoding,
mimetype: f.mimetype,
size: f.size,
buffer: f.buffer ? `Buffer(${f.buffer.length} bytes)` : 'no buffer'
})));
}
Log.info("taskData.item_id:", taskData.item_id);
Log.info("taskData keys:", Object.keys(taskData));
Log.info("taskData.deletion:", taskData.deletion);
// Validate required fields
if (!taskData.item_id) {
return res.status(400).json({
status: 400,
message: "item_id is required",
});
}
// Pass req to service for file handling
const result = await updateOfficePartnerTask(taskData, req);
res.status(200).json({
status: 200,
message: "Task updated successfully",
data: result,
});
} catch (error) {
Log.error("Error in updateOfficePartnerTask:", error);
res.status(500).json({
status: 500,
message: error.message || "Internal server error",
});
}
},
};
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists