Duffer Derek

Current Path : /var/www/api-mk-planner.bitkit.dk/httpdocs/Backend/src/controllers/
Upload File :
Current File : /var/www/api-mk-planner.bitkit.dk/httpdocs/Backend/src/controllers/apiController.js

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