Skip to content

Commit 98c8af5

Browse files
feat: create document and template with coordinate
1 parent 8bbbf9b commit 98c8af5

File tree

5 files changed

+457
-5
lines changed

5 files changed

+457
-5
lines changed

apps/OpenSignServer/Utils.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,18 @@ export function customAPIurl() {
22
const url = new URL(process.env.SERVER_URL);
33
return url.pathname === '/api/app' ? url.origin + '/api' : url.origin;
44
}
5+
6+
export const color = [
7+
'#93a3db',
8+
'#e6c3db',
9+
'#c0e3bc',
10+
'#bce3db',
11+
'#b8ccdb',
12+
'#ceb8db',
13+
'#ffccff',
14+
'#99ffcc',
15+
'#cc99ff',
16+
'#ffcc99',
17+
'#66ccff',
18+
'#ffffcc',
19+
];

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

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ import createDocumentWithTemplate from './routes/CreateDocumentWithTemplate.js';
2424
import saveWebhook from './routes/saveWebhook.js';
2525
import deleteWebhook from './routes/deleteWebhook.js';
2626
import getWebhook from './routes/getWebhook.js';
27-
27+
import createDocumentwithCoordinate from './routes/createDocumentwithCoordinate.js';
28+
import createTemplatewithCoordinate from './routes/createTemplatewithCoordinate.js';
2829
dotenv.config();
2930
const storage = multer.memoryStorage();
3031
const upload = multer({ storage: storage });
@@ -51,8 +52,11 @@ app.get('/contactlist', getContactList);
5152
// create Document
5253
app.post('/createdocumentwithbinary', upload.array('file', 1), createDocument);
5354

54-
// create Document with base64
55-
app.post('/createdocument', createDocument);
55+
// create Document with co-ordinate
56+
app.post('/createdocument', upload.array('file', 1), createDocumentwithCoordinate);
57+
58+
// create Document with base64 without placeholder
59+
app.post('/draftdocument', createDocument);
5660

5761
// create Document with templateId
5862
app.post('/createdocument/:template_id', createDocumentWithTemplate);
@@ -69,8 +73,11 @@ app.delete('/document/:document_id', deleteDocument);
6973
// get all types of documents on the basis of doctype
7074
app.get('/documentlist/:doctype', getDocumentList);
7175

76+
// create Template with co-ordinate
77+
app.post('/createtemplate', upload.array('file', 1), createTemplatewithCoordinate);
78+
7279
// create Template
73-
app.post('/createtemplate', createTemplate);
80+
app.post('/drafttemplate', createTemplate);
7481

