From 11a2f6b8ec484ae017dceeef796eab1511d4e184 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs Date: Fri, 10 May 2024 21:07:06 +0530 Subject: [PATCH] feat: add resend mail api, add expiry date in create document api & return certificate url in get document api --- .../cloud/customRoute/v1/apiV1.js | 6 +- .../v1/routes/CreateDocumentWithTemplate.js | 4 + .../v1/routes/createDocumentwithCoordinate.js | 4 + .../customRoute/v1/routes/getDocument.js | 1 + .../customRoute/v1/routes/getDocumentList.js | 2 +- .../cloud/customRoute/v1/routes/resendMail.js | 156 ++++++++++++++++++ 6 files changed, 171 insertions(+), 2 deletions(-) create mode 100644 apps/OpenSignServer/cloud/customRoute/v1/routes/resendMail.js diff --git a/apps/OpenSignServer/cloud/customRoute/v1/apiV1.js b/apps/OpenSignServer/cloud/customRoute/v1/apiV1.js index 343db0d3c..e4f532853 100644 --- a/apps/OpenSignServer/cloud/customRoute/v1/apiV1.js +++ b/apps/OpenSignServer/cloud/customRoute/v1/apiV1.js @@ -17,7 +17,6 @@ import getTemplatetList from './routes/getTemplateList.js'; import updateTemplate from './routes/updateTemplate.js'; import createContact from './routes/createContact.js'; import multer from 'multer'; -// import fs from 'node:fs'; import updateDocument from './routes/updateDocument.js'; import deleteDocument from './routes/deleteDocument.js'; import createDocumentWithTemplate from './routes/CreateDocumentWithTemplate.js'; @@ -26,6 +25,7 @@ import deleteWebhook from './routes/deleteWebhook.js'; import getWebhook from './routes/getWebhook.js'; import createDocumentwithCoordinate from './routes/createDocumentwithCoordinate.js'; import createTemplatewithCoordinate from './routes/createTemplatewithCoordinate.js'; +import resendMail from './routes/resendMail.js'; dotenv.config(); const storage = multer.memoryStorage(); const upload = multer({ storage: storage }); @@ -102,3 +102,7 @@ app.post('/webhook', saveWebhook); // set and update webhook app.delete('/webhook', deleteWebhook); + +// resend mail +app.post('/resendmail', resendMail); + diff --git a/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js b/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js index 630fe1090..70dfd94b6 100644 --- a/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js +++ b/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js @@ -55,6 +55,7 @@ export default async function createDocumentWithTemplate(request, response) { const email_subject = request.body.email_subject; const email_body = request.body.email_body; const sendInOrder = request.body.sendInOrder || false; + const TimeToCompleteDays = request.body.TimeToCompleteDays || 15; try { const reqToken = request.headers['x-api-token']; @@ -178,6 +179,9 @@ export default async function createDocumentWithTemplate(request, response) { } object.set('URL', template.URL); object.set('SignedUrl', template.URL); + if (TimeToCompleteDays) { + object.set('TimeToCompleteDays', TimeToCompleteDays); + } object.set('CreatedBy', template.CreatedBy); object.set('ExtUserPtr', { __type: 'Pointer', diff --git a/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js b/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js index 40b124cd8..993faa6d6 100644 --- a/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js +++ b/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js @@ -66,6 +66,7 @@ export default async function createDocumentwithCoordinate(request, response) { const email_subject = request.body.email_subject; const email_body = request.body.email_body; const sendInOrder = request.body.sendInOrder || false; + const TimeToCompleteDays = request.body.TimeToCompleteDays || 15; // console.log('fileData ', fileData); const protocol = customAPIurl(); const baseUrl = new URL(process.env.SERVER_URL); @@ -142,6 +143,9 @@ export default async function createDocumentwithCoordinate(request, response) { object.set('SignedUrl', fileUrl); object.set('CreatedBy', userPtr); object.set('ExtUserPtr', extUserPtr); + if (TimeToCompleteDays) { + object.set('TimeToCompleteDays', TimeToCompleteDays); + } object.set('IsSendMail', send_email); let contact = []; if (signers && signers.length > 0) { diff --git a/apps/OpenSignServer/cloud/customRoute/v1/routes/getDocument.js b/apps/OpenSignServer/cloud/customRoute/v1/routes/getDocument.js index cc1e1e03c..30e13d1cb 100644 --- a/apps/OpenSignServer/cloud/customRoute/v1/routes/getDocument.js +++ b/apps/OpenSignServer/cloud/customRoute/v1/routes/getDocument.js @@ -40,6 +40,7 @@ export default async function getDocument(request, response) { note: document.Note || '', folder: { objectId: document?.Folder?.objectId, name: document?.Folder?.Name } || '', file: document?.SignedUrl || document.URL, + certificateUrl: document?.CertificateUrl || '', owner: document?.ExtUserPtr?.Name, signers: document?.Placeholders?.map(y => ({ diff --git a/apps/OpenSignServer/cloud/customRoute/v1/routes/getDocumentList.js b/apps/OpenSignServer/cloud/customRoute/v1/routes/getDocumentList.js index 5f5b49e71..1deae9605 100644 --- a/apps/OpenSignServer/cloud/customRoute/v1/routes/getDocumentList.js +++ b/apps/OpenSignServer/cloud/customRoute/v1/routes/getDocumentList.js @@ -77,7 +77,7 @@ export default async function getDocumentList(request, response) { title: x.Name, note: x.Note || '', folder: { objectId: x?.Folder?.objectId, name: x?.Folder?.Name } || '', - file: x?.SignedUrl || x.URL, + // file: x?.SignedUrl || x.URL, owner: x?.ExtUserPtr?.Name, signers: x?.Placeholders?.map(y => ({ diff --git a/apps/OpenSignServer/cloud/customRoute/v1/routes/resendMail.js b/apps/OpenSignServer/cloud/customRoute/v1/routes/resendMail.js new file mode 100644 index 000000000..cbca5db24 --- /dev/null +++ b/apps/OpenSignServer/cloud/customRoute/v1/routes/resendMail.js @@ -0,0 +1,156 @@ +import axios from 'axios'; +import { replaceMailVaribles } from '../../../../Utils.js'; + +export default async function resendMail(request, response) { + try { + const reqToken = request.headers['x-api-token']; + const docId = request.body.document_id; + const userMail = request.body.email; + const email_subject = request.body.email_subject; + const email_body = request.body.email_body; + const baseUrl = new URL(process.env.SERVER_URL); + + if (!reqToken) { + return response.status(400).json({ error: 'Please Provide API Token' }); + } + const tokenQuery = new Parse.Query('appToken'); + tokenQuery.equalTo('token', reqToken); + tokenQuery.include('userId'); + const token = await tokenQuery.first({ useMasterKey: true }); + if (token !== undefined) { + // Valid Token then proceed request + const parseUser = JSON.parse(JSON.stringify(token)); + const userPtr = { + __type: 'Pointer', + className: '_User', + objectId: parseUser.userId.objectId, + }; + + const docQuery = new Parse.Query('contracts_Document'); + docQuery.equalTo('objectId', docId); + docQuery.equalTo('CreatedBy', userPtr); + docQuery.include('Signers,ExtUserPtr'); + docQuery.notEqualTo('IsCompleted', true); + docQuery.notEqualTo('IsDeclined', true); + docQuery.notEqualTo('IsArchive', true); + docQuery.lessThanOrEqualTo('ExpiryDate', new Date()); + docQuery.exists('SignedUrl'); + + const resDoc = await docQuery.first({ useMasterKey: true }); + if (resDoc) { + const _resDoc = resDoc.toJSON(); + const contact = _resDoc.Signers.find(x => x.Email === userMail); + if (contact) { + try { + const imgPng = 'https://qikinnovation.ams3.digitaloceanspaces.com/logo.png'; + let url = `${process.env.SERVER_URL}/functions/sendmailv3/`; + const headers = { + 'Content-Type': 'application/json', + 'X-Parse-Application-Id': process.env.APP_ID, + 'X-Parse-Master-Key': process.env.MASTER_KEY, + }; + + const objectId = contact.objectId; + const hostUrl = baseUrl.origin; + const title = _resDoc.Name; + const receiverMail = contact.Email; + //encode this url value `${response.id}/${receiverMail}/${objectId}` to base64 using `btoa` function + const encodeBase64 = btoa(`${_resDoc.objectId}/${receiverMail}/${objectId}`); + let signPdf = `${hostUrl}/login/${encodeBase64}`; + const openSignUrl = 'https://www.opensignlabs.com/contact-us'; + const orgName = _resDoc.ExtUserPtr.Company ? _resDoc.ExtUserPtr.Company : ''; + const newDate = new Date(_resDoc.ExpiryDate.iso); + newDate.setDate(newDate.getDate() + 15); + const sender = _resDoc.ExtUserPtr.Email; + const localExpireDate = newDate.toLocaleDateString('en-US', { + day: 'numeric', + month: 'long', + year: 'numeric', + }); + + const themeBGcolor = '#47a3ad'; + const email_html = + "

