Merge pull request #658 from OpenSignLabs/signPdf

This commit is contained in:
Andrew
2024-05-03 15:51:57 +05:30
committed by GitHub
12 changed files with 1081 additions and 373 deletions
+122 -45
View File
@@ -541,54 +541,88 @@ export const signPdfFun = async (
setIsAlert,
objectId,
isSubscribed,
activeMailAdapter
activeMailAdapter,
xyPosition
) => {
let singleSign,
isCustomCompletionMail = false;
//get tenant details
const tenantDetails = await getTenantDetails(objectId);
if (tenantDetails && tenantDetails === "user does not exist!") {
alert("User does not exist");
} else {
if (
tenantDetails?.CompletionBody &&
tenantDetails?.CompletionSubject &&
(!isEnableSubscription || isSubscribed)
) {
isCustomCompletionMail = true;
}
}
singleSign = {
mailProvider: activeMailAdapter,
pdfFile: base64Url,
docId: documentId,
userId: signerObjectId,
isCustomCompletionMail: isCustomCompletionMail
};
const response = await axios
.post(`${localStorage.getItem("baseUrl")}functions/signPdf`, singleSign, {
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
sessionToken: localStorage.getItem("accesstoken")
try {
//get tenant details
const tenantDetails = await getTenantDetails(objectId);
if (tenantDetails && tenantDetails === "user does not exist!") {
alert("User does not exist");
} else {
if (
tenantDetails?.CompletionBody &&
tenantDetails?.CompletionSubject &&
(!isEnableSubscription || isSubscribed)
) {
isCustomCompletionMail = true;
}
})
.then((Listdata) => {
const json = Listdata.data;
const res = json.result;
return res;
})
.catch((err) => {
console.log("Err ", err);
setIsAlert({
isShow: true,
alertMessage: "something went wrong"
});
});
}
return response;
let getSignature;
for (let item of xyPosition) {
const typeExist = item.pos.some((data) => data?.type);
if (typeExist) {
getSignature = item.pos.filter((data) => data?.type === "signature");
} else {
getSignature = item.pos.filter((data) => !data.isStamp);
}
}
let base64Sign = getSignature[0].SignUrl;
//check https type signature (default signature exist) then convert in base64
const isUrl = base64Sign.includes("https");
if (isUrl) {
try {
base64Sign = await fetchImageBase64(base64Sign);
} catch (e) {
console.log("error", e);
}
}
//change image width and height to 104/44 in png base64
const getNewse64 = await changeImageWH(base64Sign);
//remove suffiix of base64
const suffixbase64 = getNewse64 && getNewse64.split(",").pop();
singleSign = {
mailProvider: activeMailAdapter,
pdfFile: base64Url,
docId: documentId,
userId: signerObjectId,
isCustomCompletionMail: isCustomCompletionMail,
signature: suffixbase64
};
const response = await axios
.post(`${localStorage.getItem("baseUrl")}functions/signPdf`, singleSign, {
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
// sessionToken: localStorage.getItem("accesstoken")
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
}
})
.then((Listdata) => {
const json = Listdata.data;
const res = json.result;
return res;
})
.catch((err) => {
console.log("Err ", err);
setIsAlert({
isShow: true,
alertMessage: "something went wrong"
});
});
return response;
} catch (e) {
setIsAlert({
isShow: true,
alertMessage: "something went wrong"
});
}
};
export const randomId = () => {
@@ -1125,7 +1159,6 @@ export const onImageSelect = (event, setImgWH, setImage) => {
const imageType = event.target.files[0].type;
const reader = new FileReader();
reader.readAsDataURL(event.target.files[0]);
reader.onloadend = function (e) {
let width, height;
const image = new Image();
@@ -1153,6 +1186,50 @@ export const onImageSelect = (event, setImgWH, setImage) => {
};
};
//convert https url to base64
export const fetchImageBase64 = async (imageUrl) => {
try {
const response = await fetch(imageUrl);
const blob = await response.blob();
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(blob);
reader.onloadend = () => {
const base64data = reader.result;
resolve(base64data);
};
reader.onerror = (error) => {
reject(error);
};
});
} catch (error) {
throw new Error("Error converting URL to base64:", error);
}
};
//function for select image and upload image
export const changeImageWH = async (base64Image) => {
const newWidth = 100;
const newHeight = 40;
return new Promise((resolve, reject) => {
const img = new Image();
img.src = base64Image;
img.onload = async () => {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
canvas.width = newWidth;
canvas.height = newHeight;
ctx.imageSmoothingEnabled = false;
ctx.drawImage(img, 0, 0, newWidth, newHeight);
const resizedBase64 = canvas.toDataURL("image/png", 1);
resolve(resizedBase64);
};
img.onerror = (error) => {
reject(error);
};
});
};
//function for embed multiple signature using pdf-lib
export const multiSignEmbed = async (
pngUrl,
@@ -1267,8 +1344,8 @@ export const multiSignEmbed = async (
position.type === radioButtonWidget
? 10
: position.type === "checkbox"
? 10
: newUpdateHeight;
? 10
: newUpdateHeight;
const newHeight = ind ? (ind > 0 ? widgetHeight : 0) : widgetHeight;
if (signyourself) {
+115 -16
View File
@@ -11,8 +11,10 @@ import SignersInput from "../components/shared/fields/SignersInput";
import Title from "../components/Title";
import PageNotFound from "./PageNotFound";
import { SaveFileSize } from "../constant/saveFileSize";
import { getFileName } from "../constant/Utils";
import { getFileName, toDataUrl } from "../constant/Utils";
import { PDFDocument } from "pdf-lib";
import axios from "axios";
import { isEnableSubscription } from "../constant/const";
// `Form` render all type of Form on this basis of their provided in path
function Form() {
@@ -32,6 +34,7 @@ function Form() {
const Forms = (props) => {
const maxFileSize = 20;
const abortController = new AbortController();
const navigate = useNavigate();
const [signers, setSigners] = useState([]);
const [folder, setFolder] = useState({ ObjectId: "", Name: "" });
@@ -54,6 +57,7 @@ const Forms = (props) => {
};
useEffect(() => {
handleReset();
return () => abortController.abort();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.title]);
@@ -86,22 +90,112 @@ const Forms = (props) => {
e.target.value = "";
return;
} else {
try {
const res = await getFileAsArrayBuffer(files[0]);
const pdfBytes = await PDFDocument.load(res);
console.log("pdfbytes ", pdfBytes);
handleFileUpload(files[0]);
} catch (err) {
alert(`Currently encrypted pdf files are not supported.`);
setFileUpload("");
e.target.value = "";
console.log("err ", err);
if (files?.[0]?.type === "application/pdf") {
try {
await Parse.Cloud.run("encryptedpdf", {
email: Parse.User.current().getEmail()
});
const res = await getFileAsArrayBuffer(files[0]);
await PDFDocument.load(res);
handleFileUpload(files[0]);
} catch (err) {
console.log("err in sending posthog encryptedpdf", err);
alert(`Currently encrypted pdf files are not supported.`);
setFileUpload("");
e.target.value = "";
console.log("err ", err);
try {
await Parse.Cloud.run("encryptedpdf", {
email: Parse.User.current().getEmail()
});
} catch (err) {
console.log("err in sending posthog encryptedpdf", err);
}
}
} else {
if (isEnableSubscription) {
const isImage = files?.[0]?.type.includes("image/");
if (isImage) {
const image = await toDataUrl(files[0]);
const pdfDoc = await PDFDocument.create();
let embedImg;
if (files?.[0]?.type === "image/png") {
embedImg = await pdfDoc.embedPng(image);
} else {
embedImg = await pdfDoc.embedJpg(image);
}
// Get image dimensions
const imageWidth = embedImg.width;
const imageHeight = embedImg.height;
const page = pdfDoc.addPage([imageWidth, imageHeight]);
page.drawImage(embedImg, {
x: 0,
y: 0,
width: imageWidth,
height: imageHeight
});
const getFile = await pdfDoc.save({
useObjectStreams: false
});
setfileload(true);
const fileName = files[0].name;
const size = files[0].size;
const name = sanitizeFileName(fileName);
const pdfName = `${name?.split(".")[0]}.pdf`;
const parseFile = new Parse.File(
pdfName,
[...getFile],
"application/pdf"
);
try {
const response = await parseFile.save({
progress: (progressValue, loaded, total, { type }) => {
if (type === "upload" && progressValue !== null) {
const percentCompleted = Math.round(
(loaded * 100) / total
);
setpercentage(percentCompleted);
}
}
});
// The response object will contain information about the uploaded file
// You can access the URL of the uploaded file using response.url()
setFileUpload(response.url());
setfileload(false);
if (response.url()) {
const tenantId = localStorage.getItem("TenantId");
SaveFileSize(size, response.url(), tenantId);
return response.url();
}
} catch (error) {
e.target.value = "";
setfileload(false);
setpercentage(0);
console.error("Error uploading file:", error);
}
} else {
try {
setfileload(true);
const url = "http://tools.opensignlabs.com/docxtopdf";
let formData = new FormData();
formData.append("file", files[0]);
const config = {
headers: {
"content-type": "multipart/form-data",
sessiontoken: Parse.User.current().getSessionToken()
},
signal: abortController.signal
};
const res = await axios.post(url, formData, config);
if (res.data) {
setFileUpload(res.data.url);
setfileload(false);
}
} catch (err) {
e.target.value = "";
setfileload(false);
setpercentage(0);
console.log("err in libreconverter ", err);
}
}
}
}
}
@@ -360,7 +454,12 @@ const Forms = (props) => {
type="file"
className="bg-white px-2 py-1.5 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
onChange={(e) => handleFileInput(e)}
accept="application/pdf"
// accept="application/pdf"
accept={
isEnableSubscription
? "application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,image/png,image/jpeg"
: "application/pdf"
}
required
/>
{process.env.REACT_APP_DROPBOX_API_KEY && (
+20 -11
View File
@@ -271,6 +271,7 @@ function PdfRequestFiles() {
) {
const params = {
event: "viewed",
contactId: currUserId,
body: {
objectId: documentData?.[0].objectId,
file: documentData?.[0]?.SignedUrl || documentData?.[0]?.URL,
@@ -471,7 +472,8 @@ function PdfRequestFiles() {
const maxCount =
requiredCheckbox[i].options?.validation?.maxRequiredCount;
const parseMax = maxCount && parseInt(maxCount);
const response = requiredCheckbox[i].options?.response?.length;
const response =
requiredCheckbox[i].options?.response?.length;
const defaultValue =
requiredCheckbox[i].options?.defaultValue?.length;
if (parseMin === 0 && parseMax === 0) {
@@ -615,7 +617,8 @@ function PdfRequestFiles() {
setIsAlert,
objectId,
isSubscribed,
activeMailAdapter
activeMailAdapter,
pngUrl
);
if (res && res.status === "success") {
setPdfUrl(res.data);
@@ -641,11 +644,14 @@ function PdfRequestFiles() {
if (user) {
const expireDate = pdfDetails?.[0].ExpiryDate.iso;
const newDate = new Date(expireDate);
const localExpireDate = newDate.toLocaleDateString("en-US", {
day: "numeric",
month: "long",
year: "numeric"
});
const localExpireDate = newDate.toLocaleDateString(
"en-US",
{
day: "numeric",
month: "long",
year: "numeric"
}
);
let senderEmail = pdfDetails?.[0].ExtUserPtr.Email;
let senderPhone = pdfDetails?.[0]?.ExtUserPtr?.Phone;
const senderName = `${pdfDetails?.[0].ExtUserPtr.Name}`;
@@ -682,7 +688,10 @@ function PdfRequestFiles() {
requestSubject &&
(!isEnableSubscription || isSubscribed)
) {
const replacedRequestBody = requestBody.replace(/"/g, "'");
const replacedRequestBody = requestBody.replace(
/"/g,
"'"
);
const htmlReqBody =
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>" +
replacedRequestBody +
@@ -1156,9 +1165,9 @@ function PdfRequestFiles() {
isDecline.currnt === "Sure"
? "Are you sure want to decline this document ?"
: isDecline.currnt === "YouDeclined"
? "You have declined this document!"
: isDecline.currnt === "another" &&
"You can not sign this document as it has been declined/revoked."
? "You have declined this document!"
: isDecline.currnt === "another" &&
"You can not sign this document as it has been declined/revoked."
}
footerMessage={isDecline.currnt === "Sure"}
declineDoc={declineDoc}
+42 -13
View File
@@ -29,7 +29,9 @@ import {
textWidget,
getTenantDetails,
checkIsSubscribed,
convertPdfArrayBuffer
convertPdfArrayBuffer,
fetchImageBase64,
changeImageWH
} from "../constant/Utils";
import { useParams } from "react-router-dom";
import Tour from "reactour";
@@ -194,13 +196,18 @@ function SignYourSelf() {
setPdfDetails(documentData);
setExtUserId(documentData[0]?.ExtUserPtr?.objectId);
const url = documentData[0] && documentData[0]?.URL;
//convert document url in array buffer format to use embed widgets in pdf using pdf-lib
const arrayBuffer = await convertPdfArrayBuffer(url);
if (arrayBuffer === "Error") {
setHandleError("Error: Something went wrong!");
if (url) {
//convert document url in array buffer format to use embed widgets in pdf using pdf-lib
const arrayBuffer = await convertPdfArrayBuffer(url);
if (arrayBuffer === "Error") {
setHandleError("Error: Something went wrong!");
} else {
setPdfArrayBuffer(arrayBuffer);
}
} else {
setPdfArrayBuffer(arrayBuffer);
setHandleError("Error: Something went wrong!");
}
isCompleted = documentData[0].IsCompleted && documentData[0].IsCompleted;
if (isCompleted) {
setIsCompleted(true);
@@ -443,13 +450,13 @@ function SignYourSelf() {
Width: widgetTypeExist
? calculateInitialWidthHeight(dragTypeValue, widgetValue).getWidth
: dragTypeValue === "initials"
? defaultWidthHeight(dragTypeValue).width
: "",
? defaultWidthHeight(dragTypeValue).width
: "",
Height: widgetTypeExist
? calculateInitialWidthHeight(dragTypeValue, widgetValue).getHeight
: dragTypeValue === "initials"
? defaultWidthHeight(dragTypeValue).height
: "",
? defaultWidthHeight(dragTypeValue).height
: "",
options: addWidgetOptions(dragTypeValue)
};
@@ -526,7 +533,6 @@ function SignYourSelf() {
setSelectWidgetId(key);
setSignKey(key);
};
//function for send placeholder's co-ordinate(x,y) position embed signature url or stamp url
async function embedWidgetsData() {
let showAlert = false;
@@ -613,7 +619,6 @@ function SignYourSelf() {
});
}
}
// console.log("signyourself", xyPostion);
//function for get digital signature
const signPdfFun = async (base64Url, documentId) => {
let isCustomCompletionMail = false;
@@ -630,12 +635,36 @@ function SignYourSelf() {
isCustomCompletionMail = true;
}
}
let getSignature;
for (let item of xyPostion) {
const typeExist = item.pos.some((data) => data?.type);
if (typeExist) {
getSignature = item.pos.filter((data) => data?.type === "signature");
} else {
getSignature = item.pos.filter((data) => !data.isStamp);
}
}
let base64Sign = getSignature[0].SignUrl;
//check https type signature (default signature exist) then convert in base64
const isUrl = base64Sign.includes("https");
if (isUrl) {
try {
base64Sign = await fetchImageBase64(base64Sign);
} catch (e) {
console.log("error", e);
}
}
//change image width and height to 104/44 in png base64
const getNewse64 = await changeImageWH(base64Sign);
//remove suffiix of base64
const suffixbase64 = getNewse64 && getNewse64.split(",").pop();
let singleSign = {
pdfFile: base64Url,
docId: documentId,
isCustomCompletionMail: isCustomCompletionMail,
mailProvider: activeMailAdapter
mailProvider: activeMailAdapter,
signature: suffixbase64
};
await axios
+127 -5
View File
@@ -8,9 +8,10 @@ import sanitizeFileName from "../primitives/sanitizeFileName";
import axios from "axios";
import PremiumAlertHeader from "../primitives/PremiumAlertHeader";
import Tooltip from "../primitives/Tooltip";
import { isEnableSubscription } from "../constant/const";
import { isEnableSubscription, rejectBtn, submitBtn } from "../constant/const";
import { checkIsSubscribed } from "../constant/Utils";
import Upgrade from "../primitives/Upgrade";
import ModalUi from "../primitives/ModalUi";
function UserProfile() {
const navigate = useNavigate();
@@ -32,12 +33,16 @@ function UserProfile() {
const [jobTitle, setJobTitle] = useState(
extendUser && extendUser?.[0]?.JobTitle
);
const [isVerifyModal, setIsVerifyModal] = useState(false);
const [otp, setOtp] = useState("");
const [otpLoader, setOtpLoader] = useState(false);
const [isEmailVerified, setIsEmailVerified] = useState(false);
useEffect(() => {
getUserDetail();
}, []);
const getUserDetail = async () => {
setIsLoader(true);
const extClass = localStorage.getItem("Extand_Class");
const jsonSender = JSON.parse(extClass);
const HeaderDocId = jsonSender[0]?.HeaderDocId;
@@ -48,6 +53,9 @@ function UserProfile() {
if (HeaderDocId) {
setIsDisableDocId(HeaderDocId);
}
const isEmailVerified = Parse.User.current()?.attributes?.emailVerified;
setIsEmailVerified(isEmailVerified);
setIsLoader(false);
};
const handleSubmit = async (e) => {
e.preventDefault();
@@ -177,6 +185,56 @@ function UserProfile() {
const handleDisableDocId = () => {
setIsDisableDocId((prevChecked) => !prevChecked);
};
const handleVerifyBtn = async () => {
setIsVerifyModal(true);
await handleSendOTP();
};
const handleCloseVerifyModal = async () => {
setIsVerifyModal(false);
};
const handleSendOTP = async () => {
try {
let url = `${parseBaseUrl}functions/SendOTPMailV1`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": parseAppId
};
const body = { email: Parse.User.current().getEmail() };
await axios.post(url, body, { headers: headers });
} catch (error) {
alert(error.message);
}
};
const handleVerifyEmail = async (e) => {
e.preventDefault();
setOtpLoader(true);
try {
const resEmail = await Parse.Cloud.run("verifyemail", {
otp: otp,
email: Parse.User.current().getEmail()
});
if (resEmail?.message === "Email is verified.") {
setIsEmailVerified(true);
} else if (resEmail?.message === "Email is already verified.") {
setIsEmailVerified(true);
}
setOtp("");
alert(resEmail.message);
setIsVerifyModal(false);
} catch (error) {
alert(error.message);
} finally {
setOtpLoader(false);
}
};
const handleReset = async (e) => {
e.preventDefault();
setOtpLoader(true);
await handleSendOTP();
setOtpLoader(false);
alert("OTP sent on you email");
};
return (
<React.Fragment>
<Title title={"Profile"} />
@@ -310,9 +368,20 @@ function UserProfile() {
<li className="flex justify-between items-center border-t-[1px] border-gray-300 py-2 break-all">
<span className="font-semibold">Is Email verified:</span>{" "}
<span>
{UserProfile && UserProfile.emailVerified
? "Verified"
: "Not verified"}
{isEmailVerified ? (
"Verified"
) : (
<span>
Not verified(
<span
onClick={() => handleVerifyBtn()}
className="hover:underline text-blue-600 cursor-pointer"
>
verify
</span>
)
</span>
)}
</span>
</li>
<li className="border-y-[1px] border-gray-300 break-all">
@@ -399,6 +468,59 @@ function UserProfile() {
</button>
</div>
</div>
{isVerifyModal && (
<ModalUi
isOpen
title={"OTP verification"}
handleClose={handleCloseVerifyModal}
>
{otpLoader ? (
<div
style={{
height: "150px",
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
<div
style={{
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
) : (
<form onSubmit={(e) => handleVerifyEmail(e)}>
<div className="px-6 py-3">
<label className="mb-2">Enter OTP</label>
<input
type="tel"
pattern="[0-9]{4}"
className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs"
placeholder="Enter OTP sent on mail"
value={otp}
onChange={(e) => setOtp(e.target.value)}
/>
</div>
<hr />
<div className="px-6 my-3">
<button type="submit" className={submitBtn}>
Verify
</button>
<button
className={`${rejectBtn} ml-2`}
onClick={(e) => handleReset(e)}
>
Resend
</button>
</div>
</form>
)}
</ModalUi>
)}
</div>
)}
</React.Fragment>
+4 -2
View File
@@ -33,7 +33,8 @@ import getInvoices from './parsefunction/getInvoices.js';
import getPayments from './parsefunction/getPayments.js';
import getSubscriptions from './parsefunction/getSubscriptions.js';
import TenantAterFind from './parsefunction/TenantAfterFind.js';
import saveSubscriptio from './parsefunction/saveSubscription.js';
import saveSubscription from './parsefunction/saveSubscription.js';
import VerifyEmail from './parsefunction/VerifyEmail.js';
import encryptedpdf from './parsefunction/encryptedPdf.js';
Parse.Cloud.define('AddUserToRole', addUserToGroups);
@@ -71,5 +72,6 @@ Parse.Cloud.afterFind('contracts_Document', DocumentBeforeFind);
Parse.Cloud.afterFind('contracts_Template', TemplateAfterFind);
Parse.Cloud.afterFind('contracts_Signature', SignatureAfterFind);
Parse.Cloud.afterFind('partners_Tenant', TenantAterFind);
Parse.Cloud.define('savesubscription', saveSubscriptio);
Parse.Cloud.define('savesubscription', saveSubscription);
Parse.Cloud.define('verifyemail', VerifyEmail);
Parse.Cloud.define('encryptedpdf', encryptedpdf)
@@ -0,0 +1,49 @@
export default async function VerifyEmail(request) {
try {
if (!request?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
} else {
let otpN = request.params.otp;
let otp = parseInt(otpN);
let email = request.params.email;
//checking otp is correct or not which already save in defaultdata_Otp class
const checkOtp = new Parse.Query('defaultdata_Otp');
checkOtp.equalTo('Email', email);
checkOtp.equalTo('OTP', otp);
const res = await checkOtp.first({ useMasterKey: true });
if (res) {
// Fetch the user by their objectId
const isEmailVerified = request?.user?.get('emailVerified');
if (isEmailVerified) {
return { message: 'Email is already verified.' };
} else {
const userQuery = new Parse.Query(Parse.User);
const user = await userQuery.get(request?.user.id, {
sessionToken: request?.user.getSessionToken(),
});
// Update the emailVerified field to true
user.set('emailVerified', true);
// Save the user object
const res = await user.save(null, { useMasterKey: true });
if (res) {
return { message: 'Email is verified.' };
} else {
const error = new Error('Something went wrong, please try again later!');
error.code = 400; // Set the error code (e.g., 400 for bad request)
throw error;
}
}
} else {
const error = new Error('OTP is invalid.');
error.code = 400; // Set the error code (e.g., 400 for bad request)
throw error;
}
}
} catch (err) {
console.log('err ', err.code + ' ' + err.message);
throw err;
}
}
@@ -2,6 +2,8 @@ import axios from 'axios';
export default async function callWebhook(request) {
const event = request.params.event;
const body = request.params.body;
const docId = body.objectId;
const contactId = request.params.contactId;
const serverUrl = process.env.SERVER_URL;
const appId = process.env.APP_ID;
const userRes = await axios.get(serverUrl + '/users/me', {
@@ -13,6 +15,39 @@ export default async function callWebhook(request) {
const userId = userRes.data && userRes.data.objectId;
if (userId) {
if (event === 'viewed' && contactId) {
const docQuery = new Parse.Query('contracts_Document');
const res = await docQuery.get(docId, { useMasterKey: true });
if (res) {
const _res = res.toJSON();
const userPtr = {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: contactId,
};
const date = new Date().toISOString();
const obj = {
UserPtr: userPtr,
SignedUrl: _res.SignedUrl,
Activity: 'Viewed',
ipAddress: request.headers['x-real-ip'],
ViewedOn: date,
};
const isUserExist = _res?.AuditTrail?.some(
x => x.UserPtr.objectId === contactId && x?.ViewedOn
);
if (!isUserExist) {
const updateDoc = new Parse.Object('contracts_Document');
updateDoc.id = res.id;
if (_res?.AuditTrail && _res?.AuditTrail?.length > 0) {
updateDoc.set('AuditTrail', [..._res?.AuditTrail, obj]);
} else {
updateDoc.set('AuditTrail', [obj]);
}
await updateDoc.save(null, { useMasterKey: true });
}
}
}
const extendcls = new Parse.Query('contracts_Users');
extendcls.equalTo('UserId', { __type: 'Pointer', className: '_User', objectId: userId });
const res = await extendcls.first({ useMasterKey: true });
@@ -39,11 +74,11 @@ export default async function callWebhook(request) {
});
webhook.save(null, { useMasterKey: true });
} catch (err) {
console.log('err save in contracts_Webhook', err);
console.log('err save in contracts_Webhook', err.message);
}
})
.catch(err => {
console.log('Err send data to webhook', err);
console.log('Err send data to webhook', err.message);
try {
const webhook = new Parse.Object('contracts_Webhook');
webhook.set('Log', err?.status);
@@ -54,7 +89,7 @@ export default async function callWebhook(request) {
});
webhook.save(null, { useMasterKey: true });
} catch (err) {
console.log('err save in contracts_Webhook', err);
console.log('err save in contracts_Webhook', err.message);
}
});
}
@@ -1,6 +1,6 @@
import { PostHog } from 'posthog-node';
const ph_project_api_key = process.env.PH_PROJECT_API_KEY;
const client = new PostHog(ph_project_api_key);
const client = ph_project_api_key ? new PostHog(ph_project_api_key) : '';
export default async function encryptedpdf(request) {
const email = request.params.email;
if (client) {
@@ -17,21 +17,36 @@ export default async function GenerateCertificate(docDetails) {
const text = 14;
const textKeyColor = rgb(0.12, 0.12, 0.12);
const textValueColor = rgb(0.3, 0.3, 0.3);
const completedAt = new Date(docDetails.updatedAt);
const completedAt = new Date();
const completedUTCtime = completedAt.toUTCString();
const signersCount = docDetails?.Signers?.length || 1;
const createdAt = new Date();
const createdUTCTime = createdAt.toUTCString();
const createDate = 'Generated On ' + createdUTCTime;
const generateAt = new Date();
const generatedUTCTime = generateAt.toUTCString();
const generatedOn = 'Generated On ' + generatedUTCTime;
const company = docDetails?.ExtUserPtr?.Company || '';
const auditTrail =
docDetails.AuditTrail?.length > 1
? docDetails.AuditTrail.map(x => {
const data = docDetails.Signers.find(y => y.objectId === x.UserPtr.objectId);
return { ...data, ipAddress: x.ipAddress };
return {
...data,
ipAddress: x.ipAddress,
SignedOn: x?.SignedOn || generatedUTCTime,
ViewedOn: x?.ViewedOn || generatedUTCTime,
Signature: x?.Signature || '',
};
})
: [{ ...docDetails.ExtUserPtr, ipAddress: docDetails?.AuditTrail[0].ipAddress }];
: [
{
...docDetails.ExtUserPtr,
ipAddress: docDetails?.AuditTrail[0].ipAddress,
SignedOn: docDetails?.AuditTrail[0]?.SignedOn || generatedUTCTime,
ViewedOn: docDetails?.AuditTrail[0]?.ViewedOn || generatedUTCTime,
Signature: docDetails?.AuditTrail[0]?.Signature || '',
},
];
const half = width / 2;
// Draw a border
page.drawRectangle({
x: startX,
@@ -48,7 +63,7 @@ export default async function GenerateCertificate(docDetails) {
height: 25,
});
page.drawText(createDate, {
page.drawText(generatedOn, {
x: 320,
y: 810,
size: 12,
@@ -127,8 +142,7 @@ export default async function GenerateCertificate(docDetails) {
font: timesRomanFont,
color: textValueColor,
});
page.drawText('Completed on :', {
page.drawText('Created on :', {
x: 30,
y: 625,
size: text,
@@ -136,15 +150,14 @@ export default async function GenerateCertificate(docDetails) {
color: textKeyColor,
});
page.drawText(`${completedUTCtime}`, {
x: 120,
page.drawText(`${new Date(docDetails.createdAt).toUTCString()}`, {
x: 105,
y: 625,
size: text,
font: timesRomanFont,
color: textValueColor,
});
page.drawText('Signers :', {
page.drawText('Completed on :', {
x: 30,
y: 605,
size: text,
@@ -152,35 +165,102 @@ export default async function GenerateCertificate(docDetails) {
color: textKeyColor,
});
page.drawText(`${completedUTCtime}`, {
x: 125,
y: 605,
size: text,
font: timesRomanFont,
color: textValueColor,
});
page.drawText('Signers :', {
x: 30,
y: 585,
size: text,
font: timesRomanFont,
color: textKeyColor,
});
page.drawText(`${signersCount}`, {
x: 80,
y: 605,
y: 585,
size: text,
font: timesRomanFont,
color: textValueColor,
});
page.drawText('Document originator', {
x: 30,
y: 565,
size: 17,
font: timesRomanFont,
color: titleColor,
});
page.drawText('Name :', {
x: 60,
y: 545,
size: text,
font: timesRomanFont,
color: textKeyColor,
});
page.drawText(`${docDetails.ExtUserPtr.Name}`, {
x: 105,
y: 545,
size: text,
font: timesRomanFont,
color: textValueColor,
});
page.drawText('Email :', {
x: 60,
y: 525,
size: text,
font: timesRomanFont,
color: textKeyColor,
});
page.drawText(`${docDetails.ExtUserPtr.Email}`, {
x: 105,
y: 525,
size: text,
font: timesRomanFont,
color: textValueColor,
});
page.drawText('IP address :', {
x: 60,
y: 505,
size: text,
font: timesRomanFont,
color: textKeyColor,
});
page.drawText(`152.58.21.622`, {
x: 130,
y: 505,
size: text,
font: timesRomanFont,
color: textValueColor,
});
page.drawLine({
start: { x: 30, y: 565 },
end: { x: width - 30, y: 565 },
start: { x: 30, y: 495 },
end: { x: width - 30, y: 495 },
color: rgb(0.12, 0.12, 0.12),
thickness: 0.5,
});
page.drawText('Recipients', {
x: 30,
y: 575,
size: subtitle,
font: timesRomanFont,
color: titleColor,
});
let yPosition1 = 550;
let yPosition2 = 530;
let yPosition3 = 510;
let yPosition4 = 500;
auditTrail.forEach(x => {
page.drawText('Name :', {
let yPosition1 = 475;
let yPosition2 = 455;
let yPosition3 = 435;
let yPosition4 = 415;
let yPosition5 = 395;
let yPosition6 = 360;
auditTrail.slice(0, 3).forEach(async (x, i) => {
const embedPng = x.Signature ? await pdfDoc.embedPng(x.Signature) : '';
page.drawText(`Signer ${i + 1}`, {
x: 30,
y: yPosition1,
size: subtitle,
font: timesRomanFont,
color: titleColor,
});
page.drawText('Name :', {
x: 30,
y: yPosition2,
size: text,
font: timesRomanFont,
color: textKeyColor,
@@ -188,7 +268,23 @@ export default async function GenerateCertificate(docDetails) {
page.drawText(x?.Name, {
x: 75,
y: yPosition1,
y: yPosition2,
size: text,
font: timesRomanFont,
color: textValueColor,
});
page.drawText('Viewed on :', {
x: half,
y: yPosition2,
size: text,
font: timesRomanFont,
color: textKeyColor,
});
page.drawText(`${new Date(x.ViewedOn).toUTCString()}`, {
x: half + 75,
y: yPosition2,
size: text,
font: timesRomanFont,
color: textValueColor,
@@ -196,7 +292,7 @@ export default async function GenerateCertificate(docDetails) {
page.drawText('Email :', {
x: 30,
y: yPosition2,
y: yPosition3,
size: text,
font: timesRomanFont,
color: textKeyColor,
@@ -204,41 +300,271 @@ export default async function GenerateCertificate(docDetails) {
page.drawText(x?.Email, {
x: 75,
y: yPosition2,
y: yPosition3,
size: text,
font: timesRomanFont,
color: textValueColor,
});
page.drawText('Accessed from :', {
x: 30,
page.drawText('Signed on :', {
x: half,
y: yPosition3,
size: text,
font: timesRomanFont,
color: textKeyColor,
});
page.drawText(x?.ipAddress, {
x: 125,
page.drawText(`${new Date(x.SignedOn).toUTCString()}`, {
x: half + 70,
y: yPosition3,
size: text,
font: timesRomanFont,
color: textValueColor,
});
page.drawText('IP address :', {
x: 30,
y: yPosition4,
size: text,
font: timesRomanFont,
color: textKeyColor,
});
page.drawText(x?.ipAddress, {
x: 100,
y: yPosition4,
size: text,
font: timesRomanFont,
color: textValueColor,
});
page.drawText('Security level :', {
x: half,
y: yPosition4,
size: text,
font: timesRomanFont,
color: textKeyColor,
});
page.drawText(`Email, OTP Auth`, {
x: half + 90,
y: yPosition4,
size: text,
font: timesRomanFont,
color: textValueColor,
});
page.drawText('Signature :', {
x: 30,
y: yPosition5,
size: text,
font: timesRomanFont,
color: textKeyColor,
});
page.drawRectangle({
x: 98,
y: yPosition5 - 27,
width: 104,
height: 44,
borderColor: rgb(0.22, 0.18, 0.47),
borderWidth: 1,
});
if (embedPng) {
page.drawImage(embedPng, {
x: 100,
y: yPosition5 - 25,
width: 100,
height: 40,
});
}
page.drawLine({
start: { x: 30, y: yPosition4 },
end: { x: width - 30, y: yPosition4 },
start: { x: 30, y: yPosition6 },
end: { x: width - 30, y: yPosition6 },
color: rgb(0.12, 0.12, 0.12),
thickness: 0.5,
});
yPosition1 = yPosition4 - 20;
yPosition1 = yPosition6 - 20;
yPosition2 = yPosition1 - 20;
yPosition3 = yPosition2 - 20;
yPosition4 = yPosition4 - 70;
yPosition4 = yPosition3 - 20;
yPosition5 = yPosition4 - 20;
yPosition6 = yPosition6 - 140;
});
if (auditTrail.length > 3) {
let currentPageIndex = 1;
let currentPage = page;
auditTrail.slice(3).forEach(async (x, i) => {
const embedPng = x.Signature ? await pdfDoc.embedPng(x.Signature) : '';
// Calculate remaining space on current page
const remainingSpace = yPosition6;
// If there's not enough space for the next entry, create a new page
if (remainingSpace < 90) {
// Adjust the value as needed
currentPageIndex++;
currentPage = pdfDoc.addPage();
currentPage.drawRectangle({
x: startX,
y: startY,
width: width - 2 * startX,
height: height - 2 * startY,
borderColor: borderColor,
borderWidth: 1,
});
yPosition1 = currentPage.getHeight() - 40;
yPosition2 = yPosition1 - 20;
yPosition3 = yPosition2 - 20;
yPosition4 = yPosition3 - 20;
yPosition5 = yPosition4 - 20;
yPosition6 = currentPage.getHeight() - 160;
}
currentPage.drawText(`Signer ${4 + i}`, {
x: 30,
y: yPosition1,
size: subtitle,
font: timesRomanFont,
color: titleColor,
});
currentPage.drawText('Name :', {
x: 30,
y: yPosition2,
size: text,
font: timesRomanFont,
color: textKeyColor,
});
currentPage.drawText(x?.Name, {
x: 75,
y: yPosition2,
size: text,
font: timesRomanFont,
color: textValueColor,
});
currentPage.drawText('Viewed on :', {
x: half,
y: yPosition2,
size: text,
font: timesRomanFont,
color: textKeyColor,
});
currentPage.drawText(`${new Date(x.ViewedOn).toUTCString()}`, {
x: half + 75,
y: yPosition2,
size: text,
font: timesRomanFont,
color: textValueColor,
});
currentPage.drawText('Email :', {
x: 30,
y: yPosition3,
size: text,
font: timesRomanFont,
color: textKeyColor,
});
currentPage.drawText(x?.Email, {
x: 75,
y: yPosition3,
size: text,
font: timesRomanFont,
color: textValueColor,
});
currentPage.drawText('Signed on :', {
x: half,
y: yPosition3,
size: text,
font: timesRomanFont,
color: textKeyColor,
});
currentPage.drawText(`${new Date(x.SignedOn).toUTCString()}`, {
x: half + 70,
y: yPosition3,
size: text,
font: timesRomanFont,
color: textValueColor,
});
currentPage.drawText('IP address :', {
x: 30,
y: yPosition4,
size: text,
font: timesRomanFont,
color: textKeyColor,
});
currentPage.drawText(x?.ipAddress, {
x: 100,
y: yPosition4,
size: text,
font: timesRomanFont,
color: textValueColor,
});
currentPage.drawText('Security level :', {
x: half,
y: yPosition4,
size: text,
font: timesRomanFont,
color: textKeyColor,
});
currentPage.drawText(`Email, OTP Auth`, {
x: half + 90,
y: yPosition4,
size: text,
font: timesRomanFont,
color: textValueColor,
});
currentPage.drawText('Signature :', {
x: 30,
y: yPosition5,
size: text,
font: timesRomanFont,
color: textKeyColor,
});
currentPage.drawRectangle({
x: 98,
y: yPosition5 - 27,
width: 104,
height: 44,
borderColor: rgb(0.22, 0.18, 0.47),
borderWidth: 1,
});
if (embedPng) {
currentPage.drawImage(embedPng, {
x: 100,
y: yPosition5 - 25,
width: 100,
height: 40,
});
}
currentPage.drawLine({
start: { x: 30, y: yPosition6 },
end: { x: width - 30, y: yPosition6 },
color: rgb(0.12, 0.12, 0.12),
thickness: 0.5,
});
// Update y positions for the next entry
yPosition1 = yPosition6 - 20;
yPosition2 = yPosition1 - 20;
yPosition3 = yPosition2 - 20;
yPosition4 = yPosition3 - 20;
yPosition5 = yPosition4 - 20;
yPosition6 = yPosition6 - 140;
});
}
const pdfBytes = await pdfDoc.save();
return pdfBytes;
}
+196 -236
View File
@@ -8,85 +8,96 @@ import GenerateCertificate from './GenerateCertificate.js';
const serverUrl = process.env.SERVER_URL,
APPID = process.env.APP_ID,
masterKEY = process.env.MASTER_KEY;
async function uploadFile(e, a) {
async function uploadFile(e, t) {
try {
var t = fs.readFileSync(a),
s = new Parse.File(e, [...t], 'application/pdf'),
r = (await s.save({ useMasterKey: !0 }), s.url());
return { imageUrl: r };
var a = fs.readFileSync(t),
r = new Parse.File(e, [...a], 'application/pdf'),
i = (await r.save({ useMasterKey: !0 }), r.url());
return { imageUrl: i };
} catch (e) {
console.log('Err ', e), fs.unlinkSync(a);
console.log('Err ', e), fs.unlinkSync(t);
}
}
async function updateDoc(t, s, r, i, o, n) {
async function updateDoc(a, r, i, s, o, n, l) {
try {
var l = {
UserPtr: { __type: 'Pointer', className: n, objectId: r },
SignedUrl: s,
Activity: 'Signed',
ipAddress: i,
};
var c,
d,
p = {
UserPtr: { __type: 'Pointer', className: n, objectId: i },
SignedUrl: r,
Activity: 'Signed',
ipAddress: s,
SignedOn: new Date(),
Signature: l,
};
let e;
var d = (e = o.AuditTrail && 0 < o.AuditTrail.length ? [...o.AuditTrail, l] : [l]).filter(
e => 'Signed' === e.Activity
);
let a = !1;
!((o.Signers && 0 < o.Signers.length && d.length !== o.Signers.length) || !(a = !0));
var c = { SignedUrl: s, AuditTrail: e, IsCompleted: a };
await axios.put(serverUrl + '/classes/contracts_Document/' + t, c, {
var m = (e =
o.AuditTrail && 0 < o.AuditTrail.length
? (-1 !==
(d = (c = JSON.parse(JSON.stringify(o.AuditTrail))).findIndex(
e => e.UserPtr.objectId === i && 'Created' !== e.Activity
))
? (c[d] = { ...c[d], ...p })
: c.push(p),
c)
: [p]).filter(e => 'Signed' === e.Activity);
let t = !1;
!((o.Signers && 0 < o.Signers.length && m.length !== o.Signers.length) || !(t = !0));
var g = { SignedUrl: r, AuditTrail: e, IsCompleted: t };
await axios.put(serverUrl + '/classes/contracts_Document/' + a, g, {
headers: {
'Content-Type': 'application/json',
'X-Parse-Application-Id': APPID,
'X-Parse-Master-Key': masterKEY,
},
});
return { isCompleted: a, message: 'success', AuditTrail: e };
return { isCompleted: t, message: 'success', AuditTrail: e };
} catch (e) {
return console.log('update doc err ', e), 'err';
}
}
async function sendCompletedMail(e) {
var a = e.url,
t = e.doc,
s = e.doc.ExtUserPtr,
r = t.Name,
i = s.Email;
let o = `Document "${r}" has been signed by all parties`,
var t = e.url,
a = e.doc,
r = e.doc.ExtUserPtr,
i = a.Name,
s = r.Email;
let o = `Document "${i}" has been signed by all parties`,
n =
"<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=https://qikinnovation.ams3.digitaloceanspaces.com/logo.png 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 signed successfully</p></div><div><p style='padding:20px;font-family:system-ui;font-size:14px'>All parties have successfully signed the document " +
`<b>"${r}"</b>` +
`<b>"${i}"</b>` +
'. Kindly download the document from the attachment.</p></div> </div><div><p>This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender ' +
s.Email +
r.Email +
' directly. If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href=www.opensignlabs.com target=_blank>here</a>.</p></div></div></body></html>';
if (e?.isCustomMail)
try {
var l,
d,
c,
d,
p,
m,
g = new Parse.Query('partners_Tenant');
g.equalTo('UserId', { __type: 'Pointer', className: '_User', objectId: s.UserId.objectId });
const u = await g.first();
u &&
((l = JSON.parse(JSON.stringify(u)))?.CompletionSubject && (o = l?.CompletionSubject),
g.equalTo('UserId', { __type: 'Pointer', className: '_User', objectId: r.UserId.objectId });
const f = await g.first();
f &&
((l = JSON.parse(JSON.stringify(f)))?.CompletionSubject && (o = l?.CompletionSubject),
l?.CompletionBody && (n = l?.CompletionBody),
(d = t.ExpiryDate.iso),
(c = new Date(d).toLocaleDateString('en-US', {
(c = a.ExpiryDate.iso),
(d = new Date(c).toLocaleDateString('en-US', {
day: 'numeric',
month: 'long',
year: 'numeric',
})),
(p = {
document_title: r,
sender_name: s.Name,
sender_mail: s.Email,
sender_phone: s.Phone,
receiver_name: s.Name,
receiver_email: s.Email,
receiver_phone: s.Phone,
expiry_date: c,
company_name: s.Company,
document_title: i,
sender_name: r.Name,
sender_mail: r.Email,
sender_phone: r.Phone,
receiver_name: r.Name,
receiver_email: r.Email,
receiver_phone: r.Phone,
expiry_date: d,
company_name: r.Company,
}),
(m = replaceMailVaribles(o, n, p)),
(o = m.subject),
@@ -95,12 +106,12 @@ async function sendCompletedMail(e) {
console.log('error in fetch tenant in signpdf', e.message);
}
g = {
extUserId: s.objectId,
url: a,
extUserId: r.objectId,
url: t,
from: 'OpenSign™',
recipient: i,
recipient: s,
subject: o,
pdfName: r,
pdfName: i,
html: n,
mailProvider: e.mailProvider,
};
@@ -112,44 +123,40 @@ async function sendCompletedMail(e) {
},
});
}
async function sendDoctoWebhook(t, e, a, s) {
let r = [];
(r = s
? { name: s?.Name, email: s?.Email, phone: s?.Phone }
: t?.data?.Signers?.map(e => ({ name: e.Name, email: e.Email, phone: e.Phone })) || [
{
name: t?.data?.ExtUserPtr?.Name,
email: t?.data?.ExtUserPtr?.Email,
phone: t?.data?.ExtUserPtr?.Phone,
},
async function sendDoctoWebhook(a, e, t, r) {
let i = [];
(i = r
? { name: r?.Name, email: r?.Email, phone: r?.Phone }
: a?.Signers?.map(e => ({ name: e.Name, email: e.Email, phone: e.Phone })) || [
{ name: a?.ExtUserPtr?.Name, email: a?.ExtUserPtr?.Email, phone: a?.ExtUserPtr?.Phone },
]),
t.data.ExtUserPtr?.Webhook &&
((s =
'signed' === a
? { signer: r, signedAt: new Date() }
: { signers: r, completedAt: new Date() }),
(a = {
event: a,
objectId: t?.data?.objectId,
a.ExtUserPtr?.Webhook &&
((r =
'signed' === t
? { signer: i, signedAt: new Date() }
: { signers: i, completedAt: new Date() }),
(t = {
event: t,
objectId: a?.objectId,
file: e || '',
name: t?.data?.Name,
note: t?.data?.Note || '',
description: t?.data?.Description || '',
...s,
createdAt: t?.data?.createdAt,
name: a?.Name,
note: a?.Note || '',
description: a?.Description || '',
...r,
createdAt: a?.createdAt,
}),
await axios
.post(t?.data?.ExtUserPtr?.Webhook, a, { headers: { 'Content-Type': 'application/json' } })
axios
.post(a?.ExtUserPtr?.Webhook, t, { headers: { 'Content-Type': 'application/json' } })
.then(e => {
try {
var a = new Parse.Object('contracts_Webhook');
a.set('Log', e?.status),
a.set('UserId', {
var t = new Parse.Object('contracts_Webhook');
t.set('Log', e?.status),
t.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: t.data.ExtUserPtr.UserId.objectId,
objectId: a.ExtUserPtr.UserId.objectId,
}),
a.save(null, { useMasterKey: !0 });
t.save(null, { useMasterKey: !0 });
} catch (e) {
console.log('err save in contracts_Webhook', e.message);
}
@@ -157,109 +164,106 @@ async function sendDoctoWebhook(t, e, a, s) {
.catch(e => {
console.log('Err send data to webhook', e.message);
try {
var a = new Parse.Object('contracts_Webhook');
a.set('Log', e?.status),
a.set('UserId', {
var t = new Parse.Object('contracts_Webhook');
t.set('Log', e?.status),
t.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: t.data.ExtUserPtr.UserId.objectId,
objectId: a.ExtUserPtr.UserId.objectId,
}),
a.save(null, { useMasterKey: !0 });
t.save(null, { useMasterKey: !0 });
} catch (e) {
console.log('err save in contracts_Webhook', e.message);
}
}));
}
const sendMailsaveCertifcate = async (e, t, a, r, i, s) => {
var o = await GenerateCertificate(e),
o = await PDFDocument.load(o),
o =
(pdflibAddPlaceholder({
pdfDoc: o,
reason: 'Digitally signed by OpenSign.',
location: 'n/a',
signatureLength: 15e3,
}),
await o.save()),
o = Buffer.from(o),
t = await new SignPDF(o, t).signPDF(),
t =
(fs.writeFileSync('./exports/certificate.pdf', t),
await uploadFile('certificate.pdf', './exports/certificate.pdf')),
n = { CertificateUrl: t.imageUrl };
await axios.put(serverUrl + '/classes/contracts_Document/' + e.objectId, n, {
headers: {
'Content-Type': 'application/json',
'X-Parse-Application-Id': APPID,
'X-Parse-Master-Key': masterKEY,
},
}),
e.IsSendMail && !1 === e.IsSendMail
? console.log("don't send mail")
: sendCompletedMail({ url: a, isCustomMail: r, doc: e, mailProvider: i }),
saveFileUsage(o.length, t.imageUrl, s),
sendDoctoWebhook(e, a, 'completed');
};
async function PDF(o) {
try {
var n = o.params.docId,
e = o.params.userId,
l = o.params.isCustomCompletionMail || !1,
d = o.params.mailProvider || '',
c = await axios.get(
serverUrl + '/classes/contracts_Document/' + n + '?include=ExtUserPtr,Signers',
{
headers: {
'Content-Type': 'application/json',
'X-Parse-Application-Id': APPID,
'X-Parse-Master-Key': masterKEY,
},
}
),
p = await axios.get(serverUrl + '/users/me', {
headers: {
'X-Parse-Application-Id': APPID,
'X-Parse-Session-Token': o.headers.sessiontoken,
},
});
if (!p.data || !p.data.objectId) return { status: 'error', message: 'This user not allowed!' };
if (!o?.user)
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
{
var a,
t,
s,
m = JSON.stringify({ objectId: e });
let r, i;
i = e
? (a = await axios.get(serverUrl + '/classes/contracts_Contactbook?where=' + m, {
headers: {
'X-Parse-Application-Id': APPID,
'X-Parse-Session-Token': o.headers.sessiontoken,
},
})).data && 0 < a.data.results.length
? ((r = a), 'contracts_Contactbook')
: ((r = await axios.get(serverUrl + '/classes/contracts_Users?where=' + m, {
headers: { 'X-Parse-Application-Id': APPID, 'X-Parse-Master-Key': masterKEY },
})),
'contracts_Users')
: ((t = JSON.stringify({
UserId: { __type: 'Pointer', className: '_User', objectId: p.data.objectId },
})),
(s = await axios.get(serverUrl + '/classes/contracts_Users?where=' + t, {
headers: { 'X-Parse-Application-Id': APPID, 'X-Parse-Master-Key': masterKEY },
})).data && 0 < s.data.results.length
? ((r = s), 'contracts_Users')
: ((r = await axios.get(serverUrl + '/classes/contracts_Contactbook?where=' + t, {
headers: {
'X-Parse-Application-Id': APPID,
'X-Parse-Session-Token': o.headers.sessiontoken,
},
})),
'contracts_Contactbook'));
var g = r.data.results[0].Name,
u = r.data.results[0].Email;
if (!o.params.pdfFile) return { status: 'error', message: 'Pdf file not present!' };
var n = o?.user?.toJSON(),
e = o.params.docId;
const F = o.params.userId;
var l = o.params.isCustomCompletionMail || !1,
c = o.params.mailProvider || '',
d = o.params.signature || '',
t = new Parse.Query('contracts_Document'),
a =
(t.include('ExtUserPtr,Signers'),
t.equalTo('objectId', e),
await t.first({ useMasterKey: !0 }));
if (!a) throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Document not found.');
var r,
p = a?.toJSON();
let i, s;
F
? ((r = p.Signers.find(e => e.objectId === F)),
console.log('_contractUser ', r),
r && ((i = r), (s = 'contracts_Contactbook')))
: ((s = 'contracts_Users'), (i = p.ExtUserPtr));
var m,
g = i.Name,
f = i.Email;
if (!o.params.pdfFile) throw (((m = new Error('Pdf file not present!')).code = 400), m);
{
let e = Buffer.from(o.params.pdfFile, 'base64');
var h = process.env.PFX_BASE64,
f = Buffer.from(h, 'base64'),
P = {
UserPtr: { __type: 'Pointer', className: i, objectId: r.data.results[0].objectId },
var u = process.env.PFX_BASE64,
h = Buffer.from(u, 'base64'),
y = {
UserPtr: { __type: 'Pointer', className: s, objectId: i.objectId },
SignedUrl: '',
Activity: 'Signed',
ipAddress: o.headers['x-real-ip'],
};
let a;
var y = (a =
c.data.AuditTrail && 0 < c.data.AuditTrail.length
? [...c.data.AuditTrail, P]
: [P]).filter(e => 'Signed' === e.Activity);
let t = !1;
!(
(c.data.Signers && 0 < c.data.Signers.length && y.length !== c.data.Signers.length) ||
!(t = !0)
let t;
var P = (t = p.AuditTrail && 0 < p.AuditTrail.length ? [...p.AuditTrail, y] : [y]).filter(
e => 'Signed' === e.Activity
);
let a = !1;
!((p.Signers && 0 < p.Signers.length && P.length !== p.Signers.length) || !(a = !0));
var v,
b,
U,
I,
S,
w,
U,
D,
S = `exported_file_${Math.floor(5e3 * Math.random())}.pdf`,
_ = './exports/' + S;
let s = e.length;
s = (
t
? ((v = c.data.Signers?.map(e => e.Name + ' <' + e.Email + '>')),
I = `exported_file_${Math.floor(5e3 * Math.random())}.pdf`,
_ = './exports/' + I;
let r = e.length;
r = (
a
? ((v = p.Signers?.map(e => e.Name + ' <' + e.Email + '>')),
(e =
v && 0 < v.length
? ((b = await PDFDocument.load(e)),
@@ -269,97 +273,53 @@ async function PDF(o) {
location: 'n/a',
signatureLength: 15e3,
}),
(U = await b.save()),
Buffer.from(U))
: ((I = await PDFDocument.load(e)),
(S = await b.save()),
Buffer.from(S))
: ((w = await PDFDocument.load(e)),
pdflibAddPlaceholder({
pdfDoc: I,
reason: 'Digitally signed by OpenSign for ' + g + ' <' + u + '>',
pdfDoc: w,
reason: 'Digitally signed by OpenSign for ' + g + ' <' + f + '>',
location: 'n/a',
signatureLength: 15e3,
}),
(w = await I.save()),
Buffer.from(w))),
(D = await new SignPDF(e, f).signPDF()),
(U = await w.save()),
Buffer.from(U))),
(D = await new SignPDF(e, h).signPDF()),
fs.writeFileSync(_, D),
D)
: (fs.writeFileSync(_, e), e)
).length;
var A,
x,
E,
j,
k,
F,
C,
T,
N,
M = await uploadFile(S, _);
if (M && M.imageUrl)
return (
(A = await updateDoc(
var E = await uploadFile(I, _);
if (E && E.imageUrl) {
var x,
A,
j = await updateDoc(
o.params.docId,
M.imageUrl,
r.data.results[0].objectId,
E.imageUrl,
i.objectId,
o.headers['x-real-ip'],
c.data,
i
)),
sendDoctoWebhook(c, M.imageUrl, 'signed', r?.data.results?.[0]),
saveFileUsage(s, M.imageUrl, p.data.objectId),
A &&
A.isCompleted &&
((x = { ...c.data, AuditTrail: A.AuditTrail }),
(E = await GenerateCertificate(x)),
(j = await PDFDocument.load(E)),
pdflibAddPlaceholder({
pdfDoc: j,
reason: 'Digitally signed by OpenSign.',
location: 'n/a',
signatureLength: 15e3,
}),
(k = await j.save()),
(F = Buffer.from(k)),
(C = await new SignPDF(F, f).signPDF()),
fs.writeFileSync('./exports/certificate.pdf', C),
(N = {
CertificateUrl: (T = await uploadFile(
'certificate.pdf',
'./exports/certificate.pdf'
)).imageUrl,
}),
await axios.put(serverUrl + '/classes/contracts_Document/' + n, N, {
headers: {
'Content-Type': 'application/json',
'X-Parse-Application-Id': APPID,
'X-Parse-Master-Key': masterKEY,
},
}),
c.data.IsSendMail && !1 === c.data.IsSendMail
? console.log("don't send mail")
: sendCompletedMail({
url: M.imageUrl,
isCustomMail: l,
doc: c.data,
mailProvider: d,
}),
saveFileUsage(F.length, T.imageUrl, p.data.objectId),
sendDoctoWebhook(c, M.imageUrl, 'completed')),
p,
s,
d
);
if (
(sendDoctoWebhook(p, E.imageUrl, 'signed', i),
saveFileUsage(r, E.imageUrl, n.objectId),
j &&
j.isCompleted &&
((x = { ...p, AuditTrail: j.AuditTrail }),
sendMailsaveCertifcate(x, h, E.imageUrl, l, c, n.objectId)),
fs.unlinkSync(_),
console.log('New Signed PDF created called: ' + _),
'success' === A.message
? { status: 'success', data: M.imageUrl }
: { status: 'error', message: 'Please provide required parameters!' }
);
'success' === j.message)
)
return { status: 'success', data: E.imageUrl };
throw (((A = new Error('Please provide required parameters!')).code = 400), A);
}
}
}
} catch (e) {
return (
console.log('Err ', e),
'ERR_BAD_REQUEST' === e.code
? { status: 'error', message: 'Invalid session token!' }
: { status: 'error', message: 'Encrypted files are currently not supported!' }
);
throw (console.log('Err in signpdf', e), e);
}
}
export default PDF;
@@ -1,5 +1,5 @@
import axios from 'axios';
export default async function saveSubscriptio(request) {
export default async function saveSubscription(request) {
const serverUrl = process.env.SERVER_URL;
const appId = process.env.APP_ID;
const subscription = request.params.subscription;