Skip to content

Commit 11a2f6b

Browse files
feat: add resend mail api, add expiry date in create document api & return certificate url in get document api
1 parent 56dd060 commit 11a2f6b

File tree

6 files changed

+171
-2
lines changed

6 files changed

+171
-2
lines changed

apps/OpenSignServer/cloud/customRoute/v1/apiV1.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import getTemplatetList from './routes/getTemplateList.js';
1717
import updateTemplate from './routes/updateTemplate.js';
1818
import createContact from './routes/createContact.js';
1919
import multer from 'multer';
20-
// import fs from 'node:fs';
2120
import updateDocument from './routes/updateDocument.js';
2221
import deleteDocument from './routes/deleteDocument.js';
2322
import createDocumentWithTemplate from './routes/CreateDocumentWithTemplate.js';
@@ -26,6 +25,7 @@ import deleteWebhook from './routes/deleteWebhook.js';
2625
import getWebhook from './routes/getWebhook.js';
2726
import createDocumentwithCoordinate from './routes/createDocumentwithCoordinate.js';
2827
import createTemplatewithCoordinate from './routes/createTemplatewithCoordinate.js';
28+
import resendMail from './routes/resendMail.js';
2929
dotenv.config();
3030
const storage = multer.memoryStorage();
3131
const upload = multer({ storage: storage });
@@ -102,3 +102,7 @@ app.post('/webhook', saveWebhook);
102102

103103
// set and update webhook
104104
app.delete('/webhook', deleteWebhook);
105+
106+
// resend mail
107+
app.post('/resendmail', resendMail);
108+

apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export default async function createDocumentWithTemplate(request, response) {
5555
const email_subject = request.body.email_subject;
5656
const email_body = request.body.email_body;
5757
const sendInOrder = request.body.sendInOrder || false;
58+
const TimeToCompleteDays = request.body.TimeToCompleteDays || 15;
5859

5960
try {
6061
const reqToken = request.headers['x-api-token'];
@@ -178,6 +179,9 @@ export default async function createDocumentWithTemplate(request, response) {
178179
}
179180
object.set('URL', template.URL);
180181
object.set('SignedUrl', template.URL);
182+
if (TimeToCompleteDays) {
183+
object.set('TimeToCompleteDays', TimeToCompleteDays);
184+
}
181185
object.set('CreatedBy', template.CreatedBy);
182186
object.set('ExtUserPtr', {
183187
__type: 'Pointer',

apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export default async function createDocumentwithCoordinate(request, response) {
6666
const email_subject = request.body.email_subject;
6767
const email_body = request.body.email_body;
6868
const sendInOrder = request.body.sendInOrder || false;
69+
const TimeToCompleteDays = request.body.TimeToCompleteDays || 15;
6970
// console.log('fileData ', fileData);
7071
const protocol = customAPIurl();
7172
const baseUrl = new URL(process.env.SERVER_URL);
@@ -142,6 +143,9 @@ export default async function createDocumentwithCoordinate(request, response) {
142143
object.set('SignedUrl', fileUrl);
143144
object.set('CreatedBy', userPtr);
144145
object.set('ExtUserPtr', extUserPtr);
146+
if (TimeToCompleteDays) {
147+
object.set('TimeToCompleteDays', TimeToCompleteDays);
148+
}
145149
object.set('IsSendMail', send_email);
146150
let contact = [];
147151
if (signers && signers.length > 0) {

apps/OpenSignServer/cloud/customRoute/v1/routes/getDocument.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export default async function getDocument(request, response) {
4040
note: document.Note || '',
4141
folder: { objectId: document?.Folder?.objectId, name: document?.Folder?.Name } || '',
4242
file: document?.SignedUrl || document.URL,
43+
certificateUrl: document?.CertificateUrl || '',
4344
owner: document?.ExtUserPtr?.Name,
4445
signers:
4546
document?.Placeholders?.map(y => ({

apps/OpenSignServer/cloud/customRoute/v1/routes/getDocumentList.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export default async function getDocumentList(request, response) {
7777
title: x.Name,
7878
note: x.Note || '',
7979
folder: { objectId: x?.Folder?.objectId, name: x?.Folder?.Name } || '',
80-
file: x?.SignedUrl || x.URL,
80+
// file: x?.SignedUrl || x.URL,
8181
owner: x?.ExtUserPtr?.Name,
8282
signers:
8383
x?.Placeholders?.map(y => ({
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
import axios from 'axios';
2+
import { replaceMailVaribles } from '../../../../Utils.js';
3+
4+
export default async function resendMail(request, response) {
5+
try {
6+
const reqToken = request.headers['x-api-token'];
7+
const docId = request.body.document_id;
8+
const userMail = request.body.email;
9+
const email_subject = request.body.email_subject;
10+
const email_body = request.body.email_body;
11+
const baseUrl = new URL(process.env.SERVER_URL);
12+
13+
if (!reqToken) {
14+
return response.status(400).json({ error: 'Please Provide API Token' });
15+
}
16+
const tokenQuery = new Parse.Query('appToken');
17+
tokenQuery.equalTo('token', reqToken);
18+
tokenQuery.include('userId');
19+
const token = await tokenQuery.first({ useMasterKey: true });
20+
if (token !== undefined) {
21+
// Valid Token then proceed request
22+
const parseUser = JSON.parse(JSON.stringify(token));
23+
const userPtr = {
24+
__type: 'Pointer',
25+
className: '_User',
26+
objectId: parseUser.userId.objectId,
27+
};
28+
29+
const docQuery = new Parse.Query('contracts_Document');
30+
docQuery.equalTo('objectId', docId);
31+
docQuery.equalTo('CreatedBy', userPtr);
32+
docQuery.include('Signers,ExtUserPtr');
33+
docQuery.notEqualTo('IsCompleted', true);
34+
docQuery.notEqualTo('IsDeclined', true);
35+
docQuery.notEqualTo('IsArchive', true);
36+
docQuery.lessThanOrEqualTo('ExpiryDate', new Date());
37+
docQuery.exists('SignedUrl');
38+
39+
const resDoc = await docQuery.first({ useMasterKey: true });
40+
if (resDoc) {
41+
const _resDoc = resDoc.toJSON();
42+
const contact = _resDoc.Signers.find(x => x.Email === userMail);
43+
if (contact) {
44+
try {
45+
const imgPng = 'https://qikinnovation.ams3.digitaloceanspaces.com/logo.png';
46+
let url = `${process.env.SERVER_URL}/functions/sendmailv3/`;
47+
const headers = {
48+
'Content-Type': 'application/json',
49+
'X-Parse-Application-Id': process.env.APP_ID,
50+
'X-Parse-Master-Key': process.env.MASTER_KEY,
51+
};
52+
53+
const objectId = contact.objectId;
54+
const hostUrl = baseUrl.origin;
55+
const title = _resDoc.Name;
56+
const receiverMail = contact.Email;
57+
//encode this url value `${response.id}/${receiverMail}/${objectId}` to base64 using `btoa` function
58+
const encodeBase64 = btoa(`${_resDoc.objectId}/${receiverMail}/${objectId}`);
59+
let signPdf = `${hostUrl}/login/${encodeBase64}`;
60+
const openSignUrl = 'https://www.opensignlabs.com/contact-us';
61+
const orgName = _resDoc.ExtUserPtr.Company ? _resDoc.ExtUserPtr.Company : '';
62+
const newDate = new Date(_resDoc.ExpiryDate.iso);
63+
newDate.setDate(newDate.getDate() + 15);
64+
const sender = _resDoc.ExtUserPtr.Email;
65+
const localExpireDate = newDate.toLocaleDateString('en-US', {
66+
day: 'numeric',
67+
month: 'long',
68+
year: 'numeric',
69+
});
70+
71+
const themeBGcolor = '#47a3ad';
72+
const email_html =
73+
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /> </head> <body> <div style='background-color: #f5f5f5; padding: 20px'> <div style=' box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background: white;padding-bottom: 20px;'> <div style='padding:10px 10px 0 10px'><img src=" +
74+
imgPng +
75+
" height='50' style='padding:20px; width:170px; height:40px;' /></div> <div style='padding:2px; font-family: system-ui;background-color:" +
76+
themeBGcolor +
77+
";'><p style='font-size: 20px;font-weight: 400;color: white;padding-left: 20px;' > Digital Signature Request</p></div><div><p style='padding: 20px;font-family: system-ui;font-size: 14px; margin-bottom: 10px;'> " +
78+
_resDoc.ExtUserPtr.Name +
79+
' has requested you to review and sign <strong> ' +
80+
title +
81+
"</strong>.</p><div style='padding: 5px 0px 5px 25px;display: flex;flex-direction: row;justify-content: space-around;'><table> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Sender</td> <td> </td> <td style='color:#626363;font-weight:bold'>" +
82+
sender +
83+
"</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Organization</td> <td> </td><td style='color:#626363;font-weight:bold'> " +
84+
orgName +
85+
"</td></tr> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expires on</td><td> </td> <td style='color:#626363;font-weight:bold'>" +
86+
localExpireDate +
87+
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a href=" +
88+
signPdf +
89+
"> <button style='padding: 12px 12px 12px 12px;background-color: #d46b0f;color: white; border: 0px;box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;font-weight:bold;margin-top:30px'>Sign here</button></a> </div> <div style='display: flex; justify-content: center;margin-top: 10px;'> </div></div></div><div><p> This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " +
90+
sender +
91+
' directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href=' +
92+
openSignUrl +
93+
' target=_blank>here</a>.</p> </div></div></body> </html>';
94+
let replaceVar;
95+
const variables = {
96+
document_title: title,
97+
sender_name: _resDoc.ExtUserPtr.Name,
98+
sender_mail: _resDoc.ExtUserPtr.Email,
99+
sender_phone: _resDoc.ExtUserPtr.Phone,
100+
receiver_name: contact.Name,
101+
receiver_email: contact.Email,
102+
receiver_phone: contact.Phone,
103+
expiry_date: localExpireDate,
104+
company_name: orgName,
105+
signing_url: signPdf,
106+
};
107+
if (email_subject && email_body) {
108+
replaceVar = replaceMailVaribles(email_subject, email_body, variables);
109+
} else if (email_subject) {
110+
replaceVar = replaceMailVaribles(email_subject, '', variables);
111+
replaceVar = { subject: replaceVar.subject, body: email_html };
112+
} else if (email_body) {
113+
replaceVar = replaceMailVaribles(
114+
`${_resDoc.ExtUserPtr.Name} has requested you to sign "${title}"`,
115+
email_body,
116+
variables
117+
);
118+
} else {
119+
replaceVar = {
120+
subject: `${_resDoc.ExtUserPtr.Name} has requested you to sign "${title}"`,
121+
body: email_html,
122+
};
123+
}
124+
const subject = replaceVar.subject;
125+
const html = replaceVar.body;
126+
127+
let params = {
128+
recipient: contact.Email,
129+
subject: subject,
130+
from: sender,
131+
html: html,
132+
extUserId: _resDoc.ExtUserPtr.objectId,
133+
};
134+
135+
const res = await axios.post(url, params, { headers: headers });
136+
if (res.data.result && res.data.result.status === 'success') {
137+
return response.json({ result: 'mail sent successfully.' });
138+
}
139+
} catch (error) {
140+
console.log('error', error);
141+
return response.status(400).json({ error: error.message });
142+
}
143+
} else {
144+
return response.status(404).json({ error: 'user not found.' });
145+
}
146+
} else {
147+
return response.status(404).json({ error: 'document not found.' });
148+
}
149+
} else {
150+
return response.status(405).json({ error: 'Invalid API Token.' });
151+
}
152+
} catch (err) {
153+
console.log('err in resendmail', err);
154+
return response.status(400).json({ error: err.message || 'Something went wrong.' });
155+
}
156+
}

0 commit comments

Comments
 (0)