7582
// create Template with binary
7683
app.post('/createtemplatewithbinary', upload.array('file', 1), createTemplate);
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
import axios from 'axios';
2+
import { color, customAPIurl } from '../../../../Utils.js';
3+
4+
const randomId = () => Math.floor(1000 + Math.random() * 9000);
5+
export default async function createDocumentwithCoordinate(request, response) {
6+
const name = request.body.title;
7+
const note = request.body.note;
8+
const description = request.body.description;
9+
const signers = request.body.signers;
10+
const folderId = request.body.folderId;
11+
const base64File = request.body.file;
12+
const fileData = request.files?.[0] ? request.files[0].buffer : null;
13+
// console.log('fileData ', fileData);
14+
const protocol = customAPIurl();
15+
const baseUrl = new URL(process.env.SERVER_URL);
16+
17+
try {
18+
const reqToken = request.headers['x-api-token'];
19+
if (!reqToken) {
20+
return response.status(400).json({ error: 'Please Provide API Token' });
21+
}
22+
const tokenQuery = new Parse.Query('appToken');
23+
tokenQuery.equalTo('token', reqToken);
24+
const token = await tokenQuery.first({ useMasterKey: true });
25+
if (token !== undefined) {
26+
// Valid Token then proceed request
27+
if (signers && signers.length > 0) {
28+
const userPtr = token.get('userId');
29+
let fileUrl;
30+
if (request.files?.[0]) {
31+
const file = new Parse.File(request.files?.[0]?.originalname, {
32+
base64: fileData?.toString('base64'),
33+
});
34+
await file.save({ useMasterKey: true });
35+
fileUrl = file.url();
36+
} else {
37+
const file = new Parse.File(`${name}.pdf`, {
38+
base64: base64File,
39+
});
40+
await file.save({ useMasterKey: true });
41+
fileUrl = file.url();
42+
}
43+
const contractsUser = new Parse.Query('contracts_Users');
44+
contractsUser.equalTo('UserId', userPtr);
45+
const extUser = await contractsUser.first({ useMasterKey: true });
46+
const parseExtUser = JSON.parse(JSON.stringify(extUser));
47+
48+
const extUserPtr = {
49+
__type: 'Pointer',
50+
className: 'contracts_Users',
51+
objectId: extUser.id,
52+
};
53+
54+
const object = new Parse.Object('contracts_Document');
55+
object.set('Name', name);
56+
57+
if (note) {
58+
object.set('Note', note);
59+
}
60+
if (description) {
61+
object.set('Description', description);
62+
}
63+
object.set('URL', fileUrl);
64+
object.set('CreatedBy', userPtr);
65+
object.set('ExtUserPtr', extUserPtr);
66+
let contact = [];
67+
if (signers && signers.length > 0) {
68+
let parseSigners;
69+
if (base64File) {
70+
parseSigners = signers;
71+
} else {
72+
parseSigners = JSON.parse(signers);
73+
}
74+
let createContactUrl = protocol + '/v1/createcontact';
75+
76+
for (const [index, element] of parseSigners.entries()) {
77+
const body = {
78+
name: element?.name || '',
79+
email: element?.email || '',
80+
phone: element?.phone || '',
81+
};
82+
try {
83+
const res = await axios.post(createContactUrl, body, {
84+
headers: { 'Content-Type': 'application/json', 'x-api-token': reqToken },
85+
});
86+
// console.log('res ', res.data);
87+
const contactPtr = {
88+
__type: 'Pointer',
89+
className: 'contracts_Contactbook',
90+
objectId: res.data?.objectId,
91+
};
92+
const newObj = { ...element, contactPtr: contactPtr, index: index };
93+
contact.push(newObj);
94+
} catch (err) {
95+
// console.log('err ', err.response);
96+
if (err?.response?.data?.objectId) {
97+
const contactPtr = {
98+
__type: 'Pointer',
99+
className: 'contracts_Contactbook',
100+
objectId: err.response.data?.objectId,
101+
};
102+
const newObj = { ...element, contactPtr: contactPtr, index: index };
103+
contact.push(newObj);
104+
}
105+
}
106+
}
107+
object.set(
108+
'Signers',
109+
contact?.map(x => x.contactPtr)
110+
);
111+
let updatePlaceholders = contact.map(x => {
112+
return {
113+
signerObjId: x?.contactPtr?.objectId,
114+
signerPtr: x?.contactPtr,
115+
Role: x.role,
116+
Id: randomId(),
117+
blockColor: color[x?.index],
118+
placeHolder: x.widgets.map((widget, i) => ({
119+
pageNumber: widget.page,
120+
pos: [
121+
{
122+
xPosition: widget.x,
123+
yPosition: widget.y,
124+
isStamp: widget.type === 'stamp' || widget.type === 'image' ? true : false,
125+
key: randomId(),
126+
isDrag: false,
127+
firstXPos: widget.x,
128+
firstYPos: widget.y,
129+
yBottom: 0,
130+
scale: 1,
131+
isMobile: false,
132+
zIndex: i + 1,
133+
type: widget.type,
134+
widgetValue: '',
135+
Width: widget.w,
136+
Height: widget.h,
137+
},
138+
],
139+
})),
140+
};
141+
});
142+
object.set('Placeholders', updatePlaceholders);
143+
}
144+
if (folderId) {
145+
object.set('Folder', {
146+
__type: 'Pointer',
147+
className: 'contracts_Document',
148+
objectId: folderId,
149+
});
150+
}
151+
const newACL = new Parse.ACL();
152+
newACL.setPublicReadAccess(false);
153+
newACL.setPublicWriteAccess(false);
154+
newACL.setReadAccess(userPtr.id, true);
155+
newACL.setWriteAccess(userPtr.id, true);
156+
object.setACL(newACL);
157+
const res = await object.save(null, { useMasterKey: true });
158+
159+
const newDate = new Date();
160+
newDate.setDate(newDate.getDate() + 15);
161+
const localExpireDate = newDate.toLocaleDateString('en-US', {
162+
day: 'numeric',
163+
month: 'long',
164+
year: 'numeric',
165+
});
166+
let sender = parseExtUser.Email;
167+
let sendMail;
168+
const serverUrl = process.env.SERVER_URL;
169+
const newServer = serverUrl.replaceAll('/', '%2F');
170+
const serverParams = `${newServer}%2F&${process.env.APP_ID}&contracts`;
171+
172+
for (let i = 0; i < contact.length; i++) {
173+
try {
174+
const imgPng = 'https://qikinnovation.ams3.digitaloceanspaces.com/logo.png';
175+
let url = `${process.env.SERVER_URL}/functions/sendmailv3/`;
176+
const headers = {
177+
'Content-Type': 'application/json',
178+
'X-Parse-Application-Id': process.env.APP_ID,
179+
'X-Parse-Master-Key': process.env.MASTER_KEY,
180+
};
181+
182+
const objectId = contact[i].contactPtr.objectId;
183+
184+
const hostUrl = baseUrl.origin + '/loadmf/signmicroapp';
185+
let signPdf = `${hostUrl}/login/${res.id}/${contact[i].email}/${objectId}/${serverParams}`;
186+
const openSignUrl = 'https://www.opensignlabs.com/contact-us';
187+
const orgName = parseExtUser.Company ? parseExtUser.Company : '';
188+
const themeBGcolor = '#47a3ad';
189+
let params = {
190+
recipient: contact[i].email,
191+
subject: `${parseExtUser.Name} has requested you to sign ${parseExtUser.Name}`,
192+
from: sender,
193+
html:
194+
"<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=" +
195+
imgPng +
196+
" height='50' style='padding: 20px,width:170px,height:40px' /></div> <div style=' padding: 2px;font-family: system-ui;background-color:" +
197+
themeBGcolor +
198+
";'><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;'> " +
199+
parseExtUser.Name +
200+
' has requested you to review and sign <strong> ' +
201+
name +
202+
"</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'>" +
203+
sender +
204+
"</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'> " +
205+
orgName +
206+
"</td></tr> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expire on</td><td> </td> <td style='color:#626363;font-weight:bold'>" +
207+
localExpireDate +
208+
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a href=" +
209+
signPdf +
210+
"> <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 " +
211+
sender +
212+
' directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href= ' +
213+
openSignUrl +
214+
' target=_blank>here</a>.</p> </div></div></body> </html>',
215+
};
216+
sendMail = await axios.post(url, params, { headers: headers });
217+
} catch (error) {
218+
console.log('error', error);
219+
}
220+
}
221+
222+
if (sendMail.data.result.status === 'success') {
223+
const user = contact.find(x => x.email === parseExtUser.Email);
224+
if (user && user.email) {
225+
return response.json({
226+
objectId: res.id,
227+
url: `${baseUrl.origin}/loadmf/signmicroapp/login/${res.id}/${user.email}/${user.contactPtr.objectId}/${serverParams}`,
228+
message: 'Document sent successfully!',
229+
});
230+
} else {
231+
return response.json({
232+
objectId: res.id,
233+
message: 'Document sent successfully!',
234+
});
235+
}
236+
}
237+
} else {
238+
return response.status(400).json({ error: 'Please provide signers!' });
239+
}
240+
} else {
241+
return response.status(405).json({ error: 'Invalid API Token!' });
242+
}
243+
} catch (err) {
244+
console.log('err ', err);
245+
return response.status(400).json({ error: 'Something went wrong!' });
246+
}
247+
}

0 commit comments

Comments
 (0)