From 98c8af5b9ced5680256da080c1c9f194193f3530 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs Date: Thu, 1 Feb 2024 20:26:36 +0530 Subject: [PATCH] feat: create document and template with coordinate --- apps/OpenSignServer/Utils.js | 15 ++ .../cloud/customRoute/v1/apiV1.js | 15 +- .../v1/routes/createDocumentwithCoordinate.js | 247 ++++++++++++++++++ .../v1/routes/createTemplatewithCoordinate.js | 183 +++++++++++++ .../cloud/parsefunction/pdf/PDF.min.js | 2 +- 5 files changed, 457 insertions(+), 5 deletions(-) create mode 100644 apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js create mode 100644 apps/OpenSignServer/cloud/customRoute/v1/routes/createTemplatewithCoordinate.js diff --git a/apps/OpenSignServer/Utils.js b/apps/OpenSignServer/Utils.js index 884e8fdad..98c1a4366 100644 --- a/apps/OpenSignServer/Utils.js +++ b/apps/OpenSignServer/Utils.js @@ -2,3 +2,18 @@ export function customAPIurl() { const url = new URL(process.env.SERVER_URL); return url.pathname === '/api/app' ? url.origin + '/api' : url.origin; } + +export const color = [ + '#93a3db', + '#e6c3db', + '#c0e3bc', + '#bce3db', + '#b8ccdb', + '#ceb8db', + '#ffccff', + '#99ffcc', + '#cc99ff', + '#ffcc99', + '#66ccff', + '#ffffcc', +]; diff --git a/apps/OpenSignServer/cloud/customRoute/v1/apiV1.js b/apps/OpenSignServer/cloud/customRoute/v1/apiV1.js index b4a5449b3..cf671c8d9 100644 --- a/apps/OpenSignServer/cloud/customRoute/v1/apiV1.js +++ b/apps/OpenSignServer/cloud/customRoute/v1/apiV1.js @@ -24,7 +24,8 @@ import createDocumentWithTemplate from './routes/CreateDocumentWithTemplate.js'; import saveWebhook from './routes/saveWebhook.js'; import deleteWebhook from './routes/deleteWebhook.js'; import getWebhook from './routes/getWebhook.js'; - +import createDocumentwithCoordinate from './routes/createDocumentwithCoordinate.js'; +import createTemplatewithCoordinate from './routes/createTemplatewithCoordinate.js'; dotenv.config(); const storage = multer.memoryStorage(); const upload = multer({ storage: storage }); @@ -51,8 +52,11 @@ app.get('/contactlist', getContactList); // create Document app.post('/createdocumentwithbinary', upload.array('file', 1), createDocument); -// create Document with base64 -app.post('/createdocument', createDocument); +// create Document with co-ordinate +app.post('/createdocument', upload.array('file', 1), createDocumentwithCoordinate); + +// create Document with base64 without placeholder +app.post('/draftdocument', createDocument); // create Document with templateId app.post('/createdocument/:template_id', createDocumentWithTemplate); @@ -69,8 +73,11 @@ app.delete('/document/:document_id', deleteDocument); // get all types of documents on the basis of doctype app.get('/documentlist/:doctype', getDocumentList); +// create Template with co-ordinate +app.post('/createtemplate', upload.array('file', 1), createTemplatewithCoordinate); + // create Template -app.post('/createtemplate', createTemplate); +app.post('/drafttemplate', createTemplate); // create Template with binary app.post('/createtemplatewithbinary', upload.array('file', 1), createTemplate); diff --git a/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js b/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js new file mode 100644 index 000000000..8c1c4e68d --- /dev/null +++ b/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js @@ -0,0 +1,247 @@ +import axios from 'axios'; +import { color, customAPIurl } from '../../../../Utils.js'; + +const randomId = () => Math.floor(1000 + Math.random() * 9000); +export default async function createDocumentwithCoordinate(request, response) { + const name = request.body.title; + const note = request.body.note; + const description = request.body.description; + const signers = request.body.signers; + const folderId = request.body.folderId; + const base64File = request.body.file; + const fileData = request.files?.[0] ? request.files[0].buffer : null; + // console.log('fileData ', fileData); + const protocol = customAPIurl(); + const baseUrl = new URL(process.env.SERVER_URL); + + try { + const reqToken = request.headers['x-api-token']; + if (!reqToken) { + return response.status(400).json({ error: 'Please Provide API Token' }); + } + const tokenQuery = new Parse.Query('appToken'); + tokenQuery.equalTo('token', reqToken); + const token = await tokenQuery.first({ useMasterKey: true }); + if (token !== undefined) { + // Valid Token then proceed request + if (signers && signers.length > 0) { + const userPtr = token.get('userId'); + let fileUrl; + if (request.files?.[0]) { + const file = new Parse.File(request.files?.[0]?.originalname, { + base64: fileData?.toString('base64'), + }); + await file.save({ useMasterKey: true }); + fileUrl = file.url(); + } else { + const file = new Parse.File(`${name}.pdf`, { + base64: base64File, + }); + await file.save({ useMasterKey: true }); + fileUrl = file.url(); + } + const contractsUser = new Parse.Query('contracts_Users'); + contractsUser.equalTo('UserId', userPtr); + const extUser = await contractsUser.first({ useMasterKey: true }); + const parseExtUser = JSON.parse(JSON.stringify(extUser)); + + const extUserPtr = { + __type: 'Pointer', + className: 'contracts_Users', + objectId: extUser.id, + }; + + const object = new Parse.Object('contracts_Document'); + object.set('Name', name); + + if (note) { + object.set('Note', note); + } + if (description) { + object.set('Description', description); + } + object.set('URL', fileUrl); + object.set('CreatedBy', userPtr); + object.set('ExtUserPtr', extUserPtr); + let contact = []; + if (signers && signers.length > 0) { + let parseSigners; + if (base64File) { + parseSigners = signers; + } else { + parseSigners = JSON.parse(signers); + } + let createContactUrl = protocol + '/v1/createcontact'; + + for (const [index, element] of parseSigners.entries()) { + const body = { + name: element?.name || '', + email: element?.email || '', + phone: element?.phone || '', + }; + try { + const res = await axios.post(createContactUrl, body, { + headers: { 'Content-Type': 'application/json', 'x-api-token': reqToken }, + }); + // console.log('res ', res.data); + const contactPtr = { + __type: 'Pointer', + className: 'contracts_Contactbook', + objectId: res.data?.objectId, + }; + const newObj = { ...element, contactPtr: contactPtr, index: index }; + contact.push(newObj); + } catch (err) { + // console.log('err ', err.response); + if (err?.response?.data?.objectId) { + const contactPtr = { + __type: 'Pointer', + className: 'contracts_Contactbook', + objectId: err.response.data?.objectId, + }; + const newObj = { ...element, contactPtr: contactPtr, index: index }; + contact.push(newObj); + } + } + } + object.set( + 'Signers', + contact?.map(x => x.contactPtr) + ); + let updatePlaceholders = contact.map(x => { + return { + signerObjId: x?.contactPtr?.objectId, + signerPtr: x?.contactPtr, + Role: x.role, + Id: randomId(), + blockColor: color[x?.index], + placeHolder: x.widgets.map((widget, i) => ({ + pageNumber: widget.page, + pos: [ + { + xPosition: widget.x, + yPosition: widget.y, + isStamp: widget.type === 'stamp' || widget.type === 'image' ? true : false, + key: randomId(), + isDrag: false, + firstXPos: widget.x, + firstYPos: widget.y, + yBottom: 0, + scale: 1, + isMobile: false, + zIndex: i + 1, + type: widget.type, + widgetValue: '', + Width: widget.w, + Height: widget.h, + }, + ], + })), + }; + }); + object.set('Placeholders', updatePlaceholders); + } + if (folderId) { + object.set('Folder', { + __type: 'Pointer', + className: 'contracts_Document', + objectId: folderId, + }); + } + const newACL = new Parse.ACL(); + newACL.setPublicReadAccess(false); + newACL.setPublicWriteAccess(false); + newACL.setReadAccess(userPtr.id, true); + newACL.setWriteAccess(userPtr.id, true); + object.setACL(newACL); + const res = await object.save(null, { useMasterKey: true }); + + const newDate = new Date(); + newDate.setDate(newDate.getDate() + 15); + const localExpireDate = newDate.toLocaleDateString('en-US', { + day: 'numeric', + month: 'long', + year: 'numeric', + }); + let sender = parseExtUser.Email; + let sendMail; + const serverUrl = process.env.SERVER_URL; + const newServer = serverUrl.replaceAll('/', '%2F'); + const serverParams = `${newServer}%2F&${process.env.APP_ID}&contracts`; + + for (let i = 0; i < contact.length; i++) { + 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[i].contactPtr.objectId; + + const hostUrl = baseUrl.origin + '/loadmf/signmicroapp'; + let signPdf = `${hostUrl}/login/${res.id}/${contact[i].email}/${objectId}/${serverParams}`; + const openSignUrl = 'https://www.opensignlabs.com/contact-us'; + const orgName = parseExtUser.Company ? parseExtUser.Company : ''; + const themeBGcolor = '#47a3ad'; + let params = { + recipient: contact[i].email, + subject: `${parseExtUser.Name} has requested you to sign ${parseExtUser.Name}`, + from: sender, + html: + "

