mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-08-17 21:25:54 +02:00
330 lines
12 KiB
JavaScript
330 lines
12 KiB
JavaScript
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:
|
|
"<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-color:white;'> <div><img src=" +
|
|
mailLogo +
|
|
" height='50' style='padding:20px'/> </div><div style='padding:2px;font-family:system-ui; background-color: #47a3ad;'> <p style='font-size:20px;font-weight:400;color:white;padding-left:20px',> Document Copy</p></div><div><p style='padding:20px;font-family:system-ui;font-size:14px'>A copy of the document " +
|
|
pdfName +
|
|
' Standard is attached to this email. Kindly download the document from the attachment.</p></div> </div><div><p>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.</p></div></div></body></html>',
|
|
};
|
|
|
|
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:
|
|
"<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-color:white;'> <div><img src=" +
|
|
mailLogo +
|
|
" height='50' style='padding:20px'/> </div><div style='padding:2px;font-family:system-ui; background-color: #47a3ad;'> <p style='font-size:20px;font-weight:400;color:white;padding-left:20px',> Document sign successfully</p></div><div><p style='padding:20px;font-family:system-ui;font-size:14px'>All parties have successfully signed the document" +
|
|
pdfName +
|
|
'. Kindly download the document from the attachment.</p></div> </div><div><p>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.</p></div></div></body></html>',
|
|
};
|
|
|
|
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 pfxFile = process.env.PFX_BASE64;
|
|
// const P12Buffer = fs.readFileSync();
|
|
const P12Buffer = Buffer.from(pfxFile, 'base64');
|
|
|
|
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);
|
|
|
|
//`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;
|