const express = require('express'); const bodyParser = require('body-parser'); const fs = require('fs'); const path = require('path'); const PizZip = require('pizzip'); const vCard = require('vcf'); const Docxtemplater = require('docxtemplater'); const ExcelJS = require('exceljs'); const ics = require('ics'); const dayjs = require('dayjs'); const advancedFormat = require('dayjs/plugin/advancedFormat'); const numberToWords = require('number-to-words'); const { fieldToCellMap } = require('./constants'); // Define directoryPath for Client Folder Location // Define possible directory paths const linuxPath = '/mnt/DropBox/clients'; const windowsPath = 'D:\\Bklistservinfo Dropbox\\Ryan Callahan\\clients'; // Function to check if a directory exists and is accessible function isDirectoryAccessible(path) { try { if (!fs.existsSync(path)) { console.log(`Path does not exist: ${path}`); return false; } if (!fs.statSync(path).isDirectory()) { console.log(`Path is not a directory: ${path}`); return false; } fs.accessSync(path, fs.constants.W_OK); return true; } catch (err) { console.log(`No access to path: ${path}, Error: ${err.message}`); return false; } } // Use the Linux path if it's accessible; otherwise, fallback to Windows path const directoryPath = isDirectoryAccessible(linuxPath) ? linuxPath : windowsPath; console.log(`Using directory path: ${directoryPath}`); dayjs.extend(advancedFormat); let today = dayjs(); console.log(today.format('MMMM Do, YYYY')); // Output: July 14th, 2023 const app = express(); // Default to root for local dev — set BASE_PATH to "/" locally or to "/your-base" behind Apache const BASE_PATH = process.env.BASE_PATH || '/'; const router = express.Router(); // Apply JSON/body-parser and static middleware to the router so routes are mounted under BASE_PATH router.use(bodyParser.json()); router.use(express.json()); // Middleware to parse JSON bodies router.use(express.static(path.join(__dirname, 'public'))); function generateDocument(data, documentPath) { let content = fs.readFileSync( path.resolve(__dirname, documentPath), 'binary' ); let zip = new PizZip(content); let doc = new Docxtemplater(); doc.loadZip(zip); doc.setData(data); try { doc.render(); } catch (error) { var e = { message: error.message, name: error.name, stack: error.stack, properties: error.properties, }; console.log(JSON.stringify({ error: e })); throw error; } return doc.getZip().generate({ type: 'nodebuffer' }); } async function generateClientFolder(data) { const folderPath = directoryPath; // Path for the folder // Check if directoryPath exists if (!fs.existsSync(folderPath)) { console.error(`Error: The directory path ${folderPath} does not exist.`); return; // Exit the function without crashing the code } const subFolderPath = path.join(folderPath, `${data.clientLastName}, ${data.clientFirstName}`); // Path for the subfolder const subfolderCasePath = path.join(subFolderPath, `${data.caseNumber}_${data.casePlaintiffFileName}`); // Path for the case subfolder // Check if the main "client" folder exists if (!fs.existsSync(folderPath)) { // If it doesn't exist, create the "client" folder fs.mkdirSync(folderPath, { recursive: true }); console.log(`Folder created at: ${folderPath}`); } // Check if the subfolder for the client exists if (!fs.existsSync(subFolderPath)) { // If it doesn't exist, create the subfolder fs.mkdirSync(subFolderPath, { recursive: true }); console.log(`Subfolder created at: ${subFolderPath}`); } else { console.log(`Subfolder already exists at: ${subFolderPath}`); } // Check if the case documents subfolder exists if (!fs.existsSync(subfolderCasePath)) { // If it doesn't exist, create the case documents subfolder fs.mkdirSync(subfolderCasePath, { recursive: true }); console.log(`Case documents subfolder created at: ${subfolderCasePath}`); } else { console.log(`Case documents subfolder already exists at: ${subfolderCasePath}`); } /* Dynamically import the 'open' module and open the subfolder try { const open = (await import('open')).default; await open(subFolderPath); console.log(`Opened ${subFolderPath}`); } catch (err) { console.error(`Failed to open ${subFolderPath}:`, err); } */ } function createVCard(data) { let card = new vCard(); // Set the fields in the vCard card.set( 'fn', `${data.clientFirstName} ${data.clientMiddleName} ${data.clientLastName} ${data.clientNameSuffix}` ); card.set( 'n', `${data.clientLastName};${data.clientFirstName};${data.clientMiddleName};;${data.clientNameSuffix}` ); card.set('email', data.email); card.add('tel', data.homePhone, { type: 'home' }); card.add('tel', data.cellPhone, { type: 'cell' }); card.add( 'adr', `;;${data.homeAddress};${data.homeCity};${data.homeState};${data.homeZip}`, { type: 'home' } ); // Set the 'adr' field with a type of 'home' card.set('bday', data.dob); card.set( 'note', `Representation on ${data.casePlaintiff} v. ${data.caseDefendant}, ${data.caseNumber}` ); card.set('org', `Clients - ${data.homeState}`); return card.toString(); } async function generateExcel(data) { const workbook = new ExcelJS.Workbook(); await workbook.xlsx.readFile(path.resolve(__dirname, 'template.xlsx')); const worksheet = workbook.getWorksheet(1); for (let field in fieldToCellMap) { if (fieldToCellMap.hasOwnProperty(field) && data.hasOwnProperty(field)) { worksheet.getCell(fieldToCellMap[field]).value = data[field]; } } return await workbook.xlsx.writeBuffer(); } router.post('/generate-excel', async (req, res) => { const data = req.body; let buf = await generateExcel(data); let filename = `${data.clientLastName}-${data.clientFirstName}_${data.caseNumber}_Data.xlsx`; res.setHeader( 'Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ); res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); res.send(buf); }); router.post('/generate-ics', (req, res) => { const data = req.body; const eventName = `${data.casePlaintiff} v. ${data.caseDefendant}`; const location = `${data.caseCounty} County ${data.caseDesignation} Court, Div ${data.caseDivisionNumber}`; const startDateArray = dayjs(data.caseAnswerDate) .format('YYYY-M-D-H-m') .split('-') .map(Number); const endDateArray = dayjs(data.caseAnswerDate) .add(30, 'minute') .format('YYYY-M-D-H-m') .split('-') .map(Number); // Create the HTML table const description = `Case No: ${data.caseNumber}\t Client Name: ${data.clientFirstName} ${data.clientLastName} Appearance Information: Judge: ${data.caseDivisionJudge}; Suit Amount: $${data.caseSuitAmount}; Case Information: First Court Date: ${data.caseAnswerDate}; Opposing Counsel: ${data.caseOpposingCounsel}; Suit Theory: ${data.caseSuitTheory}`; const { error, value } = ics.createEvent({ start: startDateArray, end: endDateArray, title: eventName, description: description, // Add the description here location: location, }); if (error) { console.log(error); return res.status(500).send({ error }); } let filename = `${eventName}.ics`; res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); res.send(value); }); router.post('/generate-MOdisco-ics', (req, res) => { const data = req.body; // Get the Discovery CoS Date from the request body const discoCosDate = data.discoCosDate; // Parse the Discovery CoS Date and add 30 days to get the discovery response date const discoveryResponseDate = dayjs(discoCosDate).add(30, 'days'); const startDateArray = discoveryResponseDate .format('YYYY-M-D') .split('-') .map(Number); const endDateArray = discoveryResponseDate .add(1, 'day') .format('YYYY-M-D') .split('-') .map(Number); // Set the event title, description, and location const eventName = `DISCO due on ${data.casePlaintiff} v. ${data.caseDefendant}, ${data.caseNumber}`; const description = `CoS Date: ${discoCosDate}`; // Add the CoD Date to the description const location = `${data.caseCounty} County ${data.caseDesignation} Court, Div ${data.caseDivisionNumber}`; const { error, value } = ics.createEvent({ start: startDateArray, end: endDateArray, title: eventName, description: description, location: location, }); if (error) { console.log(error); return res.status(500).send({ error }); } let filename = `${eventName}.ics`; res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); res.send(value); }); router.post('/generate-KSdisco-ics', (req, res) => { const data = req.body; // Get the Discovery CoS Date from the request body const discoCosDate = data.discoCosDate; // Parse the Discovery CoS Date and add 30 days to get the discovery response date const discoveryResponseDate = dayjs(discoCosDate).add(14, 'days'); const startDateArray = discoveryResponseDate .format('YYYY-M-D') .split('-') .map(Number); const endDateArray = discoveryResponseDate .add(1, 'day') .format('YYYY-M-D') .split('-') .map(Number); // Set the event title, description, and location const eventName = `DISCO due on ${data.casePlaintiff} v. ${data.caseDefendant}, ${data.caseNumber}`; const description = `CoS Date: ${discoCosDate}`; // Add the CoD Date to the description const location = `${data.caseCounty} County ${data.caseDesignation} Court, Div ${data.caseDivisionNumber}`; const { error, value } = ics.createEvent({ start: startDateArray, end: endDateArray, title: eventName, description: description, location: location, }); if (error) { console.log(error); return res.status(500).send({ error }); } let filename = `${eventName}.ics`; res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); res.send(value); }); router.post('/generate-vCard', (req, res) => { const data = req.body; // This is your form data let vCardString = createVCard(data); let filename = `${data.caseNumber}-contact.vcf`; // Modify this line to use your desired filename res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); res.type('text/vcard'); res.send(vCardString); }); router.post('/generate-reminder-ics', (req, res) => { const data = req.body; // Get the current date const today = dayjs(); // Add 45 days to the current date const futureDate = today.add(45, 'days'); const startDateArray = futureDate .format('YYYY-M-D') .split('-') .map(Number); const endDateArray = futureDate .add(1, 'day') .format('YYYY-M-D') .split('-') .map(Number); // Set the event title, description, and location const todayDate = dayjs().format('MM/DD/YYYY'); const eventName = `REMINDER - ${data.clientFirstName} ${data.clientLastName} - Check Credit Dispute Reporting`; let description = `FDCPA Disputes were mailed on ${todayDate} to:\n`; for (let i = 1; i <= 9; i++) { let name = data[`debtCollector${i}Name`]; let creditor = data[`debtCollector${i}Creditor`]; let account = data[`debtCollector${i}Account`]; let amount = data[`debtCollector${i}Amount`]; if (name) { description += `- ${name} \n \tregarding a ${creditor} account (Acc. No. ${account}) for $${amount},\n`; } } // Remove the last comma and newline description = description.trim().slice(0, -1); const { error, value } = ics.createEvent({ start: startDateArray, end: endDateArray, title: eventName, description: description, }); if (error) { console.log(error); return res.status(500).send({ error }); } let filename = `${eventName}.ics`; res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); res.send(value); }); router.post('/generate-vCard', (req, res) => { const data = req.body; // This is your form data let vCardString = createVCard(data); let filename = `${data.caseNumber}-contact.vcf`; // Modify this line to use your desired filename res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); res.type('text/vcard'); res.send(vCardString); }); router.post('/generate-ics-body', (req, res) => { const data = req.body; let buf = generateDocument(data, 'isc-body.docx'); let filename = `${data.caseNumber}-ics-body.docx`; res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); res.send(buf); }); router.post('/generate-retainer', (req, res) => { const data = req.body; let buf = generateDocument(data, 'retainer.docx'); let filename = `${data.clientLastName}-${data.clientFirstName}_${data.caseNumber}_Representation-and-Fee-Agreement.docx`; res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); res.send(buf); }); router.post('/generate-eoa', (req, res) => { const data = req.body; let buf = generateDocument(data, 'entry.docx'); let filename = `${data.caseNumber}_EoA.docx`; res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); res.send(buf); }); router.post('/generate-notes', (req, res) => { const data = req.body; let buf = generateDocument(data, 'notes.docx'); let filename = `${data.clientLastName}-${data.clientFirstName} - Intake and Notes.docx`; res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); res.send(buf); }); router.post('/generate-spreadsheet', (req, res) => { const data = req.body; let buf = generateDocument(data, 'spreadsheets.docx'); let filename = `${data.clientLastName}-${data.clientFirstName}_Spreadsheets.docx`; res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); res.send(buf); }); router.post('/generate-ADs', (req, res) => { const data = req.body; let buf = generateDocument(data, 'defenses.docx'); let filename = `${data.caseNumber}_Affirmative-Defenses.docx`; res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); res.send(buf); }); router.post('/generate-closeout', (req, res) => { const data = req.body; let buf = generateDocument(data, 'closeout.docx'); let filename = `${data.caseNumber}_${data.clientLastName}_Closeout-Letter.docx`; res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); res.send(buf); }); router.post('/generate-closeout-with-costs', (req, res) => { const data = req.body; let buf = generateDocument(data, 'closeout_CJ_with-interest-costs.docx'); let filename = `${data.caseNumber}_${data.clientLastName}_Closeout.docx`; res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); res.send(buf); }); router.post('/generate-waiver', (req, res) => { const data = req.body; let buf = generateDocument(data, 'waiver.docx'); let filename = `${data.caseNumber}_Service-Waiver.docx`; res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); res.send(buf); }); router.post('/generate-pleading', (req, res) => { const data = req.body; let templateName = data.templateName; // This should be sent from the client side if (!templateName) { res.status(400).send('Template name is required'); return; } let templatePath = path.join(__dirname, 'Pleadings', `${templateName}.docx`); let buf = generateDocument(data, templatePath); let filename = `${data.caseNumber}_Pleading.docx`; res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); res.send(buf); }); router.post('/generate-disco', (req, res) => { const data = req.body; let templateName = data.templateName; // This should be sent from the client side if (!templateName) { res.status(400).send('Template name is required'); return; } let templatePath = path.join(__dirname, 'Discovery', `${templateName}.docx`); let buf = generateDocument(data, templatePath); let filename = `${data.caseNumber}_Disco-Responses.docx`; res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); res.send(buf); }); router.post('/generate-letter', (req, res) => { const data = req.body; let templateName = data.templateName; // This should be sent from the client side if (!templateName) { res.status(400).send('Template name is required'); return; } let templatePath = path.join(__dirname, 'Letters', `${templateName}.docx`); let buf = generateDocument(data, templatePath); let filename = `${data.clientLastName}-${data.clientFirstName}_${data.caseNumber}_Letter.docx`; res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); res.send(buf); }); router.post('/generate-Canned-Emails', (req, res) => { const data = req.body; let buf = generateDocument(data, 'Canned-Emails.docx'); let filename = `${data.caseNumber}_Canned-Emails.docx`; res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); res.send(buf); }); router.post('/generate-Dispute-Letter', (req, res) => { const data = req.body; let buf = generateDocument(data, 'letter.docx'); let date = dayjs().format('YYYY-MM-DD'); let filename = `${data.clientLastName}_FDCPA-Disputes_${date}.docx`; res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); res.send(buf); }); router.post('/generate-Dispute-Envelope', (req, res) => { const data = req.body; let buf = generateDocument(data, 'dispute-envelopes.docx'); let date = dayjs().format('YYYY-MM-DD'); let filename = `${data.clientLastName}_FDCPA-Disputes_${date}_Envelopes.docx`; res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); res.send(buf); }); router.post('/generate-Dispute-Cert', (req, res) => { const data = req.body; let buf = generateDocument(data, 'cert-mailing.docx'); let date = dayjs().format('YYYY-MM-DD'); let filename = `${data.clientLastName}_FDCPA-Disputes_${date}_Mail-Cert.docx`; res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); res.send(buf); }); router.post('/generate-client-folder', (req, res) => { const data = req.body; let buf = generateClientFolder(data); res.send(buf); }); router.post('/generate-credit-auth', (req, res) => { const data = req.body; let buf = generateDocument(data, 'authorization-disclosure.docx'); let filename = `${data.clientLastName}-${data.clientFirstName}_Credit-Authorization.docx`; res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); res.send(buf); }); router.post('/generate-credit-auth-no-payment', (req, res) => { const data = req.body; let buf = generateDocument(data, 'authorization-disclosure-no-payment.docx'); let filename = `${data.clientLastName}-${data.clientFirstName}_Credit-Authorization-No-Payment.docx`; res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); res.send(buf); }); // Mount router under BASE_PATH if (BASE_PATH === '/') { app.use('/', router); // local dev } else { app.use(BASE_PATH, router); // behind Apache or with a base path } const PORT = process.env.PORT || 3001; app.listen(PORT, '0.0.0.0', () => console.log(`Server is running and accessible on ${BASE_PATH} at port ${PORT}`));