import SignPDF from './SignPDF.cjs'; import fs from 'node:fs'; import axios from 'axios'; import FormData from 'form-data'; import plainplaceholder from './customSignPdf/plainplaceholder.js'; import { plainAddPlaceholder } from 'node-signpdf/dist/helpers/index.js'; const serverUrl = process.env.SERVER_URL; const APPID = process.env.APP_ID; const masterKEY = process.env.MASTER_KEY; // `uploadFile` is used to upload signed pdf on aws s3 and get file url async function uploadFile(pdfFile) { try { const formData = new FormData(); formData.append('file', fs.createReadStream(pdfFile)); const headers = { 'content-type': 'multipart/form-data', 'X-Parse-Application-Id': process.env.APP_ID, }; const serverURL = process.env.SERVER_URL; // const split = serverURL.split('/'); // const desiredUrl = serverURL.includes('/app') // ? serverURL.replace('/app', '') // : serverURL.replace('/' + split[3], ''); const desiredUrl = serverURL.slice(0, -4); const url = desiredUrl + '/file_upload'; //process.env.SERVER_URL const res = await axios.post(url, formData, { headers: headers }); // console.log("res ", res.data); return res.data; } catch (err) { console.log('err ', err); // `fs.unlinkSync` is used to remove exported signed pdf file from exports folder fs.unlinkSync(pdfFile); } } async function updateDoc(docId, url, userId, ipAddress, data, className) { try { const UserPtr = { __type: 'Pointer', className: className, objectId: userId, }; const obj = { UserPtr: UserPtr, SignedUrl: url, Activity: 'Signed', ipAddress: ipAddress, }; let updateAuditTrail; if (data.AuditTrail && data.AuditTrail.length > 0) { updateAuditTrail = [...data.AuditTrail, obj]; } else { updateAuditTrail = [obj]; } const auditTrail = updateAuditTrail.filter(x => x.Activity === 'Signed'); let isCompleted = false; if (data.Signers && data.Signers.length > 0) { if (auditTrail.length === data.Signers.length) { isCompleted = true; } } else { isCompleted = true; } const body = { SignedUrl: url, AuditTrail: updateAuditTrail, IsCompleted: isCompleted, }; const signedRes = await axios.put(serverUrl + '/classes/contracts_Document/' + docId, body, { headers: { 'Content-Type': 'application/json', 'X-Parse-Application-Id': APPID, 'X-Parse-Master-Key': masterKEY, }, }); return { isCompleted: isCompleted, message: 'success' }; } catch (err) { console.log('update doc err ', err); return 'err'; } } // `sendMail` is used to send copy signed mail async function sendMail(obj) { const url = obj.url; const sender = obj.sender; const pdfName = obj.pdfName; const mailLogo = 'https://qikinnovation.ams3.digitaloceanspaces.com/logo.png'; const recipient = obj.receiver; const subject = `${sender.Name} has signed the doc - ${pdfName}`; const params = { url: url, from: 'Open sign', recipient: recipient, subject: subject, pdfName: pdfName, html: "

Document Copy

A copy of the document " + pdfName + ' Standard is attached to this email. Kindly download the document from the attachment.

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

', }; const res = await axios.post(serverUrl + '/functions/sendmailv3', params, { headers: { 'Content-Type': 'application/json', 'X-Parse-Application-Id': APPID, 'X-Parse-Master-Key': masterKEY, }, }); // console.log('Res ', res); } // `sendMail` is used to send copy signed mail async function sendCompletedMail(obj) { const url = obj.url; const sender = obj.sender; const pdfName = obj.pdfName; const mailLogo = 'https://qikinnovation.ams3.digitaloceanspaces.com/logo.png'; const recipient = obj.receiver; const subject = `Document ${pdfName} has beeen signed by all parties`; const params = { url: url, from: 'Open sign', recipient: recipient, subject: subject, pdfName: pdfName, html: "

Document sign successfully

All parties have successfully signed the document" + pdfName + '. Kindly download the document from the attachment.

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

