diff --git a/apps/OpenSign/src/constant/Utils.js b/apps/OpenSign/src/constant/Utils.js index 67b399d03..ab4f4e9a6 100644 --- a/apps/OpenSign/src/constant/Utils.js +++ b/apps/OpenSign/src/constant/Utils.js @@ -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) { diff --git a/apps/OpenSign/src/pages/Form.js b/apps/OpenSign/src/pages/Form.js index 1a53c6da7..6af4107ac 100644 --- a/apps/OpenSign/src/pages/Form.js +++ b/apps/OpenSign/src/pages/Form.js @@ -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 && ( diff --git a/apps/OpenSign/src/pages/PdfRequestFiles.js b/apps/OpenSign/src/pages/PdfRequestFiles.js index c3ea8300c..50be8ced4 100644 --- a/apps/OpenSign/src/pages/PdfRequestFiles.js +++ b/apps/OpenSign/src/pages/PdfRequestFiles.js @@ -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 = "" + 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} diff --git a/apps/OpenSign/src/pages/SignyourselfPdf.js b/apps/OpenSign/src/pages/SignyourselfPdf.js index 74772ce70..2b276d776 100644 --- a/apps/OpenSign/src/pages/SignyourselfPdf.js +++ b/apps/OpenSign/src/pages/SignyourselfPdf.js @@ -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 diff --git a/apps/OpenSign/src/pages/UserProfile.js b/apps/OpenSign/src/pages/UserProfile.js index eaeafffaa..69b491468 100644 --- a/apps/OpenSign/src/pages/UserProfile.js +++ b/apps/OpenSign/src/pages/UserProfile.js @@ -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 ( @@ -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> diff --git a/apps/OpenSignServer/cloud/main.js b/apps/OpenSignServer/cloud/main.js index 47869fadc..652e69e53 100644 --- a/apps/OpenSignServer/cloud/main.js +++ b/apps/OpenSignServer/cloud/main.js @@ -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) diff --git a/apps/OpenSignServer/cloud/parsefunction/VerifyEmail.js b/apps/OpenSignServer/cloud/parsefunction/VerifyEmail.js new file mode 100644 index 000000000..8c1f08d32 --- /dev/null +++ b/apps/OpenSignServer/cloud/parsefunction/VerifyEmail.js @@ -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; + } +} diff --git a/apps/OpenSignServer/cloud/parsefunction/callWebhook.js b/apps/OpenSignServer/cloud/parsefunction/callWebhook.js index 7e2fa4056..a9d127c36 100644 --- a/apps/OpenSignServer/cloud/parsefunction/callWebhook.js +++ b/apps/OpenSignServer/cloud/parsefunction/callWebhook.js @@ -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); } }); } diff --git a/apps/OpenSignServer/cloud/parsefunction/encryptedPdf.js b/apps/OpenSignServer/cloud/parsefunction/encryptedPdf.js index 7fb6c58cf..315cf737b 100644 --- a/apps/OpenSignServer/cloud/parsefunction/encryptedPdf.js +++ b/apps/OpenSignServer/cloud/parsefunction/encryptedPdf.js @@ -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) { diff --git a/apps/OpenSignServer/cloud/parsefunction/pdf/GenerateCertificate.js b/apps/OpenSignServer/cloud/parsefunction/pdf/GenerateCertificate.js index 2b9b7625f..1d8fc2d15 100644 --- a/apps/OpenSignServer/cloud/parsefunction/pdf/GenerateCertificate.js +++ b/apps/OpenSignServer/cloud/parsefunction/pdf/GenerateCertificate.js @@ -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; } diff --git a/apps/OpenSignServer/cloud/parsefunction/pdf/PDF.min.js b/apps/OpenSignServer/cloud/parsefunction/pdf/PDF.min.js index 6b249f797..fec7ab4ef 100644 --- a/apps/OpenSignServer/cloud/parsefunction/pdf/PDF.min.js +++ b/apps/OpenSignServer/cloud/parsefunction/pdf/PDF.min.js @@ -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; diff --git a/apps/OpenSignServer/cloud/parsefunction/saveSubscription.js b/apps/OpenSignServer/cloud/parsefunction/saveSubscription.js index 9b1e262f6..2771e81c4 100644 --- a/apps/OpenSignServer/cloud/parsefunction/saveSubscription.js +++ b/apps/OpenSignServer/cloud/parsefunction/saveSubscription.js @@ -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;