Digital Signature Request

" + + parseExtUser.Name + + ' has requested you to review and sign ' + + name + + ".

Sender " + + sender + + "
Organization " + + orgName + + "
Expire 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.

', + }; + sendMail = await axios.post(url, params, { headers: headers }); + } catch (error) { + console.log('error', error); + } + } + + if (sendMail.data.result.status === 'success') { + const user = contact.find(x => x.email === parseExtUser.Email); + if (user && user.email) { + return response.json({ + objectId: res.id, + url: `${baseUrl.origin}/loadmf/signmicroapp/login/${res.id}/${user.email}/${user.contactPtr.objectId}/${serverParams}`, + message: 'Document sent successfully!', + }); + } else { + return response.json({ + objectId: res.id, + message: 'Document sent successfully!', + }); + } + } + } else { + return response.status(400).json({ error: 'Please provide signers!' }); + } + } else { + return response.status(405).json({ error: 'Invalid API Token!' }); + } + } catch (err) { + console.log('err ', err); + return response.status(400).json({ error: 'Something went wrong!' }); + } +} diff --git a/apps/OpenSignServer/cloud/customRoute/v1/routes/createTemplatewithCoordinate.js b/apps/OpenSignServer/cloud/customRoute/v1/routes/createTemplatewithCoordinate.js new file mode 100644 index 000000000..88230c6d2 --- /dev/null +++ b/apps/OpenSignServer/cloud/customRoute/v1/routes/createTemplatewithCoordinate.js @@ -0,0 +1,183 @@ +import axios from 'axios'; +import { color, customAPIurl } from '../../../../Utils.js'; + +const randomId = () => Math.floor(1000 + Math.random() * 9000); +export default async function createTemplatewithCoordinate(request, response) { + const name = request.body.title; + const note = request.body.note; + const description = request.body.description; + const signers = request.body.signers; + const folderId = request.body.folderId; + const base64File = request.body.file; + const fileData = request.files?.[0] ? request.files[0].buffer : null; + // console.log('fileData ', fileData); + const protocol = customAPIurl(); + const baseUrl = new URL(process.env.SERVER_URL); + + try { + const reqToken = request.headers['x-api-token']; + if (!reqToken) { + return response.status(400).json({ error: 'Please Provide API Token' }); + } + const tokenQuery = new Parse.Query('appToken'); + tokenQuery.equalTo('token', reqToken); + const token = await tokenQuery.first({ useMasterKey: true }); + if (token !== undefined) { + // Valid Token then proceed request + if (signers && signers.length > 0) { + const userPtr = token.get('userId'); + let fileUrl; + if (request.files?.[0]) { + const file = new Parse.File(request.files?.[0]?.originalname, { + base64: fileData?.toString('base64'), + }); + await file.save({ useMasterKey: true }); + fileUrl = file.url(); + } else { + const file = new Parse.File(`${name}.pdf`, { + base64: base64File, + }); + await file.save({ useMasterKey: true }); + fileUrl = file.url(); + } + const contractsUser = new Parse.Query('contracts_Users'); + contractsUser.equalTo('UserId', userPtr); + const extUser = await contractsUser.first({ useMasterKey: true }); + const parseExtUser = JSON.parse(JSON.stringify(extUser)); + + const extUserPtr = { + __type: 'Pointer', + className: 'contracts_Users', + objectId: extUser.id, + }; + + const object = new Parse.Object('contracts_Template'); + object.set('Name', name); + + if (note) { + object.set('Note', note); + } + if (description) { + object.set('Description', description); + } + object.set('URL', fileUrl); + object.set('CreatedBy', userPtr); + object.set('ExtUserPtr', extUserPtr); + let contact = []; + if (signers && signers.length > 0) { + let parseSigners; + if (base64File) { + parseSigners = signers; + } else { + parseSigners = JSON.parse(signers); + } + let createContactUrl = protocol + '/v1/createcontact'; + + for (const [index, element] of parseSigners.entries()) { + if (element?.name && element.phone && element?.email) { + const body = { + name: element?.name || '', + email: element?.email || '', + phone: element?.phone || '', + }; + try { + const res = await axios.post(createContactUrl, body, { + headers: { 'Content-Type': 'application/json', 'x-api-token': reqToken }, + }); + // console.log('res ', res.data); + const contactPtr = { + __type: 'Pointer', + className: 'contracts_Contactbook', + objectId: res.data?.objectId, + }; + const newObj = { ...element, contactPtr: contactPtr, index: index }; + contact.push(newObj); + } catch (err) { + // console.log('err ', err.response); + if (err?.response?.data?.objectId) { + const contactPtr = { + __type: 'Pointer', + className: 'contracts_Contactbook', + objectId: err.response.data?.objectId, + }; + const newObj = { ...element, contactPtr: contactPtr, index: index }; + contact.push(newObj); + } + } + } else { + const newObj = { ...element, contactPtr: {}, index: index }; + contact.push(newObj); + } + } + const updatedSigners = contact?.filter(x => x.contactPtr && x.contactPtr.objectId); + if (updatedSigners && updatedSigners.length > 0) { + object.set( + 'Signers', + updatedSigners?.map(x => x.contactPtr) + ); + } + let updatePlaceholders = contact.map(x => { + return { + signerObjId: x?.contactPtr?.objectId || '', + signerPtr: x?.contactPtr || {}, + Role: x.role, + Id: randomId(), + blockColor: color[x?.index], + placeHolder: x.widgets.map((widget, i) => ({ + pageNumber: widget.page, + pos: [ + { + xPosition: widget.x, + yPosition: widget.y, + isStamp: widget.type === 'stamp' || widget.type === 'image' ? true : false, + key: randomId(), + isDrag: false, + firstXPos: widget.x, + firstYPos: widget.y, + yBottom: 0, + scale: 1, + isMobile: false, + zIndex: i + 1, + type: widget.type, + widgetValue: '', + Width: widget.w, + Height: widget.h, + }, + ], + })), + }; + }); + // console.log('updatePLacholders', updatePlaceholders); + + object.set('Placeholders', updatePlaceholders); + } + if (folderId) { + object.set('Folder', { + __type: 'Pointer', + className: 'contracts_Template', + objectId: folderId, + }); + } + const newACL = new Parse.ACL(); + newACL.setPublicReadAccess(false); + newACL.setPublicWriteAccess(false); + newACL.setReadAccess(userPtr.id, true); + newACL.setWriteAccess(userPtr.id, true); + object.setACL(newACL); + const res = await object.save(null, { useMasterKey: true }); + + return response.json({ + objectId: res.id, + url: protocol + '/load/signmicroapp/template/' + res.id, + }); + } else { + return response.status(400).json({ error: 'Please provide signers!' }); + } + } else { + return response.status(405).json({ error: 'Invalid API Token!' }); + } + } catch (err) { + console.log('err ', err); + return response.status(400).json({ error: 'Something went wrong!' }); + } +} diff --git a/apps/OpenSignServer/cloud/parsefunction/pdf/PDF.min.js b/apps/OpenSignServer/cloud/parsefunction/pdf/PDF.min.js index 9922362e5..7b2f197d4 100644 --- a/apps/OpenSignServer/cloud/parsefunction/pdf/PDF.min.js +++ b/apps/OpenSignServer/cloud/parsefunction/pdf/PDF.min.js @@ -109,7 +109,7 @@ async function sendDoctoWebhook(t, e) { Phone: t?.data?.ExtUserPtr?.Phone, }, ], - Completed: !0, + Event: 'Completed', CompletedAt: new Date(), CreatedAt: t?.data?.createdAt, }),