', }; const res = await axios.post(serverUrl + '/functions/sendmailv3', params, { headers: { 'Content-Type': 'application/json', 'X-Parse-Application-Id': APPID, 'X-Parse-Master-Key': masterKEY, }, }); // console.log('Res ', res); } /** * * @param sign base64 sign of user * @param docId Id of Document in which user is signing * @param pdfFile base64 of pdfFile which you want sign * @returns if success {status, data} else {status, message} */ async function PDF(req, res) { try { const sign = req.params.sign; const data = '?include=ExtUserPtr'; const docId = req.params.docId; // below bode is used to get info of docId const resDoc = await axios.get(serverUrl + '/classes/contracts_Document/' + docId + data, { headers: { 'Content-Type': 'application/json', 'X-Parse-Application-Id': APPID, 'X-Parse-Session-Token': req.headers['sessiontoken'], }, }); // to get user from session token const user = await axios.get(serverUrl + '/users/me', { headers: { 'X-Parse-Application-Id': APPID, 'X-Parse-Session-Token': req.headers['sessiontoken'], }, }); if (user.data && user.data.objectId) { const userPtr = JSON.stringify({ UserId: { __type: 'Pointer', className: '_User', objectId: user.data.objectId, }, }); let signUser; let className; // to get contracts_Users Id from _User ptr const contractUser = await axios.get( serverUrl + '/classes/contracts_Users?where=' + userPtr, { headers: { 'X-Parse-Application-Id': APPID, 'X-Parse-Session-Token': req.headers['sessiontoken'], }, } ); if (contractUser.data && contractUser.data.results.length > 0) { signUser = contractUser; className = 'contracts_Users'; } else { // to get contracts_Contactbook Id from _User ptr signUser = await axios.get(serverUrl + '/classes/contracts_Contactbook?where=' + userPtr, { headers: { 'X-Parse-Application-Id': APPID, 'X-Parse-Session-Token': req.headers['sessiontoken'], }, }); className = 'contracts_Contactbook'; } // console.log("signUser ", signUser.data.results[0].objectId); // console.log("resDoc ", resDoc); const username = signUser.data.results[0].Name; // resDoc.data.ExtUserPtr.Name; const userEmail = signUser.data.results[0].Email; //resDoc.data.ExtUserPtr.Email; if (req.params.pdfFile) { // `PdfBuffer` used to create buffer from pdf file let PdfBuffer = Buffer.from(req.params.pdfFile, 'base64'); // let PdfBuffer = fs.readFileSync("exports/exported_file_688.pdf"); // let PdfBuffer = fs.readFileSync("exports/simple.pdf"); // `P12Buffer` used to create buffer from p12 certificate const P12Buffer = fs.readFileSync(`pdfFile/emudhra-test-class2.pfx`); if (sign) { // `plainAddPlaceholder` is used to add code of digitial sign in pdf file PdfBuffer = plainplaceholder({ pdfBuffer: PdfBuffer, reason: 'Digitally signed by Open sign for ' + username + ' <' + userEmail + '>', location: 'test location', signatureLength: 10000, sign: sign, }); } else { // `plainAddPlaceholder` is used to add code of only digitial sign without widget PdfBuffer = plainAddPlaceholder({ pdfBuffer: PdfBuffer, reason: 'Digitally signed by Open sign for ' + username + ' <' + userEmail + '>', location: 'test location', signatureLength: 10000, }); } // console.log("PdfBuffer ", PdfBuffer); // const clientIP = req.headers["x-real-ip"]; // console.log("req.ip", clientIP); //`new signPDF` create new instance of pdfBuffer and p12Buffer const OBJ = new SignPDF(PdfBuffer, P12Buffer); // `signedDocs` is used to signpdf digitally const signedDocs = await OBJ.signPDF(); const randomNumber = Math.floor(Math.random() * 5000); const pdfName = `./exports/exported_file_${randomNumber}.pdf`; //`saveUrl` is used to save signed pdf in exports folder const saveUrl = fs.writeFileSync(pdfName, signedDocs); // `uploadFile` is used to upload pdf to aws s3 and get it's url const data = await uploadFile(pdfName); if (data && data.imageUrl) { // `axios` is used to update signed pdf url in contracts_Document classes for given DocId const res = await updateDoc( req.params.docId, //docId data.imageUrl, // url signUser.data.results[0].objectId, // userID req.headers['x-real-ip'], // client ipAddress, resDoc.data, // auditTrail, signers, etc data className ); const obj = { url: data.imageUrl, sender: { Mail: resDoc.data.ExtUserPtr.Email, Name: resDoc.data.ExtUserPtr.Name, }, pdfName: resDoc.data.Name, receiver: userEmail, }; sendMail(obj); // console.log("res ", res); if (res && res.isCompleted) { // console.log("res.IsCompleted ", res.isCompleted); const mailObj = { url: data.imageUrl, sender: { Mail: resDoc.data.ExtUserPtr.Email, Name: 'Open sign', }, pdfName: resDoc.data.Name, receiver: resDoc.data.ExtUserPtr.Email, }; sendCompletedMail(mailObj); } // `fs.unlinkSync` is used to remove exported signed pdf file from exports folder fs.unlinkSync(pdfName); console.log(`New Signed PDF created called: ${pdfName}`); if (res.message === 'success') { return { status: 'success', data: data.imageUrl }; } else { return { status: 'error', message: 'please provide required parameters!', }; } } } else { return { status: 'error', message: 'pdf file not present!' }; } } else { return { status: 'error', message: 'this user not allowed!' }; } } catch (err) { console.log('Err ', err); if (err.code === 'ERR_BAD_REQUEST') { return { status: 'error', message: 'Invalid session token!', }; } else { return { status: 'error', message: 'Encrypted files are currently not supported!', }; } } } export default PDF;