Digital Signature Request

" + + _resDoc.ExtUserPtr.Name + + ' has requested you to review and sign ' + + title + + ".

Sender " + + sender + + "
Organization " + + orgName + + "
Expires on " + + localExpireDate + + "

This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " + + sender + + ' directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ here.

'; + let replaceVar; + const variables = { + document_title: title, + sender_name: _resDoc.ExtUserPtr.Name, + sender_mail: _resDoc.ExtUserPtr.Email, + sender_phone: _resDoc.ExtUserPtr.Phone, + receiver_name: contact.Name, + receiver_email: contact.Email, + receiver_phone: contact.Phone, + expiry_date: localExpireDate, + company_name: orgName, + signing_url: signPdf, + }; + if (email_subject && email_body) { + replaceVar = replaceMailVaribles(email_subject, email_body, variables); + } else if (email_subject) { + replaceVar = replaceMailVaribles(email_subject, '', variables); + replaceVar = { subject: replaceVar.subject, body: email_html }; + } else if (email_body) { + replaceVar = replaceMailVaribles( + `${_resDoc.ExtUserPtr.Name} has requested you to sign "${title}"`, + email_body, + variables + ); + } else { + replaceVar = { + subject: `${_resDoc.ExtUserPtr.Name} has requested you to sign "${title}"`, + body: email_html, + }; + } + const subject = replaceVar.subject; + const html = replaceVar.body; + + let params = { + recipient: contact.Email, + subject: subject, + from: sender, + html: html, + extUserId: _resDoc.ExtUserPtr.objectId, + }; + + const res = await axios.post(url, params, { headers: headers }); + if (res.data.result && res.data.result.status === 'success') { + return response.json({ result: 'mail sent successfully.' }); + } + } catch (error) { + console.log('error', error); + return response.status(400).json({ error: error.message }); + } + } else { + return response.status(404).json({ error: 'user not found.' }); + } + } else { + return response.status(404).json({ error: 'document not found.' }); + } + } else { + return response.status(405).json({ error: 'Invalid API Token.' }); + } + } catch (err) { + console.log('err in resendmail', err); + return response.status(400).json({ error: err.message || 'Something went wrong.' }); + } +}