From 3599fbee21cb5d2140a5e646938a36d330fe3275 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs Date: Mon, 29 Apr 2024 17:38:25 +0530 Subject: [PATCH 01/38] fix: changes in certificate --- apps/OpenSign/src/constant/Utils.js | 3 +- apps/OpenSign/src/pages/PdfRequestFiles.js | 7 +- apps/OpenSign/src/pages/SignyourselfPdf.js | 11 +- .../cloud/parsefunction/callWebhook.js | 41 +- .../parsefunction/pdf/GenerateCertificate.js | 406 ++++++++++++++-- .../cloud/parsefunction/pdf/PDF.min.js | 432 ++++++++---------- 6 files changed, 612 insertions(+), 288 deletions(-) diff --git a/apps/OpenSign/src/constant/Utils.js b/apps/OpenSign/src/constant/Utils.js index 67b399d03..1f28db8f1 100644 --- a/apps/OpenSign/src/constant/Utils.js +++ b/apps/OpenSign/src/constant/Utils.js @@ -572,7 +572,8 @@ export const signPdfFun = async ( headers: { "Content-Type": "application/json", "X-Parse-Application-Id": localStorage.getItem("parseAppId"), - sessionToken: localStorage.getItem("accesstoken") + // sessionToken: localStorage.getItem("accesstoken") + "X-Parse-Session-Token": localStorage.getItem("accesstoken") } }) .then((Listdata) => { diff --git a/apps/OpenSign/src/pages/PdfRequestFiles.js b/apps/OpenSign/src/pages/PdfRequestFiles.js index 4f433ba2f..435d613f1 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, @@ -1139,9 +1140,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 b14c6934c..d3f3c9aa8 100644 --- a/apps/OpenSign/src/pages/SignyourselfPdf.js +++ b/apps/OpenSign/src/pages/SignyourselfPdf.js @@ -443,13 +443,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) }; @@ -626,7 +626,8 @@ function SignYourSelf() { headers: { "Content-Type": "application/json", "X-Parse-Application-Id": localStorage.getItem("parseAppId"), - sessionToken: localStorage.getItem("accesstoken") + // sessionToken: localStorage.getItem("accesstoken") + "X-Parse-Session-Token": localStorage.getItem("accesstoken") } }) .then((Listdata) => { 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/pdf/GenerateCertificate.js b/apps/OpenSignServer/cloud/parsefunction/pdf/GenerateCertificate.js index 2b9b7625f..739ea3a9a 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.SignedOn).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.SignedOn).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..a84ceb9a4 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 = "

Document signed successfully

All parties have successfully signed the document " + - `"${r}"` + + `"${i}"` + '. Kindly download the document from the attachment.

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™ here.

'; 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 ', e), e); } } export default PDF; From c279fd6525abc1d22c14421d07328b3cc16ecf78 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs Date: Mon, 29 Apr 2024 21:12:54 +0530 Subject: [PATCH 02/38] feat: add email verification in user profile --- apps/OpenSign/src/pages/UserProfile.js | 126 +++++++++++++++++- apps/OpenSignServer/cloud/main.js | 6 +- .../cloud/parsefunction/VerifyEmail.js | 49 +++++++ .../cloud/parsefunction/saveSubscription.js | 2 +- 4 files changed, 175 insertions(+), 8 deletions(-) create mode 100644 apps/OpenSignServer/cloud/parsefunction/VerifyEmail.js diff --git a/apps/OpenSign/src/pages/UserProfile.js b/apps/OpenSign/src/pages/UserProfile.js index eaeafffaa..b95d60312 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,50 @@ 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() + }); + 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 +362,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 +462,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 1833e2d30..230ee17c3 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'; Parse.Cloud.define('AddUserToRole', addUserToGroups); Parse.Cloud.define('UserGroups', getUserGroups); @@ -70,4 +71,5 @@ 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); 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/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; From 74083d0606e4630d024f57306936bc961a227048 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Wed, 1 May 2024 11:36:03 +0530 Subject: [PATCH 03/38] refactor: add encrypted pdf error event --- apps/OpenSign/src/pages/Form.js | 7 + apps/OpenSign/src/pages/PdfRequestFiles.js | 348 +++++++++--------- apps/OpenSign/src/pages/SignyourselfPdf.js | 53 +-- apps/OpenSignServer/cloud/main.js | 2 + .../cloud/parsefunction/encryptedPdf.js | 14 + 5 files changed, 232 insertions(+), 192 deletions(-) create mode 100644 apps/OpenSignServer/cloud/parsefunction/encryptedPdf.js diff --git a/apps/OpenSign/src/pages/Form.js b/apps/OpenSign/src/pages/Form.js index 3dfee1aa9..1a53c6da7 100644 --- a/apps/OpenSign/src/pages/Form.js +++ b/apps/OpenSign/src/pages/Form.js @@ -96,6 +96,13 @@ const Forms = (props) => { 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 { diff --git a/apps/OpenSign/src/pages/PdfRequestFiles.js b/apps/OpenSign/src/pages/PdfRequestFiles.js index e3ec3012f..c3ea8300c 100644 --- a/apps/OpenSign/src/pages/PdfRequestFiles.js +++ b/apps/OpenSign/src/pages/PdfRequestFiles.js @@ -570,192 +570,200 @@ function PdfRequestFiles() { const pngUrl = checkUser[0].placeHolder; // Load a PDFDocument from the existing PDF bytes const existingPdfBytes = pdfArrayBuffer; - const pdfDoc = await PDFDocument.load(existingPdfBytes, { - ignoreEncryption: true - }); - const isSignYourSelfFlow = false; - const extUserPtr = pdfDetails[0].ExtUserPtr; - const HeaderDocId = extUserPtr?.HeaderDocId; - //embed document's object id to all pages in pdf document - if (!HeaderDocId) { - if (!isDocId) { - await embedDocId(pdfDoc, documentId, allPages); - } - } - //embed multi signature in pdf - const pdfBytes = await multiSignEmbed( - pngUrl, - pdfDoc, - pdfOriginalWidth, - isSignYourSelfFlow, - containerWH - ); - //get ExistUserPtr object id of user class to get tenantDetails - const objectId = pdfDetails?.[0]?.ExtUserPtr?.UserId?.objectId; - //get ExistUserPtr email to get userDetails - const currentUserEmail = pdfDetails?.[0]?.ExtUserPtr?.Email; - const res = await contractUsers(currentUserEmail); - let activeMailAdapter = ""; - if (res === "Error: Something went wrong!") { - setHandleError("Error: Something went wrong!"); - setIsLoading({ - isLoad: false - }); - } else if (!res || res?.length === 0) { - activeMailAdapter = ""; - } else if (res[0] && res.length) { - activeMailAdapter = res[0]?.active_mail_adapter; - } - //function for call to embed signature in pdf and get digital signature pdf try { - const res = await signPdfFun( - pdfBytes, - documentId, - signerObjectId, - setIsAlert, - objectId, - isSubscribed, - activeMailAdapter - ); - if (res && res.status === "success") { - setPdfUrl(res.data); - setIsSigned(true); - setSignedSigners([]); - setUnSignedSigners([]); - getDocumentDetails(); - const index = pdfDetails?.[0].Signers.findIndex( - (x) => x.Email === jsonSender.email - ); - const newIndex = index + 1; - const user = pdfDetails?.[0].Signers[newIndex]; - if (user) { - setIsCompleted({ - isModal: true, - message: - "You have successfully signed the document. You can download or print a copy of the partially signed document. A copy of the digitally signed document will be sent to the owner over email once it is signed by all signers." - }); + const pdfDoc = await PDFDocument.load(existingPdfBytes); + + const isSignYourSelfFlow = false; + const extUserPtr = pdfDetails[0].ExtUserPtr; + const HeaderDocId = extUserPtr?.HeaderDocId; + //embed document's object id to all pages in pdf document + if (!HeaderDocId) { + if (!isDocId) { + await embedDocId(pdfDoc, documentId, allPages); } - if (sendInOrder) { - const requestBody = pdfDetails?.[0]?.RequestBody; - const requestSubject = pdfDetails?.[0]?.RequestSubject; + } + //embed multi signature in pdf + const pdfBytes = await multiSignEmbed( + pngUrl, + pdfDoc, + pdfOriginalWidth, + isSignYourSelfFlow, + containerWH + ); + //get ExistUserPtr object id of user class to get tenantDetails + const objectId = pdfDetails?.[0]?.ExtUserPtr?.UserId?.objectId; + //get ExistUserPtr email to get userDetails + const currentUserEmail = pdfDetails?.[0]?.ExtUserPtr?.Email; + const res = await contractUsers(currentUserEmail); + let activeMailAdapter = ""; + if (res === "Error: Something went wrong!") { + setHandleError("Error: Something went wrong!"); + setIsLoading({ + isLoad: false + }); + } else if (!res || res?.length === 0) { + activeMailAdapter = ""; + } else if (res[0] && res.length) { + activeMailAdapter = res[0]?.active_mail_adapter; + } + //function for call to embed signature in pdf and get digital signature pdf + try { + const res = await signPdfFun( + pdfBytes, + documentId, + signerObjectId, + setIsAlert, + objectId, + isSubscribed, + activeMailAdapter + ); + if (res && res.status === "success") { + setPdfUrl(res.data); + setIsSigned(true); + setSignedSigners([]); + setUnSignedSigners([]); + getDocumentDetails(); + const index = pdfDetails?.[0].Signers.findIndex( + (x) => x.Email === jsonSender.email + ); + const newIndex = index + 1; + const user = pdfDetails?.[0].Signers[newIndex]; 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" + setIsCompleted({ + isModal: true, + message: + "You have successfully signed the document. You can download or print a copy of the partially signed document. A copy of the digitally signed document will be sent to the owner over email once it is signed by all signers." }); - let senderEmail = pdfDetails?.[0].ExtUserPtr.Email; - let senderPhone = pdfDetails?.[0]?.ExtUserPtr?.Phone; - const senderName = `${pdfDetails?.[0].ExtUserPtr.Name}`; - - try { - const imgPng = - "https://qikinnovation.ams3.digitaloceanspaces.com/logo.png"; - let url = `${localStorage.getItem( - "baseUrl" - )}functions/sendmailv3/`; - const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": - localStorage.getItem("parseAppId"), - sessionToken: localStorage.getItem("accesstoken") - }; - const serverUrl = localStorage.getItem("baseUrl"); - const newServer = serverUrl.replaceAll("/", "%2F"); - const objectId = user.objectId; - const serverParams = `${newServer}&${localStorage.getItem( - "parseAppId" - )}&${localStorage.getItem("_appName")}`; - const hostUrl = window.location.origin; - let signPdf = `${hostUrl}/login/${pdfDetails?.[0].objectId}/${user.Email}/${objectId}/${serverParams}`; - const openSignUrl = "https://www.opensignlabs.com/contact-us"; - const orgName = pdfDetails[0]?.ExtUserPtr.Company - ? pdfDetails[0].ExtUserPtr.Company - : ""; - const themeBGcolor = themeColor; - let replaceVar; - if ( - requestBody && - requestSubject && - (!isEnableSubscription || isSubscribed) - ) { - const replacedRequestBody = requestBody.replace(/"/g, "'"); - const htmlReqBody = - "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>" + - replacedRequestBody + - "</body> </html>"; - - const variables = { - document_title: pdfDetails?.[0].Name, - sender_name: senderName, - sender_mail: senderEmail, - sender_phone: senderPhone, - receiver_name: user.Name, - receiver_email: user.Email, - receiver_phone: user.Phone, - expiry_date: localExpireDate, - company_name: orgName, - signing_url: `<a href=${signPdf}>Sign here</a>` - }; - replaceVar = replaceMailVaribles( - requestSubject, - htmlReqBody, - variables - ); - } - - let params = { - mailProvider: activeMailAdapter, - extUserId: extUserId, - recipient: user.Email, - subject: requestSubject - ? replaceVar?.subject - : `${pdfDetails?.[0].ExtUserPtr.Name} has requested you to sign ${pdfDetails?.[0].Name}`, - from: senderEmail, - html: requestBody - ? replaceVar?.body - : "<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: white;padding-bottom: 20px;'> <div style='padding:10px 10px 0 10px'><img src=" + - imgPng + - " height='50' style='padding: 20px,width:170px,height:40px' /></div> <div style=' padding: 2px;font-family: system-ui;background-color:" + - themeBGcolor + - ";'><p style='font-size: 20px;font-weight: 400;color: white;padding-left: 20px;' > Digital Signature Request</p></div><div><p style='padding: 20px;font-family: system-ui;font-size: 14px; margin-bottom: 10px;'> " + - pdfDetails?.[0].ExtUserPtr.Name + - " has requested you to review and sign <strong> " + - pdfDetails?.[0].Name + - "</strong>.</p><div style='padding: 5px 0px 5px 25px;display: flex;flex-direction: row;justify-content: space-around;'><table> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Sender</td> <td> </td> <td style='color:#626363;font-weight:bold'>" + - senderEmail + - "</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Organization</td> <td> </td><td style='color:#626363;font-weight:bold'> " + - orgName + - "</td></tr> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expires on</td><td> </td> <td style='color:#626363;font-weight:bold'>" + - localExpireDate + - "</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a href=" + - signPdf + - "> <button style='padding: 12px 12px 12px 12px;background-color: #d46b0f;color: white; border: 0px;box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;font-weight:bold;margin-top:30px'>Sign here</button></a> </div> <div style='display: flex; justify-content: center;margin-top: 10px;'> </div></div></div><div><p> This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " + - senderEmail + - " directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href= " + - openSignUrl + - " target=_blank>here</a>.</p> </div></div></body> </html>" - }; - await axios.post(url, params, { - headers: headers + } + if (sendInOrder) { + const requestBody = pdfDetails?.[0]?.RequestBody; + const requestSubject = pdfDetails?.[0]?.RequestSubject; + 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" }); - } catch (error) { - console.log("error", error); + let senderEmail = pdfDetails?.[0].ExtUserPtr.Email; + let senderPhone = pdfDetails?.[0]?.ExtUserPtr?.Phone; + const senderName = `${pdfDetails?.[0].ExtUserPtr.Name}`; + + try { + const imgPng = + "https://qikinnovation.ams3.digitaloceanspaces.com/logo.png"; + let url = `${localStorage.getItem( + "baseUrl" + )}functions/sendmailv3/`; + const headers = { + "Content-Type": "application/json", + "X-Parse-Application-Id": + localStorage.getItem("parseAppId"), + sessionToken: localStorage.getItem("accesstoken") + }; + const serverUrl = localStorage.getItem("baseUrl"); + const newServer = serverUrl.replaceAll("/", "%2F"); + const objectId = user.objectId; + const serverParams = `${newServer}&${localStorage.getItem( + "parseAppId" + )}&${localStorage.getItem("_appName")}`; + const hostUrl = window.location.origin; + let signPdf = `${hostUrl}/login/${pdfDetails?.[0].objectId}/${user.Email}/${objectId}/${serverParams}`; + const openSignUrl = + "https://www.opensignlabs.com/contact-us"; + const orgName = pdfDetails[0]?.ExtUserPtr.Company + ? pdfDetails[0].ExtUserPtr.Company + : ""; + const themeBGcolor = themeColor; + let replaceVar; + if ( + requestBody && + requestSubject && + (!isEnableSubscription || isSubscribed) + ) { + const replacedRequestBody = requestBody.replace(/"/g, "'"); + const htmlReqBody = + "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>" + + replacedRequestBody + + "</body> </html>"; + + const variables = { + document_title: pdfDetails?.[0].Name, + sender_name: senderName, + sender_mail: senderEmail, + sender_phone: senderPhone, + receiver_name: user.Name, + receiver_email: user.Email, + receiver_phone: user.Phone, + expiry_date: localExpireDate, + company_name: orgName, + signing_url: `<a href=${signPdf}>Sign here</a>` + }; + replaceVar = replaceMailVaribles( + requestSubject, + htmlReqBody, + variables + ); + } + + let params = { + mailProvider: activeMailAdapter, + extUserId: extUserId, + recipient: user.Email, + subject: requestSubject + ? replaceVar?.subject + : `${pdfDetails?.[0].ExtUserPtr.Name} has requested you to sign ${pdfDetails?.[0].Name}`, + from: senderEmail, + html: requestBody + ? replaceVar?.body + : "<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: white;padding-bottom: 20px;'> <div style='padding:10px 10px 0 10px'><img src=" + + imgPng + + " height='50' style='padding: 20px,width:170px,height:40px' /></div> <div style=' padding: 2px;font-family: system-ui;background-color:" + + themeBGcolor + + ";'><p style='font-size: 20px;font-weight: 400;color: white;padding-left: 20px;' > Digital Signature Request</p></div><div><p style='padding: 20px;font-family: system-ui;font-size: 14px; margin-bottom: 10px;'> " + + pdfDetails?.[0].ExtUserPtr.Name + + " has requested you to review and sign <strong> " + + pdfDetails?.[0].Name + + "</strong>.</p><div style='padding: 5px 0px 5px 25px;display: flex;flex-direction: row;justify-content: space-around;'><table> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Sender</td> <td> </td> <td style='color:#626363;font-weight:bold'>" + + senderEmail + + "</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Organization</td> <td> </td><td style='color:#626363;font-weight:bold'> " + + orgName + + "</td></tr> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expires on</td><td> </td> <td style='color:#626363;font-weight:bold'>" + + localExpireDate + + "</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a href=" + + signPdf + + "> <button style='padding: 12px 12px 12px 12px;background-color: #d46b0f;color: white; border: 0px;box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;font-weight:bold;margin-top:30px'>Sign here</button></a> </div> <div style='display: flex; justify-content: center;margin-top: 10px;'> </div></div></div><div><p> This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " + + senderEmail + + " directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href= " + + openSignUrl + + " target=_blank>here</a>.</p> </div></div></body> </html>" + }; + await axios.post(url, params, { + headers: headers + }); + } catch (error) { + console.log("error", error); + } } } + } else { + setIsAlert({ + isShow: true, + alertMessage: "something went wrong" + }); } - } else { + } catch (err) { setIsAlert({ isShow: true, alertMessage: "something went wrong" }); } } catch (err) { + setIsUiLoading(false); setIsAlert({ isShow: true, - alertMessage: "something went wrong" + alertMessage: `Currently encrypted pdf files are not supported.` }); } } diff --git a/apps/OpenSign/src/pages/SignyourselfPdf.js b/apps/OpenSign/src/pages/SignyourselfPdf.js index 1babe9128..74772ce70 100644 --- a/apps/OpenSign/src/pages/SignyourselfPdf.js +++ b/apps/OpenSign/src/pages/SignyourselfPdf.js @@ -574,30 +574,38 @@ function SignYourSelf() { setIsUiLoading(true); const existingPdfBytes = pdfArrayBuffer; // Load a PDFDocument from the existing PDF bytes - const pdfDoc = await PDFDocument.load(existingPdfBytes, { - ignoreEncryption: true - }); - const isSignYourSelfFlow = true; - const extUserPtr = pdfDetails[0].ExtUserPtr; - const HeaderDocId = extUserPtr?.HeaderDocId; - //embed document's object id to all pages in pdf document - if (!HeaderDocId) { - await embedDocId(pdfDoc, documentId, allPages); + try { + const pdfDoc = await PDFDocument.load(existingPdfBytes, { + ignoreEncryption: true + }); + const isSignYourSelfFlow = true; + const extUserPtr = pdfDetails[0].ExtUserPtr; + const HeaderDocId = extUserPtr?.HeaderDocId; + //embed document's object id to all pages in pdf document + if (!HeaderDocId) { + await embedDocId(pdfDoc, documentId, allPages); + } + //embed multi signature in pdf + const pdfBytes = await multiSignEmbed( + xyPostion, + pdfDoc, + pdfOriginalWidth, + isSignYourSelfFlow, + containerWH + ); + // console.log("pdf", pdfBytes); + //function for call to embed signature in pdf and get digital signature pdf + await signPdfFun(pdfBytes, documentId); + } catch (err) { + setIsUiLoading(false); + setIsAlert({ + isShow: true, + alertMessage: `Currently encrypted pdf files are not supported.` + }); } - //embed multi signature in pdf - const pdfBytes = await multiSignEmbed( - xyPostion, - pdfDoc, - pdfOriginalWidth, - isSignYourSelfFlow, - containerWH - ); - // console.log("pdf", pdfBytes); - //function for call to embed signature in pdf and get digital signature pdf - await signPdfFun(pdfBytes, documentId); } } catch (err) { - console.log("err in embedselfsign ", err ); + console.log("err in embedselfsign ", err); setIsUiLoading(false); setIsAlert({ isShow: true, @@ -635,7 +643,8 @@ function SignYourSelf() { headers: { "Content-Type": "application/json", "X-Parse-Application-Id": localStorage.getItem("parseAppId"), - sessionToken: localStorage.getItem("accesstoken") + // sessionToken: localStorage.getItem("accesstoken") + "X-Parse-Session-Token": localStorage.getItem("accesstoken") } }) .then((Listdata) => { diff --git a/apps/OpenSignServer/cloud/main.js b/apps/OpenSignServer/cloud/main.js index 1833e2d30..47869fadc 100644 --- a/apps/OpenSignServer/cloud/main.js +++ b/apps/OpenSignServer/cloud/main.js @@ -34,6 +34,7 @@ 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 encryptedpdf from './parsefunction/encryptedPdf.js'; Parse.Cloud.define('AddUserToRole', addUserToGroups); Parse.Cloud.define('UserGroups', getUserGroups); @@ -71,3 +72,4 @@ 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('encryptedpdf', encryptedpdf) diff --git a/apps/OpenSignServer/cloud/parsefunction/encryptedPdf.js b/apps/OpenSignServer/cloud/parsefunction/encryptedPdf.js new file mode 100644 index 000000000..7fb6c58cf --- /dev/null +++ b/apps/OpenSignServer/cloud/parsefunction/encryptedPdf.js @@ -0,0 +1,14 @@ +import { PostHog } from 'posthog-node'; +const ph_project_api_key = process.env.PH_PROJECT_API_KEY; +const client = new PostHog(ph_project_api_key); +export default async function encryptedpdf(request) { + const email = request.params.email; + if (client) { + client?.capture({ + distinctId: email, + event: 'encrypted_pdf_error', + properties: { response_code: 200 }, + }); + } + return { message: 'success' }; +} From 41e8aa18a78678535cf03f696352f327d46f4b57 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Fri, 3 May 2024 13:20:31 +0530 Subject: [PATCH 04/38] feat: add pdftopdf and wordtopdf functionality --- apps/OpenSign/src/pages/Form.js | 130 ++++++- apps/OpenSign/src/pages/PdfRequestFiles.js | 348 +++++++++--------- apps/OpenSign/src/pages/SignyourselfPdf.js | 50 +-- apps/OpenSign/src/pages/UserProfile.js | 8 +- apps/OpenSignServer/cloud/main.js | 2 + .../cloud/parsefunction/encryptedPdf.js | 14 + .../cloud/parsefunction/pdf/PDF.min.js | 2 +- 7 files changed, 349 insertions(+), 205 deletions(-) create mode 100644 apps/OpenSignServer/cloud/parsefunction/encryptedPdf.js diff --git a/apps/OpenSign/src/pages/Form.js b/apps/OpenSign/src/pages/Form.js index 3dfee1aa9..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,16 +90,113 @@ 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 { + const res = await getFileAsArrayBuffer(files[0]); + await PDFDocument.load(res); + handleFileUpload(files[0]); + } catch (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); + } + } + } } } } else { @@ -353,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 fa6ebeb04..94977c454 100644 --- a/apps/OpenSign/src/pages/PdfRequestFiles.js +++ b/apps/OpenSign/src/pages/PdfRequestFiles.js @@ -571,192 +571,200 @@ function PdfRequestFiles() { const pngUrl = checkUser[0].placeHolder; // Load a PDFDocument from the existing PDF bytes const existingPdfBytes = pdfArrayBuffer; - const pdfDoc = await PDFDocument.load(existingPdfBytes, { - ignoreEncryption: true - }); - const isSignYourSelfFlow = false; - const extUserPtr = pdfDetails[0].ExtUserPtr; - const HeaderDocId = extUserPtr?.HeaderDocId; - //embed document's object id to all pages in pdf document - if (!HeaderDocId) { - if (!isDocId) { - await embedDocId(pdfDoc, documentId, allPages); - } - } - //embed multi signature in pdf - const pdfBytes = await multiSignEmbed( - pngUrl, - pdfDoc, - pdfOriginalWidth, - isSignYourSelfFlow, - containerWH - ); - //get ExistUserPtr object id of user class to get tenantDetails - const objectId = pdfDetails?.[0]?.ExtUserPtr?.UserId?.objectId; - //get ExistUserPtr email to get userDetails - const currentUserEmail = pdfDetails?.[0]?.ExtUserPtr?.Email; - const res = await contractUsers(currentUserEmail); - let activeMailAdapter = ""; - if (res === "Error: Something went wrong!") { - setHandleError("Error: Something went wrong!"); - setIsLoading({ - isLoad: false - }); - } else if (!res || res?.length === 0) { - activeMailAdapter = ""; - } else if (res[0] && res.length) { - activeMailAdapter = res[0]?.active_mail_adapter; - } - //function for call to embed signature in pdf and get digital signature pdf try { - const res = await signPdfFun( - pdfBytes, - documentId, - signerObjectId, - setIsAlert, - objectId, - isSubscribed, - activeMailAdapter - ); - if (res && res.status === "success") { - setPdfUrl(res.data); - setIsSigned(true); - setSignedSigners([]); - setUnSignedSigners([]); - getDocumentDetails(); - const index = pdfDetails?.[0].Signers.findIndex( - (x) => x.Email === jsonSender.email - ); - const newIndex = index + 1; - const user = pdfDetails?.[0].Signers[newIndex]; - if (user) { - setIsCompleted({ - isModal: true, - message: - "You have successfully signed the document. You can download or print a copy of the partially signed document. A copy of the digitally signed document will be sent to the owner over email once it is signed by all signers." - }); + const pdfDoc = await PDFDocument.load(existingPdfBytes); + + const isSignYourSelfFlow = false; + const extUserPtr = pdfDetails[0].ExtUserPtr; + const HeaderDocId = extUserPtr?.HeaderDocId; + //embed document's object id to all pages in pdf document + if (!HeaderDocId) { + if (!isDocId) { + await embedDocId(pdfDoc, documentId, allPages); } - if (sendInOrder) { - const requestBody = pdfDetails?.[0]?.RequestBody; - const requestSubject = pdfDetails?.[0]?.RequestSubject; + } + //embed multi signature in pdf + const pdfBytes = await multiSignEmbed( + pngUrl, + pdfDoc, + pdfOriginalWidth, + isSignYourSelfFlow, + containerWH + ); + //get ExistUserPtr object id of user class to get tenantDetails + const objectId = pdfDetails?.[0]?.ExtUserPtr?.UserId?.objectId; + //get ExistUserPtr email to get userDetails + const currentUserEmail = pdfDetails?.[0]?.ExtUserPtr?.Email; + const res = await contractUsers(currentUserEmail); + let activeMailAdapter = ""; + if (res === "Error: Something went wrong!") { + setHandleError("Error: Something went wrong!"); + setIsLoading({ + isLoad: false + }); + } else if (!res || res?.length === 0) { + activeMailAdapter = ""; + } else if (res[0] && res.length) { + activeMailAdapter = res[0]?.active_mail_adapter; + } + //function for call to embed signature in pdf and get digital signature pdf + try { + const res = await signPdfFun( + pdfBytes, + documentId, + signerObjectId, + setIsAlert, + objectId, + isSubscribed, + activeMailAdapter + ); + if (res && res.status === "success") { + setPdfUrl(res.data); + setIsSigned(true); + setSignedSigners([]); + setUnSignedSigners([]); + getDocumentDetails(); + const index = pdfDetails?.[0].Signers.findIndex( + (x) => x.Email === jsonSender.email + ); + const newIndex = index + 1; + const user = pdfDetails?.[0].Signers[newIndex]; 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" + setIsCompleted({ + isModal: true, + message: + "You have successfully signed the document. You can download or print a copy of the partially signed document. A copy of the digitally signed document will be sent to the owner over email once it is signed by all signers." }); - let senderEmail = pdfDetails?.[0].ExtUserPtr.Email; - let senderPhone = pdfDetails?.[0]?.ExtUserPtr?.Phone; - const senderName = `${pdfDetails?.[0].ExtUserPtr.Name}`; - - try { - const imgPng = - "https://qikinnovation.ams3.digitaloceanspaces.com/logo.png"; - let url = `${localStorage.getItem( - "baseUrl" - )}functions/sendmailv3/`; - const headers = { - "Content-Type": "application/json", - "X-Parse-Application-Id": - localStorage.getItem("parseAppId"), - sessionToken: localStorage.getItem("accesstoken") - }; - const serverUrl = localStorage.getItem("baseUrl"); - const newServer = serverUrl.replaceAll("/", "%2F"); - const objectId = user.objectId; - const serverParams = `${newServer}&${localStorage.getItem( - "parseAppId" - )}&${localStorage.getItem("_appName")}`; - const hostUrl = window.location.origin; - let signPdf = `${hostUrl}/login/${pdfDetails?.[0].objectId}/${user.Email}/${objectId}/${serverParams}`; - const openSignUrl = "https://www.opensignlabs.com/contact-us"; - const orgName = pdfDetails[0]?.ExtUserPtr.Company - ? pdfDetails[0].ExtUserPtr.Company - : ""; - const themeBGcolor = themeColor; - let replaceVar; - if ( - requestBody && - requestSubject && - (!isEnableSubscription || isSubscribed) - ) { - const replacedRequestBody = requestBody.replace(/"/g, "'"); - const htmlReqBody = - "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>" + - replacedRequestBody + - "</body> </html>"; - - const variables = { - document_title: pdfDetails?.[0].Name, - sender_name: senderName, - sender_mail: senderEmail, - sender_phone: senderPhone, - receiver_name: user.Name, - receiver_email: user.Email, - receiver_phone: user.Phone, - expiry_date: localExpireDate, - company_name: orgName, - signing_url: `<a href=${signPdf}>Sign here</a>` - }; - replaceVar = replaceMailVaribles( - requestSubject, - htmlReqBody, - variables - ); - } - - let params = { - mailProvider: activeMailAdapter, - extUserId: extUserId, - recipient: user.Email, - subject: requestSubject - ? replaceVar?.subject - : `${pdfDetails?.[0].ExtUserPtr.Name} has requested you to sign ${pdfDetails?.[0].Name}`, - from: senderEmail, - html: requestBody - ? replaceVar?.body - : "<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: white;padding-bottom: 20px;'> <div style='padding:10px 10px 0 10px'><img src=" + - imgPng + - " height='50' style='padding: 20px,width:170px,height:40px' /></div> <div style=' padding: 2px;font-family: system-ui;background-color:" + - themeBGcolor + - ";'><p style='font-size: 20px;font-weight: 400;color: white;padding-left: 20px;' > Digital Signature Request</p></div><div><p style='padding: 20px;font-family: system-ui;font-size: 14px; margin-bottom: 10px;'> " + - pdfDetails?.[0].ExtUserPtr.Name + - " has requested you to review and sign <strong> " + - pdfDetails?.[0].Name + - "</strong>.</p><div style='padding: 5px 0px 5px 25px;display: flex;flex-direction: row;justify-content: space-around;'><table> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Sender</td> <td> </td> <td style='color:#626363;font-weight:bold'>" + - senderEmail + - "</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Organization</td> <td> </td><td style='color:#626363;font-weight:bold'> " + - orgName + - "</td></tr> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expires on</td><td> </td> <td style='color:#626363;font-weight:bold'>" + - localExpireDate + - "</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a href=" + - signPdf + - "> <button style='padding: 12px 12px 12px 12px;background-color: #d46b0f;color: white; border: 0px;box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;font-weight:bold;margin-top:30px'>Sign here</button></a> </div> <div style='display: flex; justify-content: center;margin-top: 10px;'> </div></div></div><div><p> This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " + - senderEmail + - " directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href= " + - openSignUrl + - " target=_blank>here</a>.</p> </div></div></body> </html>" - }; - await axios.post(url, params, { - headers: headers + } + if (sendInOrder) { + const requestBody = pdfDetails?.[0]?.RequestBody; + const requestSubject = pdfDetails?.[0]?.RequestSubject; + 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" }); - } catch (error) { - console.log("error", error); + let senderEmail = pdfDetails?.[0].ExtUserPtr.Email; + let senderPhone = pdfDetails?.[0]?.ExtUserPtr?.Phone; + const senderName = `${pdfDetails?.[0].ExtUserPtr.Name}`; + + try { + const imgPng = + "https://qikinnovation.ams3.digitaloceanspaces.com/logo.png"; + let url = `${localStorage.getItem( + "baseUrl" + )}functions/sendmailv3/`; + const headers = { + "Content-Type": "application/json", + "X-Parse-Application-Id": + localStorage.getItem("parseAppId"), + sessionToken: localStorage.getItem("accesstoken") + }; + const serverUrl = localStorage.getItem("baseUrl"); + const newServer = serverUrl.replaceAll("/", "%2F"); + const objectId = user.objectId; + const serverParams = `${newServer}&${localStorage.getItem( + "parseAppId" + )}&${localStorage.getItem("_appName")}`; + const hostUrl = window.location.origin; + let signPdf = `${hostUrl}/login/${pdfDetails?.[0].objectId}/${user.Email}/${objectId}/${serverParams}`; + const openSignUrl = + "https://www.opensignlabs.com/contact-us"; + const orgName = pdfDetails[0]?.ExtUserPtr.Company + ? pdfDetails[0].ExtUserPtr.Company + : ""; + const themeBGcolor = themeColor; + let replaceVar; + if ( + requestBody && + requestSubject && + (!isEnableSubscription || isSubscribed) + ) { + const replacedRequestBody = requestBody.replace(/"/g, "'"); + const htmlReqBody = + "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>" + + replacedRequestBody + + "</body> </html>"; + + const variables = { + document_title: pdfDetails?.[0].Name, + sender_name: senderName, + sender_mail: senderEmail, + sender_phone: senderPhone, + receiver_name: user.Name, + receiver_email: user.Email, + receiver_phone: user.Phone, + expiry_date: localExpireDate, + company_name: orgName, + signing_url: `<a href=${signPdf}>Sign here</a>` + }; + replaceVar = replaceMailVaribles( + requestSubject, + htmlReqBody, + variables + ); + } + + let params = { + mailProvider: activeMailAdapter, + extUserId: extUserId, + recipient: user.Email, + subject: requestSubject + ? replaceVar?.subject + : `${pdfDetails?.[0].ExtUserPtr.Name} has requested you to sign ${pdfDetails?.[0].Name}`, + from: senderEmail, + html: requestBody + ? replaceVar?.body + : "<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: white;padding-bottom: 20px;'> <div style='padding:10px 10px 0 10px'><img src=" + + imgPng + + " height='50' style='padding: 20px,width:170px,height:40px' /></div> <div style=' padding: 2px;font-family: system-ui;background-color:" + + themeBGcolor + + ";'><p style='font-size: 20px;font-weight: 400;color: white;padding-left: 20px;' > Digital Signature Request</p></div><div><p style='padding: 20px;font-family: system-ui;font-size: 14px; margin-bottom: 10px;'> " + + pdfDetails?.[0].ExtUserPtr.Name + + " has requested you to review and sign <strong> " + + pdfDetails?.[0].Name + + "</strong>.</p><div style='padding: 5px 0px 5px 25px;display: flex;flex-direction: row;justify-content: space-around;'><table> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Sender</td> <td> </td> <td style='color:#626363;font-weight:bold'>" + + senderEmail + + "</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Organization</td> <td> </td><td style='color:#626363;font-weight:bold'> " + + orgName + + "</td></tr> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expires on</td><td> </td> <td style='color:#626363;font-weight:bold'>" + + localExpireDate + + "</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a href=" + + signPdf + + "> <button style='padding: 12px 12px 12px 12px;background-color: #d46b0f;color: white; border: 0px;box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;font-weight:bold;margin-top:30px'>Sign here</button></a> </div> <div style='display: flex; justify-content: center;margin-top: 10px;'> </div></div></div><div><p> This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " + + senderEmail + + " directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href= " + + openSignUrl + + " target=_blank>here</a>.</p> </div></div></body> </html>" + }; + await axios.post(url, params, { + headers: headers + }); + } catch (error) { + console.log("error", error); + } } } + } else { + setIsAlert({ + isShow: true, + alertMessage: "something went wrong" + }); } - } else { + } catch (err) { setIsAlert({ isShow: true, alertMessage: "something went wrong" }); } } catch (err) { + setIsUiLoading(false); setIsAlert({ isShow: true, - alertMessage: "something went wrong" + alertMessage: `Currently encrypted pdf files are not supported.` }); } } diff --git a/apps/OpenSign/src/pages/SignyourselfPdf.js b/apps/OpenSign/src/pages/SignyourselfPdf.js index 13f785405..74772ce70 100644 --- a/apps/OpenSign/src/pages/SignyourselfPdf.js +++ b/apps/OpenSign/src/pages/SignyourselfPdf.js @@ -574,30 +574,38 @@ function SignYourSelf() { setIsUiLoading(true); const existingPdfBytes = pdfArrayBuffer; // Load a PDFDocument from the existing PDF bytes - const pdfDoc = await PDFDocument.load(existingPdfBytes, { - ignoreEncryption: true - }); - const isSignYourSelfFlow = true; - const extUserPtr = pdfDetails[0].ExtUserPtr; - const HeaderDocId = extUserPtr?.HeaderDocId; - //embed document's object id to all pages in pdf document - if (!HeaderDocId) { - await embedDocId(pdfDoc, documentId, allPages); + try { + const pdfDoc = await PDFDocument.load(existingPdfBytes, { + ignoreEncryption: true + }); + const isSignYourSelfFlow = true; + const extUserPtr = pdfDetails[0].ExtUserPtr; + const HeaderDocId = extUserPtr?.HeaderDocId; + //embed document's object id to all pages in pdf document + if (!HeaderDocId) { + await embedDocId(pdfDoc, documentId, allPages); + } + //embed multi signature in pdf + const pdfBytes = await multiSignEmbed( + xyPostion, + pdfDoc, + pdfOriginalWidth, + isSignYourSelfFlow, + containerWH + ); + // console.log("pdf", pdfBytes); + //function for call to embed signature in pdf and get digital signature pdf + await signPdfFun(pdfBytes, documentId); + } catch (err) { + setIsUiLoading(false); + setIsAlert({ + isShow: true, + alertMessage: `Currently encrypted pdf files are not supported.` + }); } - //embed multi signature in pdf - const pdfBytes = await multiSignEmbed( - xyPostion, - pdfDoc, - pdfOriginalWidth, - isSignYourSelfFlow, - containerWH - ); - // console.log("pdf", pdfBytes); - //function for call to embed signature in pdf and get digital signature pdf - await signPdfFun(pdfBytes, documentId); } } catch (err) { - console.log("err in embedselfsign ", err ); + console.log("err in embedselfsign ", err); setIsUiLoading(false); setIsAlert({ isShow: true, diff --git a/apps/OpenSign/src/pages/UserProfile.js b/apps/OpenSign/src/pages/UserProfile.js index b95d60312..69b491468 100644 --- a/apps/OpenSign/src/pages/UserProfile.js +++ b/apps/OpenSign/src/pages/UserProfile.js @@ -194,7 +194,7 @@ function UserProfile() { }; const handleSendOTP = async () => { try { - let url = `${parseBaseUrl}functions/SendOTPMailV1/`; + let url = `${parseBaseUrl}functions/SendOTPMailV1`; const headers = { "Content-Type": "application/json", "X-Parse-Application-Id": parseAppId @@ -214,6 +214,12 @@ function UserProfile() { 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) { diff --git a/apps/OpenSignServer/cloud/main.js b/apps/OpenSignServer/cloud/main.js index 230ee17c3..bfdcf3fbb 100644 --- a/apps/OpenSignServer/cloud/main.js +++ b/apps/OpenSignServer/cloud/main.js @@ -34,6 +34,7 @@ import getPayments from './parsefunction/getPayments.js'; import getSubscriptions from './parsefunction/getSubscriptions.js'; import TenantAterFind from './parsefunction/TenantAfterFind.js'; import saveSubscription from './parsefunction/saveSubscription.js'; +import encryptedpdf from './parsefunction/encryptedPdf.js'; import VerifyEmail from './parsefunction/VerifyEmail.js'; Parse.Cloud.define('AddUserToRole', addUserToGroups); @@ -73,3 +74,4 @@ Parse.Cloud.afterFind('contracts_Signature', SignatureAfterFind); Parse.Cloud.afterFind('partners_Tenant', TenantAterFind); Parse.Cloud.define('savesubscription', saveSubscription); Parse.Cloud.define('verifyemail', VerifyEmail); +Parse.Cloud.define('encryptedpdf', encryptedpdf) diff --git a/apps/OpenSignServer/cloud/parsefunction/encryptedPdf.js b/apps/OpenSignServer/cloud/parsefunction/encryptedPdf.js new file mode 100644 index 000000000..7fb6c58cf --- /dev/null +++ b/apps/OpenSignServer/cloud/parsefunction/encryptedPdf.js @@ -0,0 +1,14 @@ +import { PostHog } from 'posthog-node'; +const ph_project_api_key = process.env.PH_PROJECT_API_KEY; +const client = new PostHog(ph_project_api_key); +export default async function encryptedpdf(request) { + const email = request.params.email; + if (client) { + client?.capture({ + distinctId: email, + event: 'encrypted_pdf_error', + properties: { response_code: 200 }, + }); + } + return { message: 'success' }; +} diff --git a/apps/OpenSignServer/cloud/parsefunction/pdf/PDF.min.js b/apps/OpenSignServer/cloud/parsefunction/pdf/PDF.min.js index a84ceb9a4..fec7ab4ef 100644 --- a/apps/OpenSignServer/cloud/parsefunction/pdf/PDF.min.js +++ b/apps/OpenSignServer/cloud/parsefunction/pdf/PDF.min.js @@ -319,7 +319,7 @@ async function PDF(o) { } } } catch (e) { - throw (console.log('Err ', e), e); + throw (console.log('Err in signpdf', e), e); } } export default PDF; From b421c0f1a9dc58637aa592b38f21eb42417bf0a0 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Fri, 3 May 2024 14:05:00 +0530 Subject: [PATCH 05/38] fix: remove uncessary import statement --- apps/OpenSignServer/cloud/main.js | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/OpenSignServer/cloud/main.js b/apps/OpenSignServer/cloud/main.js index 212dbd348..652e69e53 100644 --- a/apps/OpenSignServer/cloud/main.js +++ b/apps/OpenSignServer/cloud/main.js @@ -34,7 +34,6 @@ import getPayments from './parsefunction/getPayments.js'; import getSubscriptions from './parsefunction/getSubscriptions.js'; import TenantAterFind from './parsefunction/TenantAfterFind.js'; import saveSubscription from './parsefunction/saveSubscription.js'; -import encryptedpdf from './parsefunction/encryptedPdf.js'; import VerifyEmail from './parsefunction/VerifyEmail.js'; import encryptedpdf from './parsefunction/encryptedPdf.js'; From 68edecae180ddae8d7c16ac02182c784a24e9fb9 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Fri, 3 May 2024 14:26:54 +0530 Subject: [PATCH 06/38] fix: issue of encryptedpdf error api not working --- apps/OpenSignServer/cloud/parsefunction/encryptedPdf.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) { From 53bd13e03b8d5c05ed1c2ffe1f17ef61f4d5ba07 Mon Sep 17 00:00:00 2001 From: RaktimaNXG <raktima.c@nxglabs.com> Date: Fri, 3 May 2024 15:50:52 +0530 Subject: [PATCH 07/38] fix: signature is not visiable in completion certificate --- apps/OpenSign/src/constant/Utils.js | 168 +++++++++++++----- apps/OpenSign/src/pages/PdfRequestFiles.js | 30 ++-- apps/OpenSign/src/pages/SignyourselfPdf.js | 55 ++++-- .../parsefunction/pdf/GenerateCertificate.js | 8 +- 4 files changed, 187 insertions(+), 74 deletions(-) diff --git a/apps/OpenSign/src/constant/Utils.js b/apps/OpenSign/src/constant/Utils.js index 1f28db8f1..ab4f4e9a6 100644 --- a/apps/OpenSign/src/constant/Utils.js +++ b/apps/OpenSign/src/constant/Utils.js @@ -541,55 +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") - "X-Parse-Session-Token": 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 = () => { @@ -1126,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(); @@ -1154,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, @@ -1268,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/PdfRequestFiles.js b/apps/OpenSign/src/pages/PdfRequestFiles.js index 94977c454..50be8ced4 100644 --- a/apps/OpenSign/src/pages/PdfRequestFiles.js +++ b/apps/OpenSign/src/pages/PdfRequestFiles.js @@ -472,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) { @@ -616,7 +617,8 @@ function PdfRequestFiles() { setIsAlert, objectId, isSubscribed, - activeMailAdapter + activeMailAdapter, + pngUrl ); if (res && res.status === "success") { setPdfUrl(res.data); @@ -642,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}`; @@ -683,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 + @@ -1157,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/OpenSignServer/cloud/parsefunction/pdf/GenerateCertificate.js b/apps/OpenSignServer/cloud/parsefunction/pdf/GenerateCertificate.js index 739ea3a9a..1d8fc2d15 100644 --- a/apps/OpenSignServer/cloud/parsefunction/pdf/GenerateCertificate.js +++ b/apps/OpenSignServer/cloud/parsefunction/pdf/GenerateCertificate.js @@ -250,7 +250,7 @@ export default async function GenerateCertificate(docDetails) { let yPosition5 = 395; let yPosition6 = 360; auditTrail.slice(0, 3).forEach(async (x, i) => { - const embedPng = x.signature ? await pdfDoc.embedPng(x.signature) : ''; + const embedPng = x.Signature ? await pdfDoc.embedPng(x.Signature) : ''; page.drawText(`Signer ${i + 1}`, { x: 30, y: yPosition1, @@ -282,7 +282,7 @@ export default async function GenerateCertificate(docDetails) { color: textKeyColor, }); - page.drawText(`${new Date(x.SignedOn).toUTCString()}`, { + page.drawText(`${new Date(x.ViewedOn).toUTCString()}`, { x: half + 75, y: yPosition2, size: text, @@ -396,7 +396,7 @@ export default async function GenerateCertificate(docDetails) { let currentPageIndex = 1; let currentPage = page; auditTrail.slice(3).forEach(async (x, i) => { - const embedPng = x.signature ? await pdfDoc.embedPng(x.signature) : ''; + const embedPng = x.Signature ? await pdfDoc.embedPng(x.Signature) : ''; // Calculate remaining space on current page const remainingSpace = yPosition6; @@ -453,7 +453,7 @@ export default async function GenerateCertificate(docDetails) { color: textKeyColor, }); - currentPage.drawText(`${new Date(x.SignedOn).toUTCString()}`, { + currentPage.drawText(`${new Date(x.ViewedOn).toUTCString()}`, { x: half + 75, y: yPosition2, size: text, From c56830b7bed885731816dfc921dc17af28ae90da Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Fri, 3 May 2024 17:14:26 +0530 Subject: [PATCH 08/38] fix: show correct ipv4 address on completion certificate --- .../cloud/parsefunction/DocumentAftersave.js | 3 +++ .../cloud/parsefunction/pdf/GenerateCertificate.js | 3 ++- apps/OpenSignServer/index.js | 7 +------ 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/apps/OpenSignServer/cloud/parsefunction/DocumentAftersave.js b/apps/OpenSignServer/cloud/parsefunction/DocumentAftersave.js index dc26a3e09..fe8744873 100644 --- a/apps/OpenSignServer/cloud/parsefunction/DocumentAftersave.js +++ b/apps/OpenSignServer/cloud/parsefunction/DocumentAftersave.js @@ -4,6 +4,7 @@ async function DocumentAftersave(request) { console.log('new entry is insert in contracts_Document'); const createdAt = request.object.get('createdAt'); const Folder = request.object.get('Type'); + const ip = request?.headers?.['x-real-ip'] || ''; if (createdAt && Folder === undefined) { // console.log("IN If condition") const TimeToCompleteDays = request.object.get('TimeToCompleteDays'); @@ -12,6 +13,7 @@ async function DocumentAftersave(request) { const documentQuery = new Parse.Query('contracts_Document'); const updateQuery = await documentQuery.get(request.object.id, { useMasterKey: true }); updateQuery.set('ExpiryDate', ExpiryDate); + updateQuery.set('OriginIp', ip); await updateQuery.save(null, { useMasterKey: true }); } else if (createdAt && Folder === 'AIDoc') { const TimeToCompleteDays = request.object.get('TimeToCompleteDays'); @@ -24,6 +26,7 @@ async function DocumentAftersave(request) { const documentQuery = new Parse.Query('contracts_Document'); const updateQuery = await documentQuery.get(request.object.id, { useMasterKey: true }); updateQuery.set('ExpiryDate', ExpiryDate); + updateQuery.set('OriginIp', ip); await updateQuery.save(null, { useMasterKey: true }); } diff --git a/apps/OpenSignServer/cloud/parsefunction/pdf/GenerateCertificate.js b/apps/OpenSignServer/cloud/parsefunction/pdf/GenerateCertificate.js index 1d8fc2d15..c3ce72ac9 100644 --- a/apps/OpenSignServer/cloud/parsefunction/pdf/GenerateCertificate.js +++ b/apps/OpenSignServer/cloud/parsefunction/pdf/GenerateCertificate.js @@ -23,6 +23,7 @@ export default async function GenerateCertificate(docDetails) { const generateAt = new Date(); const generatedUTCTime = generateAt.toUTCString(); const generatedOn = 'Generated On ' + generatedUTCTime; + const OriginIp = docDetails?.OriginIp || ''; const company = docDetails?.ExtUserPtr?.Company || ''; const auditTrail = docDetails.AuditTrail?.length > 1 @@ -229,7 +230,7 @@ export default async function GenerateCertificate(docDetails) { font: timesRomanFont, color: textKeyColor, }); - page.drawText(`152.58.21.622`, { + page.drawText(`${OriginIp}`, { x: 130, y: 505, size: text, diff --git a/apps/OpenSignServer/index.js b/apps/OpenSignServer/index.js index a9432da6b..e06f2e40e 100644 --- a/apps/OpenSignServer/index.js +++ b/apps/OpenSignServer/index.js @@ -166,11 +166,6 @@ app.use(cors()); app.use(express.json({ limit: '50mb' })); app.use(express.urlencoded({ limit: '50mb', extended: true })); app.use(function (req, res, next) { - // console.log("req ", req.headers); - // console.log("x-forwarded-for", req.headers["x-forwarded-for"]); - // console.log("req.ip", req.ip); - // console.log("req.socket.remoteAddress; ", req.socket.remoteAddress); - // console.log("ip", ip.address()); req.headers['x-real-ip'] = getUserIP(req); next(); }); @@ -230,7 +225,7 @@ if (!process.env.TESTING) { // Set the Keep-Alive and headers timeout to 100 seconds httpServer.keepAliveTimeout = 100000; // in milliseconds httpServer.headersTimeout = 100000; // in milliseconds - httpServer.listen(port, function () { + httpServer.listen({ port: port, host: '0.0.0.0' }, function () { console.log('parse-server-example running on port ' + port + '.'); const isWindows = process.platform === 'win32'; // console.log('isWindows', isWindows); From a18733ad791a74ad8de0f972a3f0e6001a55eebe Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Fri, 3 May 2024 18:08:53 +0530 Subject: [PATCH 09/38] fix: show correct document creation date on certificate --- apps/OpenSign/src/pages/Form.js | 134 +++++++++--------- .../cloud/parsefunction/DocumentBeforesave.js | 4 + .../parsefunction/pdf/GenerateCertificate.js | 10 +- apps/OpenSignServer/index.js | 3 +- 4 files changed, 81 insertions(+), 70 deletions(-) diff --git a/apps/OpenSign/src/pages/Form.js b/apps/OpenSign/src/pages/Form.js index 6af4107ac..fd3e521f0 100644 --- a/apps/OpenSign/src/pages/Form.js +++ b/apps/OpenSign/src/pages/Form.js @@ -109,69 +109,69 @@ const Forms = (props) => { } } } 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); - } + 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 { + if (isEnableSubscription) { try { setfileload(true); const url = "http://tools.opensignlabs.com/docxtopdf"; @@ -426,7 +426,10 @@ const Forms = (props) => { )} <div className="text-xs"> <label className="block"> - File<span className="text-red-500 text-[13px]">*</span> + {`File (pdf, png, jpg, jpeg${ + isEnableSubscription ? ", docx)" : ")" + }`} + <span className="text-red-500 text-[13px]">*</span> </label> {fileupload.length > 0 ? ( <div className="flex gap-2 justify-center items-center"> @@ -454,11 +457,10 @@ 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={ isEnableSubscription - ? "application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,image/png,image/jpeg" - : "application/pdf" + ? "application/pdf,application/vnd.openxmlformats-officedocument.wordprocessingml.document,image/png,image/jpeg" + : "application/pdf,image/png,image/jpeg" } required /> diff --git a/apps/OpenSignServer/cloud/parsefunction/DocumentBeforesave.js b/apps/OpenSignServer/cloud/parsefunction/DocumentBeforesave.js index 0effff5ec..e7f251ca4 100644 --- a/apps/OpenSignServer/cloud/parsefunction/DocumentBeforesave.js +++ b/apps/OpenSignServer/cloud/parsefunction/DocumentBeforesave.js @@ -25,6 +25,10 @@ async function DocumentBeforesave(request) { } catch (error) { console.log('Error updating document count in contracts_users: ' + error.message); } + if (document?.get('Signers') && document.get('Signers').length > 0) { + document.set('DocSentAt', new Date()); + document.save(null, { useMasterKey: true }); + } } } catch (err) { console.log('err in document beforesave', err.message); diff --git a/apps/OpenSignServer/cloud/parsefunction/pdf/GenerateCertificate.js b/apps/OpenSignServer/cloud/parsefunction/pdf/GenerateCertificate.js index c3ce72ac9..0f3081c5e 100644 --- a/apps/OpenSignServer/cloud/parsefunction/pdf/GenerateCertificate.js +++ b/apps/OpenSignServer/cloud/parsefunction/pdf/GenerateCertificate.js @@ -25,6 +25,7 @@ export default async function GenerateCertificate(docDetails) { const generatedOn = 'Generated On ' + generatedUTCTime; const OriginIp = docDetails?.OriginIp || ''; const company = docDetails?.ExtUserPtr?.Company || ''; + const createdAt = docDetails?.DocSentAt?.iso || docDetails.createdAt; const auditTrail = docDetails.AuditTrail?.length > 1 ? docDetails.AuditTrail.map(x => { @@ -33,7 +34,7 @@ export default async function GenerateCertificate(docDetails) { ...data, ipAddress: x.ipAddress, SignedOn: x?.SignedOn || generatedUTCTime, - ViewedOn: x?.ViewedOn || generatedUTCTime, + ViewedOn: x?.ViewedOn || x?.SignedOn || generatedUTCTime, Signature: x?.Signature || '', }; }) @@ -42,7 +43,10 @@ export default async function GenerateCertificate(docDetails) { ...docDetails.ExtUserPtr, ipAddress: docDetails?.AuditTrail[0].ipAddress, SignedOn: docDetails?.AuditTrail[0]?.SignedOn || generatedUTCTime, - ViewedOn: docDetails?.AuditTrail[0]?.ViewedOn || generatedUTCTime, + ViewedOn: + docDetails?.AuditTrail[0]?.ViewedOn || + docDetails?.AuditTrail[0]?.SignedOn || + generatedUTCTime, Signature: docDetails?.AuditTrail[0]?.Signature || '', }, ]; @@ -151,7 +155,7 @@ export default async function GenerateCertificate(docDetails) { color: textKeyColor, }); - page.drawText(`${new Date(docDetails.createdAt).toUTCString()}`, { + page.drawText(`${new Date(createdAt).toUTCString()}`, { x: 105, y: 625, size: text, diff --git a/apps/OpenSignServer/index.js b/apps/OpenSignServer/index.js index e06f2e40e..2d51c739f 100644 --- a/apps/OpenSignServer/index.js +++ b/apps/OpenSignServer/index.js @@ -166,6 +166,7 @@ app.use(cors()); app.use(express.json({ limit: '50mb' })); app.use(express.urlencoded({ limit: '50mb', extended: true })); app.use(function (req, res, next) { + console.log('ip', getUserIP(req)); req.headers['x-real-ip'] = getUserIP(req); next(); }); @@ -225,7 +226,7 @@ if (!process.env.TESTING) { // Set the Keep-Alive and headers timeout to 100 seconds httpServer.keepAliveTimeout = 100000; // in milliseconds httpServer.headersTimeout = 100000; // in milliseconds - httpServer.listen({ port: port, host: '0.0.0.0' }, function () { + httpServer.listen(port, '0.0.0.0', function () { console.log('parse-server-example running on port ' + port + '.'); const isWindows = process.platform === 'win32'; // console.log('isWindows', isWindows); From da9363f588b61f35e8bca437b1bae27a479490d7 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Fri, 3 May 2024 18:11:50 +0530 Subject: [PATCH 10/38] fix: update description of plan --- apps/OpenSign/src/json/plansArr.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/OpenSign/src/json/plansArr.js b/apps/OpenSign/src/json/plansArr.js index dcac5e150..926862edf 100644 --- a/apps/OpenSign/src/json/plansArr.js +++ b/apps/OpenSign/src/json/plansArr.js @@ -43,7 +43,7 @@ const plans = [ : "https://billing.opensignlabs.com/subscribe/ef798486e6a0a11ea65f2bae8f2af901d8ad1135190dff951330360e47585a71/professional-yearly", target: "_self", benefits: [ - "Everything in OpenSign™ professional", + "Everything in OpenSign™ free", "Field validations", "Regular expression validations", "Organize docs in OpenSign™ Drive", From 98f84f6cc884693c08b2e723aa6ff9b7f50a0282 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Fri, 3 May 2024 18:15:28 +0530 Subject: [PATCH 11/38] fix: update description of plan --- apps/OpenSign/src/json/plansArr.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/OpenSign/src/json/plansArr.js b/apps/OpenSign/src/json/plansArr.js index 926862edf..f2a116904 100644 --- a/apps/OpenSign/src/json/plansArr.js +++ b/apps/OpenSign/src/json/plansArr.js @@ -52,7 +52,7 @@ const plans = [ "API Access", "100 API signatures included", "DocumentId removal from signed docs", - "Custom email templates(coming soon)" + "Custom email templates" ] }, { From ee18f82fa928ea8e045f3c67fcf95f78f0afc583 Mon Sep 17 00:00:00 2001 From: RaktimaNXG <raktima.c@nxglabs.com> Date: Fri, 3 May 2024 20:11:57 +0530 Subject: [PATCH 12/38] fix: verify email otp varification for inProgress reports --- apps/OpenSign/src/constant/Utils.js | 13 ++ apps/OpenSign/src/pages/PdfRequestFiles.js | 230 ++++++++++++++++++++- apps/OpenSign/src/pages/PlaceHolderSign.js | 165 +++++++++++++-- apps/OpenSign/src/pages/UserProfile.js | 17 +- 4 files changed, 404 insertions(+), 21 deletions(-) diff --git a/apps/OpenSign/src/constant/Utils.js b/apps/OpenSign/src/constant/Utils.js index ab4f4e9a6..8823a4863 100644 --- a/apps/OpenSign/src/constant/Utils.js +++ b/apps/OpenSign/src/constant/Utils.js @@ -2008,3 +2008,16 @@ export const convertPdfArrayBuffer = async (url) => { return "Error"; } }; +export const handleSendOTP = async (email) => { + try { + let url = `${localStorage.getItem("baseUrl")}functions/SendOTPMailV1`; + const headers = { + "Content-Type": "application/json", + "X-Parse-Application-Id": localStorage.getItem("parseAppId") + }; + const body = { email: email }; + await axios.post(url, body, { headers: headers }); + } catch (error) { + alert(error.message); + } +}; diff --git a/apps/OpenSign/src/pages/PdfRequestFiles.js b/apps/OpenSign/src/pages/PdfRequestFiles.js index 50be8ced4..15a3dd903 100644 --- a/apps/OpenSign/src/pages/PdfRequestFiles.js +++ b/apps/OpenSign/src/pages/PdfRequestFiles.js @@ -1,7 +1,13 @@ import React, { useState, useRef, useEffect } from "react"; -import { isEnableSubscription, themeColor } from "../constant/const"; +import { + isEnableSubscription, + rejectBtn, + submitBtn, + themeColor +} from "../constant/const"; import { PDFDocument } from "pdf-lib"; import "../styles/signature.css"; +import Parse from "parse"; import axios from "axios"; import loader from "../assets/images/loader2.gif"; import { DndProvider } from "react-dnd"; @@ -25,7 +31,8 @@ import { replaceMailVaribles, fetchSubscription, convertPdfArrayBuffer, - contractUsers + contractUsers, + handleSendOTP } from "../constant/Utils"; import Loader from "../primitives/LoaderWithMsg"; import HandleError from "../primitives/HandleError"; @@ -57,10 +64,12 @@ function PdfRequestFiles() { const imageRef = useRef(null); const [handleError, setHandleError] = useState(); const [selectWidgetId, setSelectWidgetId] = useState(""); + const [otpLoader, setOtpLoader] = useState(false); const [isLoading, setIsLoading] = useState({ isLoad: true, message: "This might take some time" }); + const [defaultSignImg, setDefaultSignImg] = useState(); const [isDocId, setIsDocId] = useState(false); const [pdfNewWidth, setPdfNewWidth] = useState(); @@ -100,6 +109,9 @@ function PdfRequestFiles() { const [isSubscriptionExpired, setIsSubscriptionExpired] = useState(false); const [extUserId, setExtUserId] = useState(""); const [pdfArrayBuffer, setPdfArrayBuffer] = useState(""); + const [isEmailVerified, setIsEmailVerified] = useState(true); + const [isVerifyModal, setIsVerifyModal] = useState(false); + const [otp, setOtp] = useState(""); const divRef = useRef(null); const isMobile = window.innerWidth < 767; const rowLevel = @@ -139,6 +151,41 @@ function PdfRequestFiles() { // eslint-disable-next-line react-hooks/exhaustive-deps }, [divRef.current]); + const handleReset = async (e) => { + e.preventDefault(); + setOtpLoader(true); + await handleSendOTP(Parse.User.current().getEmail()); + setOtpLoader(false); + alert("OTP sent on you email"); + }; + 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); + // handleRecipientSign(); + } catch (error) { + alert(error.message); + } finally { + setOtpLoader(false); + } + }; + + const handleVerifyBtn = async () => { + setIsVerifyModal(true); + await handleSendOTP(Parse.User.current().getEmail()); + }; async function checkIsSubscribed(extUserId, contactId) { const isGuestSign = location.pathname.includes("/load/") || false; const res = await fetchSubscription(extUserId, contactId, isGuestSign); @@ -238,6 +285,22 @@ function PdfRequestFiles() { setExpiredDate(expireDateFormat); } + const isGuestSign = location.pathname.includes("/load/"); + if ( + !isGuestSign && + !isCompleted && + !declined && + currDate < expireUpdateDate + ) { + const userQuery = new Parse.Query(Parse.User); + const user = await userQuery.get(jsonSender.objectId, { + sessionToken: localStorage.getItem("accesstoken") + }); + if (user) { + const isEmailVerified = user?.get("emailVerified"); + setIsEmailVerified(isEmailVerified); + } + } if (documentData.length > 0) { const checkDocIdExist = documentData[0].AuditTrail && @@ -1179,6 +1242,83 @@ function PdfRequestFiles() { headMsg="Document Expired!" bodyMssg={`This document expired on ${expiredDate} and is no longer available to sign.`} /> + {!isEmailVerified && ( + <div className="bg-black bg-opacity-[75%] absolute z-[999] flex flex-col items-center justify-center w-full h-full rounded"> + <div className="bg-white rounded outline-none md:w-[40%] w-[80%]"> + <div + style={{ backgroundColor: themeColor }} + className=" text-white p-[10px] rounded-t" + > + OTP verification + </div> + {isVerifyModal ? ( + <form + onSubmit={(e) => { + setIsVerifyModal(false); + 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> + ) : otpLoader ? ( + <div + style={{ + height: "150px", + display: "flex", + alignItems: "center", + justifyContent: "center" + }} + > + <div + style={{ + fontSize: "45px", + color: "#3dd3e0" + }} + className="loader-37" + ></div> + </div> + ) : ( + <div className="p-[15px]"> + <p>Please verify your email !</p> + <div className="h-[1px] bg-[#9f9f9f] w-full"></div> + <div className="m-[15px] "> + <button + className={submitBtn} + type="submit" + onClick={() => { + handleVerifyBtn(); + }} + > + Verify + </button> + </div> + </div> + )} + </div> + </div> + )} <ModalUi headerColor={defaultSignImg ? themeColor : "#dc3545"} @@ -1558,6 +1698,92 @@ function PdfRequestFiles() { </button> </div> </ModalUi> + + {/* {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> + )} */} + {/* {!isEmailVerified && ( + <ModalUi + isOpen={!isEmailVerified} + title={"OTP verification"} + handleClose={() => { + setValidateAlert(false); + }} + > + <div style={{ height: "100%", padding: 20 }}> + <p>Please verify your email !</p> + + <div + style={{ + height: "1px", + backgroundColor: "#9f9f9f", + width: "100%", + marginTop: "15px", + marginBottom: "15px" + }} + ></div> + <button + className={submitBtn} + type="submit" + onClick={() => { + setIsEmailVerified(true); + setIsVerifyModal(true); + }} + > + Verify + </button> + </div> + </ModalUi> + )} */} </> )} </DndProvider> diff --git a/apps/OpenSign/src/pages/PlaceHolderSign.js b/apps/OpenSign/src/pages/PlaceHolderSign.js index d161c032b..30b6071e6 100644 --- a/apps/OpenSign/src/pages/PlaceHolderSign.js +++ b/apps/OpenSign/src/pages/PlaceHolderSign.js @@ -3,7 +3,12 @@ import axios from "axios"; import Parse from "parse"; import "../styles/signature.css"; import { PDFDocument } from "pdf-lib"; -import { isEnableSubscription, themeColor } from "../constant/const"; +import { + isEnableSubscription, + rejectBtn, + submitBtn, + themeColor +} from "../constant/const"; import { DndProvider } from "react-dnd"; import { HTML5Backend } from "react-dnd-html5-backend"; import { useDrag, useDrop } from "react-dnd"; @@ -34,7 +39,8 @@ import { replaceMailVaribles, copytoData, fetchSubscription, - convertPdfArrayBuffer + convertPdfArrayBuffer, + handleSendOTP } from "../constant/Utils"; import RenderPdf from "../components/pdf/RenderPdf"; import { useNavigate } from "react-router-dom"; @@ -84,6 +90,7 @@ function PlaceHolderSign() { const [pdfOriginalWidth, setPdfOriginalWidth] = useState(); const [contractName, setContractName] = useState(""); const [containerWH, setContainerWH] = useState(); + const [otpLoader, setOtpLoader] = useState(false); const { docId } = useParams(); const signRef = useRef(null); const dragRef = useRef(null); @@ -124,6 +131,9 @@ function PlaceHolderSign() { const [requestBody, setRequestBody] = useState(""); const [pdfArrayBuffer, setPdfArrayBuffer] = useState(""); const [activeMailAdapter, setActiveMailAdapter] = useState(""); + const [isEmailVerified, setIsEmailVerified] = useState(false); + const [isVerifyModal, setIsVerifyModal] = useState(false); + const [otp, setOtp] = useState(""); const [isAlreadyPlace, setIsAlreadyPlace] = useState({ status: false, message: "" @@ -251,9 +261,63 @@ function PlaceHolderSign() { navigate(`/subscription`); } } + const handleVerifyBtn = async () => { + setIsVerifyModal(true); + await handleSendOTP(Parse.User.current().getEmail()); + }; + + const handleReset = async (e) => { + e.preventDefault(); + setOtpLoader(true); + await handleSendOTP(Parse.User.current().getEmail()); + setOtpLoader(false); + alert("OTP sent on you email"); + }; + 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); + handleRecipientSign(); + } catch (error) { + alert(error.message); + } finally { + setOtpLoader(false); + } + }; + const handleCloseVerifyModal = async () => { + setIsVerifyModal(false); + }; //function for get document details const getDocumentDetails = async () => { fetchTenantDetails(); + + const jsonSender = JSON.parse( + localStorage.getItem( + `Parse/${localStorage.getItem("parseAppId")}/currentUser` + ) + ); + // const isEmailVerified = Parse.User.current()?.attributes?.emailVerified; + const userQuery = new Parse.Query(Parse.User); + const user = await userQuery.get(jsonSender.objectId, { + sessionToken: localStorage.getItem("accesstoken") + }); + if (user) { + const isEmailVerified = user?.get("emailVerified"); + setIsEmailVerified(isEmailVerified); + } + //getting document details const documentData = await contractDocument(documentId); if (documentData && documentData.length > 0) { @@ -1837,21 +1901,36 @@ function PlaceHolderSign() { marginBottom: "15px" }} ></div> - {isCurrUser && ( - <button - onClick={() => { - handleRecipientSign(); - }} - style={{ - background: themeColor, - color: "white" - }} - type="button" - className="finishBtn" - > - Yes - </button> - )} + {isCurrUser && + (isEmailVerified ? ( + <button + onClick={() => { + handleRecipientSign(); + }} + style={{ + background: themeColor, + color: "white" + }} + type="button" + className="finishBtn" + > + Yes + </button> + ) : ( + <button + onClick={() => { + handleVerifyBtn(); + }} + style={{ + background: themeColor, + color: "white" + }} + type="button" + className="finishBtn" + > + Verify Email + </button> + ))} <button onClick={() => { @@ -2142,6 +2221,58 @@ function PlaceHolderSign() { handleData={handleWidgetdefaultdata} isSubscribe={isSubscribe} /> + {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> </> ); diff --git a/apps/OpenSign/src/pages/UserProfile.js b/apps/OpenSign/src/pages/UserProfile.js index 69b491468..ff5e19d17 100644 --- a/apps/OpenSign/src/pages/UserProfile.js +++ b/apps/OpenSign/src/pages/UserProfile.js @@ -53,8 +53,21 @@ function UserProfile() { if (HeaderDocId) { setIsDisableDocId(HeaderDocId); } - const isEmailVerified = Parse.User.current()?.attributes?.emailVerified; - setIsEmailVerified(isEmailVerified); + const currentUser = JSON.parse( + localStorage.getItem( + `Parse/${localStorage.getItem("parseAppId")}/currentUser` + ) + ); + // const isEmailVerified = Parse.User.current()?.attributes?.emailVerified; + const userQuery = new Parse.Query(Parse.User); + const user = await userQuery.get(currentUser.objectId, { + sessionToken: localStorage.getItem("accesstoken") + }); + if (user) { + const isEmailVerified = user?.get("emailVerified"); + setIsEmailVerified(isEmailVerified); + } + setIsLoader(false); }; const handleSubmit = async (e) => { From 9daed4a5ed1b9ae9a29acd7d853032fa6310d34f Mon Sep 17 00:00:00 2001 From: RaktimaNXG <raktima.c@nxglabs.com> Date: Fri, 3 May 2024 20:19:38 +0530 Subject: [PATCH 13/38] refactor : remove commented code --- apps/OpenSign/src/pages/PdfRequestFiles.js | 86 ---------------------- 1 file changed, 86 deletions(-) diff --git a/apps/OpenSign/src/pages/PdfRequestFiles.js b/apps/OpenSign/src/pages/PdfRequestFiles.js index 15a3dd903..eae720d62 100644 --- a/apps/OpenSign/src/pages/PdfRequestFiles.js +++ b/apps/OpenSign/src/pages/PdfRequestFiles.js @@ -1698,92 +1698,6 @@ function PdfRequestFiles() { </button> </div> </ModalUi> - - {/* {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> - )} */} - {/* {!isEmailVerified && ( - <ModalUi - isOpen={!isEmailVerified} - title={"OTP verification"} - handleClose={() => { - setValidateAlert(false); - }} - > - <div style={{ height: "100%", padding: 20 }}> - <p>Please verify your email !</p> - - <div - style={{ - height: "1px", - backgroundColor: "#9f9f9f", - width: "100%", - marginTop: "15px", - marginBottom: "15px" - }} - ></div> - <button - className={submitBtn} - type="submit" - onClick={() => { - setIsEmailVerified(true); - setIsVerifyModal(true); - }} - > - Verify - </button> - </div> - </ModalUi> - )} */} </> )} </DndProvider> From c240eb133d4f298bde71c0dfb9c1099826dcb4f6 Mon Sep 17 00:00:00 2001 From: RaktimaNXG <raktima.c@nxglabs.com> Date: Sun, 5 May 2024 15:53:51 +0530 Subject: [PATCH 14/38] fix: add verify email otp verification in signyour-self flow --- .../src/components/pdf/VerifyEmail.js | 84 +++++++++ apps/OpenSign/src/pages/PdfRequestFiles.js | 93 ++-------- apps/OpenSign/src/pages/PlaceHolderSign.js | 164 ++---------------- apps/OpenSign/src/pages/SignyourselfPdf.js | 68 +++++++- 4 files changed, 180 insertions(+), 229 deletions(-) create mode 100644 apps/OpenSign/src/components/pdf/VerifyEmail.js diff --git a/apps/OpenSign/src/components/pdf/VerifyEmail.js b/apps/OpenSign/src/components/pdf/VerifyEmail.js new file mode 100644 index 000000000..1387efb42 --- /dev/null +++ b/apps/OpenSign/src/components/pdf/VerifyEmail.js @@ -0,0 +1,84 @@ +import React from "react"; +import { rejectBtn, submitBtn, themeColor } from "../../constant/const"; + +function VerifyEmail(props) { + return ( + <div className="bg-black bg-opacity-[75%] absolute z-[999] flex flex-col items-center justify-center w-full h-full rounded"> + <div className="bg-white rounded outline-none md:w-[40%] w-[80%]"> + <div + style={{ backgroundColor: themeColor }} + className=" text-white p-[10px] rounded-t" + > + OTP verification + </div> + {props.isVerifyModal ? ( + <form + onSubmit={(e) => { + props.setIsVerifyModal(false); + props.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={props.otp} + onChange={(e) => props.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) => props.handleReset(e)} + > + Resend + </button> + </div> + </form> + ) : props.otpLoader ? ( + <div + style={{ + height: "150px", + display: "flex", + alignItems: "center", + justifyContent: "center" + }} + > + <div + style={{ + fontSize: "45px", + color: "#3dd3e0" + }} + className="loader-37" + ></div> + </div> + ) : ( + <div className="p-[15px]"> + <p>Please verify your email !</p> + <div className="h-[1px] bg-[#9f9f9f] w-full"></div> + <div className="m-[15px] "> + <button + className={submitBtn} + type="submit" + onClick={() => { + props.handleVerifyBtn(); + }} + > + Verify + </button> + </div> + </div> + )} + </div> + </div> + ); +} + +export default VerifyEmail; diff --git a/apps/OpenSign/src/pages/PdfRequestFiles.js b/apps/OpenSign/src/pages/PdfRequestFiles.js index eae720d62..36c8c477f 100644 --- a/apps/OpenSign/src/pages/PdfRequestFiles.js +++ b/apps/OpenSign/src/pages/PdfRequestFiles.js @@ -1,10 +1,5 @@ import React, { useState, useRef, useEffect } from "react"; -import { - isEnableSubscription, - rejectBtn, - submitBtn, - themeColor -} from "../constant/const"; +import { isEnableSubscription, themeColor } from "../constant/const"; import { PDFDocument } from "pdf-lib"; import "../styles/signature.css"; import Parse from "parse"; @@ -42,6 +37,7 @@ import PdfDeclineModal from "../primitives/PdfDeclineModal"; import Title from "../components/Title"; import DefaultSignature from "../components/pdf/DefaultSignature"; import ModalUi from "../primitives/ModalUi"; +import VerifyEmail from "../components/pdf/VerifyEmail"; function PdfRequestFiles() { const { docId } = useParams(); @@ -1243,81 +1239,16 @@ function PdfRequestFiles() { bodyMssg={`This document expired on ${expiredDate} and is no longer available to sign.`} /> {!isEmailVerified && ( - <div className="bg-black bg-opacity-[75%] absolute z-[999] flex flex-col items-center justify-center w-full h-full rounded"> - <div className="bg-white rounded outline-none md:w-[40%] w-[80%]"> - <div - style={{ backgroundColor: themeColor }} - className=" text-white p-[10px] rounded-t" - > - OTP verification - </div> - {isVerifyModal ? ( - <form - onSubmit={(e) => { - setIsVerifyModal(false); - 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> - ) : otpLoader ? ( - <div - style={{ - height: "150px", - display: "flex", - alignItems: "center", - justifyContent: "center" - }} - > - <div - style={{ - fontSize: "45px", - color: "#3dd3e0" - }} - className="loader-37" - ></div> - </div> - ) : ( - <div className="p-[15px]"> - <p>Please verify your email !</p> - <div className="h-[1px] bg-[#9f9f9f] w-full"></div> - <div className="m-[15px] "> - <button - className={submitBtn} - type="submit" - onClick={() => { - handleVerifyBtn(); - }} - > - Verify - </button> - </div> - </div> - )} - </div> - </div> + <VerifyEmail + isVerifyModal={isVerifyModal} + setIsVerifyModal={setIsVerifyModal} + handleVerifyEmail={handleVerifyEmail} + setOtp={setOtp} + otp={otp} + otpLoader={otpLoader} + handleVerifyBtn={handleVerifyBtn} + handleReset={handleReset} + /> )} <ModalUi diff --git a/apps/OpenSign/src/pages/PlaceHolderSign.js b/apps/OpenSign/src/pages/PlaceHolderSign.js index 30b6071e6..1eaddc43c 100644 --- a/apps/OpenSign/src/pages/PlaceHolderSign.js +++ b/apps/OpenSign/src/pages/PlaceHolderSign.js @@ -3,12 +3,7 @@ import axios from "axios"; import Parse from "parse"; import "../styles/signature.css"; import { PDFDocument } from "pdf-lib"; -import { - isEnableSubscription, - rejectBtn, - submitBtn, - themeColor -} from "../constant/const"; +import { isEnableSubscription, themeColor } from "../constant/const"; import { DndProvider } from "react-dnd"; import { HTML5Backend } from "react-dnd-html5-backend"; import { useDrag, useDrop } from "react-dnd"; @@ -39,8 +34,7 @@ import { replaceMailVaribles, copytoData, fetchSubscription, - convertPdfArrayBuffer, - handleSendOTP + convertPdfArrayBuffer } from "../constant/Utils"; import RenderPdf from "../components/pdf/RenderPdf"; import { useNavigate } from "react-router-dom"; @@ -90,7 +84,6 @@ function PlaceHolderSign() { const [pdfOriginalWidth, setPdfOriginalWidth] = useState(); const [contractName, setContractName] = useState(""); const [containerWH, setContainerWH] = useState(); - const [otpLoader, setOtpLoader] = useState(false); const { docId } = useParams(); const signRef = useRef(null); const dragRef = useRef(null); @@ -131,9 +124,6 @@ function PlaceHolderSign() { const [requestBody, setRequestBody] = useState(""); const [pdfArrayBuffer, setPdfArrayBuffer] = useState(""); const [activeMailAdapter, setActiveMailAdapter] = useState(""); - const [isEmailVerified, setIsEmailVerified] = useState(false); - const [isVerifyModal, setIsVerifyModal] = useState(false); - const [otp, setOtp] = useState(""); const [isAlreadyPlace, setIsAlreadyPlace] = useState({ status: false, message: "" @@ -261,63 +251,10 @@ function PlaceHolderSign() { navigate(`/subscription`); } } - const handleVerifyBtn = async () => { - setIsVerifyModal(true); - await handleSendOTP(Parse.User.current().getEmail()); - }; - const handleReset = async (e) => { - e.preventDefault(); - setOtpLoader(true); - await handleSendOTP(Parse.User.current().getEmail()); - setOtpLoader(false); - alert("OTP sent on you email"); - }; - 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); - handleRecipientSign(); - } catch (error) { - alert(error.message); - } finally { - setOtpLoader(false); - } - }; - const handleCloseVerifyModal = async () => { - setIsVerifyModal(false); - }; //function for get document details const getDocumentDetails = async () => { fetchTenantDetails(); - - const jsonSender = JSON.parse( - localStorage.getItem( - `Parse/${localStorage.getItem("parseAppId")}/currentUser` - ) - ); - // const isEmailVerified = Parse.User.current()?.attributes?.emailVerified; - const userQuery = new Parse.Query(Parse.User); - const user = await userQuery.get(jsonSender.objectId, { - sessionToken: localStorage.getItem("accesstoken") - }); - if (user) { - const isEmailVerified = user?.get("emailVerified"); - setIsEmailVerified(isEmailVerified); - } - //getting document details const documentData = await contractDocument(documentId); if (documentData && documentData.length > 0) { @@ -1901,36 +1838,21 @@ function PlaceHolderSign() { marginBottom: "15px" }} ></div> - {isCurrUser && - (isEmailVerified ? ( - <button - onClick={() => { - handleRecipientSign(); - }} - style={{ - background: themeColor, - color: "white" - }} - type="button" - className="finishBtn" - > - Yes - </button> - ) : ( - <button - onClick={() => { - handleVerifyBtn(); - }} - style={{ - background: themeColor, - color: "white" - }} - type="button" - className="finishBtn" - > - Verify Email - </button> - ))} + {isCurrUser && ( + <button + onClick={() => { + handleRecipientSign(); + }} + style={{ + background: themeColor, + color: "white" + }} + type="button" + className="finishBtn" + > + Yes + </button> + )} <button onClick={() => { @@ -2221,58 +2143,6 @@ function PlaceHolderSign() { handleData={handleWidgetdefaultdata} isSubscribe={isSubscribe} /> - {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> </> ); diff --git a/apps/OpenSign/src/pages/SignyourselfPdf.js b/apps/OpenSign/src/pages/SignyourselfPdf.js index 2b276d776..54ad8373f 100644 --- a/apps/OpenSign/src/pages/SignyourselfPdf.js +++ b/apps/OpenSign/src/pages/SignyourselfPdf.js @@ -1,6 +1,7 @@ import React, { useState, useRef, useEffect } from "react"; import { PDFDocument } from "pdf-lib"; import "../styles/signature.css"; +import Parse from "parse"; import { isEnableSubscription, themeColor } from "../constant/const"; import axios from "axios"; import Loader from "../primitives/LoaderWithMsg"; @@ -31,7 +32,8 @@ import { checkIsSubscribed, convertPdfArrayBuffer, fetchImageBase64, - changeImageWH + changeImageWH, + handleSendOTP } from "../constant/Utils"; import { useParams } from "react-router-dom"; import Tour from "reactour"; @@ -44,6 +46,7 @@ import TourContentWithBtn from "../primitives/TourContentWithBtn"; import Title from "../components/Title"; import ModalUi from "../primitives/ModalUi"; import DropdownWidgetOption from "../components/pdf/DropdownWidgetOption"; +import VerifyEmail from "../components/pdf/VerifyEmail"; //For signYourself inProgress section signer can add sign and complete doc sign. function SignYourSelf() { @@ -91,6 +94,7 @@ function SignYourSelf() { const [containerWH, setContainerWH] = useState({}); const [isPageCopy, setIsPageCopy] = useState(false); const [selectWidgetId, setSelectWidgetId] = useState(""); + const [otpLoader, setOtpLoader] = useState(false); const [showAlreadySignDoc, setShowAlreadySignDoc] = useState({ status: false }); @@ -107,6 +111,9 @@ function SignYourSelf() { const [isCompleted, setIsCompleted] = useState(false); const [pdfArrayBuffer, setPdfArrayBuffer] = useState(""); const [activeMailAdapter, setActiveMailAdapter] = useState(""); + const [isEmailVerified, setIsEmailVerified] = useState(true); + const [isVerifyModal, setIsVerifyModal] = useState(false); + const [otp, setOtp] = useState(""); const divRef = useRef(null); const nodeRef = useRef(null); const [, drop] = useDrop({ @@ -226,6 +233,17 @@ function SignYourSelf() { setSignBtnPosition([]); } } + + if (!isCompleted) { + const userQuery = new Parse.Query(Parse.User); + const user = await userQuery.get(jsonSender.objectId, { + sessionToken: localStorage.getItem("accesstoken") + }); + if (user) { + const isEmailVerified = user?.get("emailVerified"); + setIsEmailVerified(isEmailVerified); + } + } } else if ( documentData === "Error: Something went wrong!" || (documentData.result && documentData.result.error) @@ -533,6 +551,42 @@ function SignYourSelf() { setSelectWidgetId(key); setSignKey(key); }; + + const handleReset = async (e) => { + e.preventDefault(); + setOtpLoader(true); + await handleSendOTP(Parse.User.current().getEmail()); + setOtpLoader(false); + alert("OTP sent on you email"); + }; + 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); + // handleRecipientSign(); + } catch (error) { + alert(error.message); + } finally { + setOtpLoader(false); + } + }; + + const handleVerifyBtn = async () => { + setIsVerifyModal(true); + await handleSendOTP(Parse.User.current().getEmail()); + }; //function for send placeholder's co-ordinate(x,y) position embed signature url or stamp url async function embedWidgetsData() { let showAlert = false; @@ -1037,6 +1091,18 @@ function SignYourSelf() { )} <div className="signatureContainer" ref={divRef}> + {!isEmailVerified && ( + <VerifyEmail + isVerifyModal={isVerifyModal} + setIsVerifyModal={setIsVerifyModal} + handleVerifyEmail={handleVerifyEmail} + setOtp={setOtp} + otp={otp} + otpLoader={otpLoader} + handleVerifyBtn={handleVerifyBtn} + handleReset={handleReset} + /> + )} {/* this component used for UI interaction and show their functionality */} {pdfLoadFail && !checkTourStatus && ( <Tour From 8693ef29f630d3254ac8e8d17940f41b0eed4289 Mon Sep 17 00:00:00 2001 From: ugoconsonni <uconsonni@gmail.com> Date: Sun, 5 May 2024 21:51:36 +0200 Subject: [PATCH 15/38] Update sendMailv3.js Fix: [Bug]: createTransport was not imported #666 --- apps/OpenSignServer/cloud/parsefunction/sendMailv3.js | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/OpenSignServer/cloud/parsefunction/sendMailv3.js b/apps/OpenSignServer/cloud/parsefunction/sendMailv3.js index c09694a4d..7a8b2e6f8 100644 --- a/apps/OpenSignServer/cloud/parsefunction/sendMailv3.js +++ b/apps/OpenSignServer/cloud/parsefunction/sendMailv3.js @@ -4,6 +4,7 @@ import formData from 'form-data'; import Mailgun from 'mailgun.js'; import { updateMailCount } from '../../Utils.js'; import sendMailGmailProvider from './sendMailGmailProvider.js'; +import { createTransport } from 'nodemailer'; async function sendMailProvider(req) { try { let transporterSMTP; From 8fd2d4210e18ff2a682864b010c2cb600d5af264 Mon Sep 17 00:00:00 2001 From: RaktimaNXG <raktima.c@nxglabs.com> Date: Mon, 6 May 2024 11:32:30 +0530 Subject: [PATCH 16/38] refactor : add comment, handle unnecessary query call --- .../src/components/pdf/VerifyEmail.js | 3 +- apps/OpenSign/src/constant/Utils.js | 2 + apps/OpenSign/src/pages/PdfRequestFiles.js | 26 +++++++--- apps/OpenSign/src/pages/SignyourselfPdf.js | 29 +++++++---- apps/OpenSign/src/pages/UserProfile.js | 50 ++++++++----------- 5 files changed, 63 insertions(+), 47 deletions(-) diff --git a/apps/OpenSign/src/components/pdf/VerifyEmail.js b/apps/OpenSign/src/components/pdf/VerifyEmail.js index 1387efb42..48ed2b58b 100644 --- a/apps/OpenSign/src/components/pdf/VerifyEmail.js +++ b/apps/OpenSign/src/components/pdf/VerifyEmail.js @@ -21,6 +21,7 @@ function VerifyEmail(props) { <div className="px-6 py-3"> <label className="mb-2">Enter OTP</label> <input + required type="tel" pattern="[0-9]{4}" className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs" @@ -36,7 +37,7 @@ function VerifyEmail(props) { </button> <button className={`${rejectBtn} ml-2`} - onClick={(e) => props.handleReset(e)} + onClick={(e) => props.handleResend(e)} > Resend </button> diff --git a/apps/OpenSign/src/constant/Utils.js b/apps/OpenSign/src/constant/Utils.js index 8823a4863..3b3286cf7 100644 --- a/apps/OpenSign/src/constant/Utils.js +++ b/apps/OpenSign/src/constant/Utils.js @@ -2008,6 +2008,8 @@ export const convertPdfArrayBuffer = async (url) => { return "Error"; } }; + +//function to use send otp on user email using `SendOTPMailV1` cloud function export const handleSendOTP = async (email) => { try { let url = `${localStorage.getItem("baseUrl")}functions/SendOTPMailV1`; diff --git a/apps/OpenSign/src/pages/PdfRequestFiles.js b/apps/OpenSign/src/pages/PdfRequestFiles.js index 36c8c477f..211a50c68 100644 --- a/apps/OpenSign/src/pages/PdfRequestFiles.js +++ b/apps/OpenSign/src/pages/PdfRequestFiles.js @@ -147,13 +147,15 @@ function PdfRequestFiles() { // eslint-disable-next-line react-hooks/exhaustive-deps }, [divRef.current]); - const handleReset = async (e) => { + //function to use resend otp for email verification + const handleResend = async (e) => { e.preventDefault(); setOtpLoader(true); await handleSendOTP(Parse.User.current().getEmail()); setOtpLoader(false); alert("OTP sent on you email"); }; + //function to use verify email with otp const handleVerifyEmail = async (e) => { e.preventDefault(); setOtpLoader(true); @@ -178,6 +180,7 @@ function PdfRequestFiles() { } }; + //function to send otp on user mail const handleVerifyBtn = async () => { setIsVerifyModal(true); await handleSendOTP(Parse.User.current().getEmail()); @@ -288,13 +291,20 @@ function PdfRequestFiles() { !declined && currDate < expireUpdateDate ) { - const userQuery = new Parse.Query(Parse.User); - const user = await userQuery.get(jsonSender.objectId, { - sessionToken: localStorage.getItem("accesstoken") - }); - if (user) { - const isEmailVerified = user?.get("emailVerified"); + const currentUser = JSON.parse(JSON.stringify(Parse.User.current())); + let isEmailVerified; + isEmailVerified = currentUser?.emailVerified; + if (isEmailVerified) { setIsEmailVerified(isEmailVerified); + } else { + const userQuery = new Parse.Query(Parse.User); + const user = await userQuery.get(currentUser.objectId, { + sessionToken: localStorage.getItem("accesstoken") + }); + if (user) { + isEmailVerified = user?.get("emailVerified"); + setIsEmailVerified(isEmailVerified); + } } } if (documentData.length > 0) { @@ -1247,7 +1257,7 @@ function PdfRequestFiles() { otp={otp} otpLoader={otpLoader} handleVerifyBtn={handleVerifyBtn} - handleReset={handleReset} + handleResend={handleResend} /> )} diff --git a/apps/OpenSign/src/pages/SignyourselfPdf.js b/apps/OpenSign/src/pages/SignyourselfPdf.js index 54ad8373f..b281482bb 100644 --- a/apps/OpenSign/src/pages/SignyourselfPdf.js +++ b/apps/OpenSign/src/pages/SignyourselfPdf.js @@ -235,13 +235,21 @@ function SignYourSelf() { } if (!isCompleted) { - const userQuery = new Parse.Query(Parse.User); - const user = await userQuery.get(jsonSender.objectId, { - sessionToken: localStorage.getItem("accesstoken") - }); - if (user) { - const isEmailVerified = user?.get("emailVerified"); + //check current user email verified or not + const currentUser = JSON.parse(JSON.stringify(Parse.User.current())); + let isEmailVerified; + isEmailVerified = currentUser?.emailVerified; + if (isEmailVerified) { setIsEmailVerified(isEmailVerified); + } else { + const userQuery = new Parse.Query(Parse.User); + const user = await userQuery.get(currentUser.objectId, { + sessionToken: localStorage.getItem("accesstoken") + }); + if (user) { + isEmailVerified = user?.get("emailVerified"); + setIsEmailVerified(isEmailVerified); + } } } } else if ( @@ -552,13 +560,16 @@ function SignYourSelf() { setSignKey(key); }; - const handleReset = async (e) => { + //function to use resend otp for email verification + const handleResend = async (e) => { e.preventDefault(); setOtpLoader(true); await handleSendOTP(Parse.User.current().getEmail()); setOtpLoader(false); alert("OTP sent on you email"); }; + + //function to use verify email with otp const handleVerifyEmail = async (e) => { e.preventDefault(); setOtpLoader(true); @@ -582,7 +593,7 @@ function SignYourSelf() { setOtpLoader(false); } }; - + //function to send otp on user mail const handleVerifyBtn = async () => { setIsVerifyModal(true); await handleSendOTP(Parse.User.current().getEmail()); @@ -1100,7 +1111,7 @@ function SignYourSelf() { otp={otp} otpLoader={otpLoader} handleVerifyBtn={handleVerifyBtn} - handleReset={handleReset} + handleResend={handleResend} /> )} {/* this component used for UI interaction and show their functionality */} diff --git a/apps/OpenSign/src/pages/UserProfile.js b/apps/OpenSign/src/pages/UserProfile.js index ff5e19d17..9f7c4213a 100644 --- a/apps/OpenSign/src/pages/UserProfile.js +++ b/apps/OpenSign/src/pages/UserProfile.js @@ -9,7 +9,7 @@ import axios from "axios"; import PremiumAlertHeader from "../primitives/PremiumAlertHeader"; import Tooltip from "../primitives/Tooltip"; import { isEnableSubscription, rejectBtn, submitBtn } from "../constant/const"; -import { checkIsSubscribed } from "../constant/Utils"; +import { checkIsSubscribed, handleSendOTP } from "../constant/Utils"; import Upgrade from "../primitives/Upgrade"; import ModalUi from "../primitives/ModalUi"; @@ -53,19 +53,20 @@ function UserProfile() { if (HeaderDocId) { setIsDisableDocId(HeaderDocId); } - const currentUser = JSON.parse( - localStorage.getItem( - `Parse/${localStorage.getItem("parseAppId")}/currentUser` - ) - ); - // const isEmailVerified = Parse.User.current()?.attributes?.emailVerified; - const userQuery = new Parse.Query(Parse.User); - const user = await userQuery.get(currentUser.objectId, { - sessionToken: localStorage.getItem("accesstoken") - }); - if (user) { - const isEmailVerified = user?.get("emailVerified"); + const currentUser = JSON.parse(JSON.stringify(Parse.User.current())); + let isEmailVerified; + isEmailVerified = currentUser?.emailVerified; + if (isEmailVerified) { setIsEmailVerified(isEmailVerified); + } else { + const userQuery = new Parse.Query(Parse.User); + const user = await userQuery.get(currentUser.objectId, { + sessionToken: localStorage.getItem("accesstoken") + }); + if (user) { + isEmailVerified = user?.get("emailVerified"); + setIsEmailVerified(isEmailVerified); + } } setIsLoader(false); @@ -198,27 +199,16 @@ function UserProfile() { const handleDisableDocId = () => { setIsDisableDocId((prevChecked) => !prevChecked); }; + //function to send otp on user mail const handleVerifyBtn = async () => { setIsVerifyModal(true); - await handleSendOTP(); + await handleSendOTP(Parse.User.current().getEmail()); }; 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); - } - }; + //function to use verify email with otp const handleVerifyEmail = async (e) => { e.preventDefault(); setOtpLoader(true); @@ -241,7 +231,8 @@ function UserProfile() { setOtpLoader(false); } }; - const handleReset = async (e) => { + //function to use resend otp for email verification + const handleResend = async (e) => { e.preventDefault(); setOtpLoader(true); await handleSendOTP(); @@ -510,6 +501,7 @@ function UserProfile() { <div className="px-6 py-3"> <label className="mb-2">Enter OTP</label> <input + required type="tel" pattern="[0-9]{4}" className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs" @@ -525,7 +517,7 @@ function UserProfile() { </button> <button className={`${rejectBtn} ml-2`} - onClick={(e) => handleReset(e)} + onClick={(e) => handleResend(e)} > Resend </button> From f6ce1b45fbcda2ed9c4cb6779f5e35f460ced200 Mon Sep 17 00:00:00 2001 From: RaktimaNXG <raktima.c@nxglabs.com> Date: Mon, 6 May 2024 12:11:48 +0530 Subject: [PATCH 17/38] refactor: add comment for email verify functions --- apps/OpenSign/src/constant/Utils.js | 2 +- apps/OpenSign/src/pages/PdfRequestFiles.js | 22 +++++++++------- apps/OpenSign/src/pages/SignyourselfPdf.js | 25 ++++++++++-------- apps/OpenSign/src/pages/UserProfile.js | 30 ++++++++++++---------- 4 files changed, 44 insertions(+), 35 deletions(-) diff --git a/apps/OpenSign/src/constant/Utils.js b/apps/OpenSign/src/constant/Utils.js index 3b3286cf7..63289af0b 100644 --- a/apps/OpenSign/src/constant/Utils.js +++ b/apps/OpenSign/src/constant/Utils.js @@ -2009,7 +2009,7 @@ export const convertPdfArrayBuffer = async (url) => { } }; -//function to use send otp on user email using `SendOTPMailV1` cloud function +//`handleSendOTP` function is used to send otp on user's email using `SendOTPMailV1` cloud function export const handleSendOTP = async (email) => { try { let url = `${localStorage.getItem("baseUrl")}functions/SendOTPMailV1`; diff --git a/apps/OpenSign/src/pages/PdfRequestFiles.js b/apps/OpenSign/src/pages/PdfRequestFiles.js index 211a50c68..4b4cb0781 100644 --- a/apps/OpenSign/src/pages/PdfRequestFiles.js +++ b/apps/OpenSign/src/pages/PdfRequestFiles.js @@ -155,7 +155,7 @@ function PdfRequestFiles() { setOtpLoader(false); alert("OTP sent on you email"); }; - //function to use verify email with otp + //`handleVerifyEmail` function is used to verify email with otp const handleVerifyEmail = async (e) => { e.preventDefault(); setOtpLoader(true); @@ -180,7 +180,7 @@ function PdfRequestFiles() { } }; - //function to send otp on user mail + //`handleVerifyBtn` function is used to send otp on user mail const handleVerifyBtn = async () => { setIsVerifyModal(true); await handleSendOTP(Parse.User.current().getEmail()); @@ -297,13 +297,17 @@ function PdfRequestFiles() { if (isEmailVerified) { setIsEmailVerified(isEmailVerified); } else { - const userQuery = new Parse.Query(Parse.User); - const user = await userQuery.get(currentUser.objectId, { - sessionToken: localStorage.getItem("accesstoken") - }); - if (user) { - isEmailVerified = user?.get("emailVerified"); - setIsEmailVerified(isEmailVerified); + try { + const userQuery = new Parse.Query(Parse.User); + const user = await userQuery.get(currentUser.objectId, { + sessionToken: localStorage.getItem("accesstoken") + }); + if (user) { + isEmailVerified = user?.get("emailVerified"); + setIsEmailVerified(isEmailVerified); + } + } catch (e) { + setHandleError("Error: Something went wrong!"); } } } diff --git a/apps/OpenSign/src/pages/SignyourselfPdf.js b/apps/OpenSign/src/pages/SignyourselfPdf.js index b281482bb..6ad106d93 100644 --- a/apps/OpenSign/src/pages/SignyourselfPdf.js +++ b/apps/OpenSign/src/pages/SignyourselfPdf.js @@ -242,13 +242,17 @@ function SignYourSelf() { if (isEmailVerified) { setIsEmailVerified(isEmailVerified); } else { - const userQuery = new Parse.Query(Parse.User); - const user = await userQuery.get(currentUser.objectId, { - sessionToken: localStorage.getItem("accesstoken") - }); - if (user) { - isEmailVerified = user?.get("emailVerified"); - setIsEmailVerified(isEmailVerified); + try { + const userQuery = new Parse.Query(Parse.User); + const user = await userQuery.get(currentUser.objectId, { + sessionToken: localStorage.getItem("accesstoken") + }); + if (user) { + isEmailVerified = user?.get("emailVerified"); + setIsEmailVerified(isEmailVerified); + } + } catch (e) { + setHandleError("Error: Something went wrong!"); } } } @@ -560,7 +564,7 @@ function SignYourSelf() { setSignKey(key); }; - //function to use resend otp for email verification + //`handleResend` function is used to resend otp for email verification const handleResend = async (e) => { e.preventDefault(); setOtpLoader(true); @@ -568,8 +572,7 @@ function SignYourSelf() { setOtpLoader(false); alert("OTP sent on you email"); }; - - //function to use verify email with otp + //`handleVerifyEmail` function is used to verify email with otp const handleVerifyEmail = async (e) => { e.preventDefault(); setOtpLoader(true); @@ -593,7 +596,7 @@ function SignYourSelf() { setOtpLoader(false); } }; - //function to send otp on user mail + //`handleVerifyBtn` function is used to send otp on user mail const handleVerifyBtn = async () => { setIsVerifyModal(true); await handleSendOTP(Parse.User.current().getEmail()); diff --git a/apps/OpenSign/src/pages/UserProfile.js b/apps/OpenSign/src/pages/UserProfile.js index 9f7c4213a..2e74770b0 100644 --- a/apps/OpenSign/src/pages/UserProfile.js +++ b/apps/OpenSign/src/pages/UserProfile.js @@ -54,22 +54,25 @@ function UserProfile() { setIsDisableDocId(HeaderDocId); } const currentUser = JSON.parse(JSON.stringify(Parse.User.current())); - let isEmailVerified; - isEmailVerified = currentUser?.emailVerified; + let isEmailVerified = currentUser?.emailVerified || false; if (isEmailVerified) { setIsEmailVerified(isEmailVerified); + setIsLoader(false); } else { - const userQuery = new Parse.Query(Parse.User); - const user = await userQuery.get(currentUser.objectId, { - sessionToken: localStorage.getItem("accesstoken") - }); - if (user) { - isEmailVerified = user?.get("emailVerified"); - setIsEmailVerified(isEmailVerified); + try { + const userQuery = new Parse.Query(Parse.User); + const user = await userQuery.get(currentUser.objectId, { + sessionToken: localStorage.getItem("accesstoken") + }); + if (user) { + isEmailVerified = user?.get("emailVerified"); + setIsEmailVerified(isEmailVerified); + setIsLoader(false); + } + } catch (e) { + alert("something went wrong!"); } } - - setIsLoader(false); }; const handleSubmit = async (e) => { e.preventDefault(); @@ -199,7 +202,7 @@ function UserProfile() { const handleDisableDocId = () => { setIsDisableDocId((prevChecked) => !prevChecked); }; - //function to send otp on user mail + //`handleVerifyBtn` function is used to send otp on user mail const handleVerifyBtn = async () => { setIsVerifyModal(true); await handleSendOTP(Parse.User.current().getEmail()); @@ -207,8 +210,7 @@ function UserProfile() { const handleCloseVerifyModal = async () => { setIsVerifyModal(false); }; - - //function to use verify email with otp + //`handleVerifyEmail` function is used to verify email with otp const handleVerifyEmail = async (e) => { e.preventDefault(); setOtpLoader(true); From 09fb01862db851d4fde1ab9518ffa12d8cecb695 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Mon, 6 May 2024 13:29:31 +0530 Subject: [PATCH 18/38] feat: handle encrypted pdf --- apps/OpenSign/src/pages/Form.js | 487 +++++++++++++++++++++----------- apps/OpenSignServer/index.js | 1 - 2 files changed, 328 insertions(+), 160 deletions(-) diff --git a/apps/OpenSign/src/pages/Form.js b/apps/OpenSign/src/pages/Form.js index fd3e521f0..8441cb817 100644 --- a/apps/OpenSign/src/pages/Form.js +++ b/apps/OpenSign/src/pages/Form.js @@ -14,7 +14,8 @@ import { SaveFileSize } from "../constant/saveFileSize"; import { getFileName, toDataUrl } from "../constant/Utils"; import { PDFDocument } from "pdf-lib"; import axios from "axios"; -import { isEnableSubscription } from "../constant/const"; +import { isEnableSubscription, submitBtn } from "../constant/const"; +import ModalUi from "../primitives/ModalUi"; // `Form` render all type of Form on this basis of their provided in path function Form() { @@ -43,7 +44,9 @@ const Forms = (props) => { Description: "", Note: "", TimeToCompleteDays: 15, - SendinOrder: "false" + SendinOrder: "false", + password: "", + file: "" }); const [fileupload, setFileUpload] = useState(""); const [fileload, setfileload] = useState(false); @@ -52,6 +55,8 @@ const Forms = (props) => { const [isAlert, setIsAlert] = useState(false); const [isSubmit, setIsSubmit] = useState(false); const [isErr, setIsErr] = useState(""); + const [isPassword, setIsPassword] = useState(false); + const [isDecrypting, setIsDecrypting] = useState(false); const handleStrInput = (e) => { setFormData({ ...formData, [e.target.name]: e.target.value }); }; @@ -80,6 +85,7 @@ const Forms = (props) => { setpercentage(0); try { let files = e.target.files; + setFormData((prev) => ({ ...prev, file: e.target.files[0] })); if (typeof files[0] !== "undefined") { const mb = Math.round(files[0].size / Math.pow(1024, 2)); if (mb > maxFileSize) { @@ -96,16 +102,83 @@ const Forms = (props) => { await PDFDocument.load(res); handleFileUpload(files[0]); } catch (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); + if (err?.message?.includes("is encrypted")) { + try { + await Parse.Cloud.run("encryptedpdf", { + email: Parse.User.current().getEmail() + }); + } catch (err) { + console.log("err in sending posthog encryptedpdf", err); + } + // console.log("err ", err); + try { + setIsDecrypting(true); + const fileName = files?.[0].name; + const size = files?.[0].size; + const name = sanitizeFileName(fileName); + const url = "https://ai.nxglabs.in/decryptpdf"; //"https://ai.nxglabs.in/decryptpdf"; // + let formData = new FormData(); + formData.append("file", files[0]); + formData.append("password", ""); + const config = { + headers: { + "content-type": "multipart/form-data" + // sessiontoken: Parse.User.current().getSessionToken() + }, + responseType: "blob" + }; + const response = await axios.post(url, formData, config); + const pdfBlob = new Blob([response.data], { + type: "application/pdf" + }); + const pdfFile = new File([pdfBlob], name, { + type: "application/pdf" + }); + setIsDecrypting(false); + setfileload(true); + + // Upload the file to Parse Server + const parseFile = new Parse.File( + name, + pdfFile, + "application/pdf" + ); + + await parseFile.save({ + progress: (progressValue, loaded, total, { type }) => { + if (type === "upload" && progressValue !== null) { + const percentCompleted = Math.round( + (loaded * 100) / total + ); + setpercentage(percentCompleted); + } + } + }); + + // Retrieve the URL of the uploaded file + if (parseFile.url()) { + console.log("parseFile.url() ", parseFile.url()); + setFileUpload(parseFile.url()); + setfileload(false); + const tenantId = localStorage.getItem("TenantId"); + SaveFileSize(size, parseFile.url(), tenantId); + return parseFile.url(); + } + } catch (err) { + console.log("Error uploading file: ", err?.response); + setfileload(false); + setpercentage(0); + if (err?.response?.status === 401) { + setIsPassword(true); + } else { + setIsDecrypting(false); + e.target.value = ""; + } + } + } else { + console.log("err ", err); + setFileUpload(""); + e.target.value = ""; } } } else { @@ -174,7 +247,7 @@ const Forms = (props) => { if (isEnableSubscription) { try { setfileload(true); - const url = "http://tools.opensignlabs.com/docxtopdf"; + const url = "https://ai.nxglabs.in/docxtopdf"; let formData = new FormData(); formData.append("file", files[0]); const config = { @@ -387,7 +460,68 @@ const Forms = (props) => { setpercentage(0); setTimeout(() => setIsReset(false), 50); }; + const handlePasswordSubmit = async (e) => { + e.preventDefault(); + setIsPassword(false); + setfileload(true); + try { + const fileName = formData?.file?.name; + const size = formData?.file?.size; + const name = sanitizeFileName(fileName); + const url = "https://ai.nxglabs.in/decryptpdf"; // + let Data = new FormData(); + Data.append("file", formData?.file); + Data.append("password", formData.password); + const config = { + headers: { + "content-type": "multipart/form-data" + // sessiontoken: Parse.User.current().getSessionToken() + }, + responseType: "blob" + }; + const response = await axios.post(url, Data, config); + const pdfBlob = new Blob([response.data], { + type: "application/pdf" + }); + const pdfFile = new File([pdfBlob], name, { + type: "application/pdf" + }); + setIsDecrypting(false); + // Upload the file to Parse Server + const parseFile = new Parse.File(name, pdfFile, "application/pdf"); + await parseFile.save({ + progress: (progressValue, loaded, total, { type }) => { + if (type === "upload" && progressValue !== null) { + const percentCompleted = Math.round((loaded * 100) / total); + setpercentage(percentCompleted); + } + } + }); + + // Retrieve the URL of the uploaded file + if (parseFile.url()) { + setFormData((prev) => ({ ...prev, password: "" })); + console.log("parseFile.url() ", parseFile.url()); + setFileUpload(parseFile.url()); + setfileload(false); + const tenantId = localStorage.getItem("TenantId"); + SaveFileSize(size, parseFile.url(), tenantId); + return parseFile.url(); + } + } catch (err) { + console.log("Error uploading file: ", err?.response); + setfileload(false); + setpercentage(0); + setFormData((prev) => ({ ...prev, password: "" })); + if (err?.response?.status === 401) { + setIsPassword(true); + } else { + setIsDecrypting(false); + e.target.value = ""; + } + } + }; return ( <div className="shadow-md rounded my-2 p-3 bg-[#ffffff] md:border-[1px] md:border-gray-600/50"> <Title title={props?.title} /> @@ -411,173 +545,208 @@ const Forms = (props) => { ></div> </div> ) : ( - <form onSubmit={handleSubmit}> - <h1 className="text-[20px] font-semibold mb-4">{props?.title}</h1> - {fileload && ( - <div className="flex items-center gap-x-2"> - <div className="h-2 rounded-full w-[200px] md:w-[400px] bg-gray-200"> - <div - className="h-2 rounded-full bg-blue-500" - style={{ width: `${percentage}%` }} - ></div> - </div> - <span className="text-black text-sm">{percentage}%</span> - </div> - )} - <div className="text-xs"> - <label className="block"> - {`File (pdf, png, jpg, jpeg${ - isEnableSubscription ? ", docx)" : ")" - }`} - <span className="text-red-500 text-[13px]">*</span> - </label> - {fileupload.length > 0 ? ( - <div className="flex gap-2 justify-center items-center"> - <div className="flex justify-between items-center px-2 py-2 w-full font-bold rounded border-[1px] border-[#ccc] text-gray-500 bg-white text-[13px]"> - <div className="break-all"> - file selected : {getFileName(fileupload)} - </div> - <div - onClick={() => setFileUpload("")} - className="cursor-pointer px-[10px] text-[20px] font-bold bg-white text-red-500" - > - <i className="fa-solid fa-xmark"></i> - </div> - </div> - {process.env.REACT_APP_DROPBOX_API_KEY && ( - <DropboxChooser - onSuccess={dropboxSuccess} - onCancel={dropboxCancel} - /> - )} - </div> - ) : ( - <div className="flex gap-2 justify-center items-center"> + <> + <ModalUi isOpen={isPassword} title={"Enter Pdf Password"}> + <form onSubmit={handlePasswordSubmit}> + <div className="px-6 py-3"> + {/* <label className="mb-2">Enter OTP</label> */} <input - 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={ - isEnableSubscription - ? "application/pdf,application/vnd.openxmlformats-officedocument.wordprocessingml.document,image/png,image/jpeg" - : "application/pdf,image/png,image/jpeg" - } + type="text" + name="password" + value={formData.password} + onChange={(e) => handleStrInput(e)} + className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs" + placeholder="Enter pdf password" required /> - {process.env.REACT_APP_DROPBOX_API_KEY && ( - <DropboxChooser - onSuccess={dropboxSuccess} - onCancel={dropboxCancel} - /> - )} + </div> + <hr /> + <div className="px-6 my-3"> + <button type="submit" className={submitBtn}> + Submit + </button> + </div> + </form> + </ModalUi> + <form onSubmit={handleSubmit}> + <h1 className="text-[20px] font-semibold mb-4">{props?.title}</h1> + {fileload && ( + <div className="flex items-center gap-x-2"> + <div className="h-2 rounded-full w-[200px] md:w-[400px] bg-gray-200"> + <div + className="h-2 rounded-full bg-blue-500" + style={{ width: `${percentage}%` }} + ></div> + </div> + <span className="text-black text-sm">{percentage}%</span> </div> )} - </div> - <div className="text-xs mt-2"> - <label className="block"> - {props.title === "New Template" - ? "Template Title" - : "Document Title"} - <span className="text-red-500 text-[13px]">*</span> - </label> - <input - name="Name" - className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs" - value={formData.Name} - onChange={(e) => handleStrInput(e)} - required - /> - </div> - <div className="text-xs mt-2"> - <label className="block">Description</label> - <input - name="Description" - className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs" - value={formData.Description} - onChange={(e) => handleStrInput(e)} - /> - </div> - {props.signers && ( - <SignersInput onChange={handleSigners} isReset={isReset} required /> - )} - <div className="text-xs mt-2"> - <label className="block"> - Note<span className="text-red-500 text-[13px]">*</span> - </label> - <input - name="Note" - className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs" - value={formData.Note} - onChange={(e) => handleStrInput(e)} - required - /> - </div> - <SelectFolder - onSuccess={handleFolder} - folderCls={props.Cls} - isReset={isReset} - /> - - {props.title === "Request Signatures" && ( + {isDecrypting && ( + <div className="flex items-center gap-x-2"> + <span className="text-black text-sm"> + Decrypting pdf please wait... + </span> + </div> + )} + <div className="text-xs"> + <label className="block"> + {`File (pdf, png, jpg, jpeg${ + isEnableSubscription ? ", docx)" : ")" + }`} + <span className="text-red-500 text-[13px]">*</span> + </label> + {fileupload.length > 0 ? ( + <div className="flex gap-2 justify-center items-center"> + <div className="flex justify-between items-center px-2 py-2 w-full font-bold rounded border-[1px] border-[#ccc] text-gray-500 bg-white text-[13px]"> + <div className="break-all"> + file selected : {getFileName(fileupload)} + </div> + <div + onClick={() => setFileUpload("")} + className="cursor-pointer px-[10px] text-[20px] font-bold bg-white text-red-500" + > + <i className="fa-solid fa-xmark"></i> + </div> + </div> + {process.env.REACT_APP_DROPBOX_API_KEY && ( + <DropboxChooser + onSuccess={dropboxSuccess} + onCancel={dropboxCancel} + /> + )} + </div> + ) : ( + <div className="flex gap-2 justify-center items-center"> + <input + 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={ + isEnableSubscription + ? "application/pdf,application/vnd.openxmlformats-officedocument.wordprocessingml.document,image/png,image/jpeg" + : "application/pdf,image/png,image/jpeg" + } + required + /> + {process.env.REACT_APP_DROPBOX_API_KEY && ( + <DropboxChooser + onSuccess={dropboxSuccess} + onCancel={dropboxCancel} + /> + )} + </div> + )} + </div> <div className="text-xs mt-2"> <label className="block"> - Time To Complete (Days) + {props.title === "New Template" + ? "Template Title" + : "Document Title"} <span className="text-red-500 text-[13px]">*</span> </label> <input - type="number" - name="TimeToCompleteDays" + name="Name" className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs" - value={formData.TimeToCompleteDays} + value={formData.Name} onChange={(e) => handleStrInput(e)} required /> </div> - )} - {props.title !== "Sign Yourself" && ( <div className="text-xs mt-2"> - <label className="block">Send In Order</label> - <div className="flex items-center gap-2 ml-2 mb-1"> + <label className="block">Description</label> + <input + name="Description" + className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs" + value={formData.Description} + onChange={(e) => handleStrInput(e)} + /> + </div> + {props.signers && ( + <SignersInput + onChange={handleSigners} + isReset={isReset} + required + /> + )} + <div className="text-xs mt-2"> + <label className="block"> + Note<span className="text-red-500 text-[13px]">*</span> + </label> + <input + name="Note" + className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs" + value={formData.Note} + onChange={(e) => handleStrInput(e)} + required + /> + </div> + <SelectFolder + onSuccess={handleFolder} + folderCls={props.Cls} + isReset={isReset} + /> + + {props.title === "Request Signatures" && ( + <div className="text-xs mt-2"> + <label className="block"> + Time To Complete (Days) + <span className="text-red-500 text-[13px]">*</span> + </label> <input - type="radio" - value={"true"} - name="SendinOrder" - checked={formData.SendinOrder === "true"} - className="" - onChange={handleStrInput} + type="number" + name="TimeToCompleteDays" + className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs" + value={formData.TimeToCompleteDays} + onChange={(e) => handleStrInput(e)} + required /> - <div className="text-center">Yes</div> </div> - <div className="flex items-center gap-2 ml-2 mb-1"> - <input - type="radio" - value={"false"} - name="SendinOrder" - checked={formData.SendinOrder === "false"} - onChange={handleStrInput} - /> - <div className="text-center">No</div> + )} + {props.title !== "Sign Yourself" && ( + <div className="text-xs mt-2"> + <label className="block">Send In Order</label> + <div className="flex items-center gap-2 ml-2 mb-1"> + <input + type="radio" + value={"true"} + name="SendinOrder" + checked={formData.SendinOrder === "true"} + className="" + onChange={handleStrInput} + /> + <div className="text-center">Yes</div> + </div> + <div className="flex items-center gap-2 ml-2 mb-1"> + <input + type="radio" + value={"false"} + name="SendinOrder" + checked={formData.SendinOrder === "false"} + onChange={handleStrInput} + /> + <div className="text-center">No</div> + </div> + </div> + )} + <div className="flex items-center mt-3 gap-2 text-white"> + <button + className={`${ + isSubmit && "cursor-progress" + } bg-[#1ab6ce] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 focus:outline-none`} + type="submit" + disabled={isSubmit} + > + Next + </button> + <div + className="cursor-pointer bg-[#188ae2] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 text-center ml-[2px] focus:outline-none" + onClick={() => handleReset()} + > + Reset </div> </div> - )} - <div className="flex items-center mt-3 gap-2 text-white"> - <button - className={`${ - isSubmit && "cursor-progress" - } bg-[#1ab6ce] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 focus:outline-none`} - type="submit" - disabled={isSubmit} - > - Next - </button> - <div - className="cursor-pointer bg-[#188ae2] rounded-sm shadow-md text-[13px] font-semibold uppercase text-white py-1.5 px-2.5 text-center ml-[2px] focus:outline-none" - onClick={() => handleReset()} - > - Reset - </div> - </div> - </form> + </form> + </> )} </div> ); diff --git a/apps/OpenSignServer/index.js b/apps/OpenSignServer/index.js index 2d51c739f..d6ba7ad9b 100644 --- a/apps/OpenSignServer/index.js +++ b/apps/OpenSignServer/index.js @@ -166,7 +166,6 @@ app.use(cors()); app.use(express.json({ limit: '50mb' })); app.use(express.urlencoded({ limit: '50mb', extended: true })); app.use(function (req, res, next) { - console.log('ip', getUserIP(req)); req.headers['x-real-ip'] = getUserIP(req); next(); }); From 7b4fa4fe233e905021495ef0a12fa8c55ef7378c Mon Sep 17 00:00:00 2001 From: RaktimaNXG <raktima.c@nxglabs.com> Date: Mon, 6 May 2024 13:55:15 +0530 Subject: [PATCH 19/38] fix: design of verify email --- apps/OpenSign/src/components/pdf/VerifyEmail.js | 12 ++++++------ apps/OpenSign/src/pages/UserProfile.js | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/OpenSign/src/components/pdf/VerifyEmail.js b/apps/OpenSign/src/components/pdf/VerifyEmail.js index 48ed2b58b..1b40614a1 100644 --- a/apps/OpenSign/src/components/pdf/VerifyEmail.js +++ b/apps/OpenSign/src/components/pdf/VerifyEmail.js @@ -25,7 +25,7 @@ function VerifyEmail(props) { 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" + placeholder="Enter OTP received over email" value={props.otp} onChange={(e) => props.setOtp(e.target.value)} /> @@ -61,10 +61,10 @@ function VerifyEmail(props) { ></div> </div> ) : ( - <div className="p-[15px]"> - <p>Please verify your email !</p> - <div className="h-[1px] bg-[#9f9f9f] w-full"></div> - <div className="m-[15px] "> + <div className="px-6 py-3"> + <p className="mb-2">Please verify your email !</p> + <hr /> + <div className="px-0 mt-3"> <button className={submitBtn} type="submit" @@ -72,7 +72,7 @@ function VerifyEmail(props) { props.handleVerifyBtn(); }} > - Verify + Send OTP </button> </div> </div> diff --git a/apps/OpenSign/src/pages/UserProfile.js b/apps/OpenSign/src/pages/UserProfile.js index 2e74770b0..3d3f6f483 100644 --- a/apps/OpenSign/src/pages/UserProfile.js +++ b/apps/OpenSign/src/pages/UserProfile.js @@ -507,7 +507,7 @@ function UserProfile() { 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" + placeholder="Enter OTP received over email" value={otp} onChange={(e) => setOtp(e.target.value)} /> From 961ec2b2bac395ae0fe9cc6394415328b0214c76 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Mon, 6 May 2024 15:17:53 +0530 Subject: [PATCH 20/38] fix: pdf password modal is not closeable in form --- apps/OpenSign/src/pages/Form.js | 47 ++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/apps/OpenSign/src/pages/Form.js b/apps/OpenSign/src/pages/Form.js index 8441cb817..74f8777fb 100644 --- a/apps/OpenSign/src/pages/Form.js +++ b/apps/OpenSign/src/pages/Form.js @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import { useNavigate, useParams } from "react-router-dom"; import { formJson } from "../json/FormJson"; import AddUser from "../components/AddUser"; @@ -36,6 +36,7 @@ function Form() { const Forms = (props) => { const maxFileSize = 20; const abortController = new AbortController(); + const inputFileRef = useRef(null); const navigate = useNavigate(); const [signers, setSigners] = useState([]); const [folder, setFolder] = useState({ ObjectId: "", Name: "" }); @@ -57,7 +58,9 @@ const Forms = (props) => { const [isErr, setIsErr] = useState(""); const [isPassword, setIsPassword] = useState(false); const [isDecrypting, setIsDecrypting] = useState(false); + const [isCorrectPass, setIsCorrectPass] = useState(true); const handleStrInput = (e) => { + setIsCorrectPass(true); setFormData({ ...formData, [e.target.name]: e.target.value }); }; useEffect(() => { @@ -157,7 +160,6 @@ const Forms = (props) => { // Retrieve the URL of the uploaded file if (parseFile.url()) { - console.log("parseFile.url() ", parseFile.url()); setFileUpload(parseFile.url()); setfileload(false); const tenantId = localStorage.getItem("TenantId"); @@ -165,12 +167,12 @@ const Forms = (props) => { return parseFile.url(); } } catch (err) { - console.log("Error uploading file: ", err?.response); setfileload(false); setpercentage(0); if (err?.response?.status === 401) { setIsPassword(true); } else { + console.log("Error uploading file: ", err?.response); setIsDecrypting(false); e.target.value = ""; } @@ -502,7 +504,6 @@ const Forms = (props) => { // Retrieve the URL of the uploaded file if (parseFile.url()) { setFormData((prev) => ({ ...prev, password: "" })); - console.log("parseFile.url() ", parseFile.url()); setFileUpload(parseFile.url()); setfileload(false); const tenantId = localStorage.getItem("TenantId"); @@ -510,18 +511,30 @@ const Forms = (props) => { return parseFile.url(); } } catch (err) { - console.log("Error uploading file: ", err?.response); setfileload(false); setpercentage(0); - setFormData((prev) => ({ ...prev, password: "" })); if (err?.response?.status === 401) { setIsPassword(true); + setIsCorrectPass(false); } else { + console.log("Error uploading file: ", err?.response); + setFormData((prev) => ({ ...prev, password: "" })); setIsDecrypting(false); - e.target.value = ""; + if (inputFileRef.current) { + inputFileRef.current.value = ""; // Set file input value to empty string + } } } }; + const handeCloseModal = () => { + setIsPassword(false); + setFormData((prev) => ({ ...prev, file: "", password: "" })); + setIsDecrypting(false); + setfileload(false); + if (inputFileRef.current) { + inputFileRef.current.value = ""; // Set file input value to empty string + } + }; return ( <div className="shadow-md rounded my-2 p-3 bg-[#ffffff] md:border-[1px] md:border-gray-600/50"> <Title title={props?.title} /> @@ -546,10 +559,14 @@ const Forms = (props) => { </div> ) : ( <> - <ModalUi isOpen={isPassword} title={"Enter Pdf Password"}> + <ModalUi + isOpen={isPassword} + handleClose={() => handeCloseModal()} + title={"Enter Pdf Password"} + > <form onSubmit={handlePasswordSubmit}> - <div className="px-6 py-3"> - {/* <label className="mb-2">Enter OTP</label> */} + <div className="px-6 pt-3 pb-2"> + <label className="mb-2 text-xs">Password</label> <input type="text" name="password" @@ -559,6 +576,15 @@ const Forms = (props) => { placeholder="Enter pdf password" required /> + {!isCorrectPass ? ( + <p className="ml-2 text-[11px] text-red-600"> + Please provide correct password + </p> + ) : ( + <p className="ml-2 text-[11px] text-transparent text-red-600"> + . + </p> + )} </div> <hr /> <div className="px-6 my-3"> @@ -621,6 +647,7 @@ 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)} + ref={inputFileRef} accept={ isEnableSubscription ? "application/pdf,application/vnd.openxmlformats-officedocument.wordprocessingml.document,image/png,image/jpeg" From b01618c8caf8f84f53e2ada69fc8fb64cce2c7e8 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Mon, 6 May 2024 17:16:28 +0530 Subject: [PATCH 21/38] fix: Ip address overlap with security level in certificate --- .../parsefunction/pdf/GenerateCertificate.js | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/apps/OpenSignServer/cloud/parsefunction/pdf/GenerateCertificate.js b/apps/OpenSignServer/cloud/parsefunction/pdf/GenerateCertificate.js index 0f3081c5e..0845e7fbd 100644 --- a/apps/OpenSignServer/cloud/parsefunction/pdf/GenerateCertificate.js +++ b/apps/OpenSignServer/cloud/parsefunction/pdf/GenerateCertificate.js @@ -15,6 +15,7 @@ export default async function GenerateCertificate(docDetails) { const title = 25; const subtitle = 20; const text = 14; + const timeText = 11; const textKeyColor = rgb(0.12, 0.12, 0.12); const textValueColor = rgb(0.3, 0.3, 0.3); const completedAt = new Date(); @@ -280,17 +281,17 @@ export default async function GenerateCertificate(docDetails) { }); page.drawText('Viewed on :', { - x: half, + x: half +55, y: yPosition2, - size: text, + size: timeText, font: timesRomanFont, color: textKeyColor, }); page.drawText(`${new Date(x.ViewedOn).toUTCString()}`, { - x: half + 75, + x: half + 112, y: yPosition2, - size: text, + size: timeText, font: timesRomanFont, color: textValueColor, }); @@ -312,17 +313,17 @@ export default async function GenerateCertificate(docDetails) { }); page.drawText('Signed on :', { - x: half, - y: yPosition3, - size: text, + x: half + 55, + y: yPosition3 + 5, + size: timeText, font: timesRomanFont, color: textKeyColor, }); page.drawText(`${new Date(x.SignedOn).toUTCString()}`, { - x: half + 70, - y: yPosition3, - size: text, + x: half + 108, + y: yPosition3 + 5, + size: timeText, font: timesRomanFont, color: textValueColor, }); @@ -338,22 +339,22 @@ export default async function GenerateCertificate(docDetails) { page.drawText(x?.ipAddress, { x: 100, y: yPosition4, - size: text, + size: 13, font: timesRomanFont, color: textValueColor, }); page.drawText('Security level :', { - x: half, - y: yPosition4, - size: text, + x: half + 55, + y: yPosition4 + 10, + size: timeText, font: timesRomanFont, color: textKeyColor, }); page.drawText(`Email, OTP Auth`, { - x: half + 90, - y: yPosition4, - size: text, + x: half + 125, + y: yPosition4 + 10, + size: timeText, font: timesRomanFont, color: textValueColor, }); @@ -368,7 +369,7 @@ export default async function GenerateCertificate(docDetails) { page.drawRectangle({ x: 98, - y: yPosition5 - 27, + y: yPosition5 - 30, width: 104, height: 44, borderColor: rgb(0.22, 0.18, 0.47), @@ -377,7 +378,7 @@ export default async function GenerateCertificate(docDetails) { if (embedPng) { page.drawImage(embedPng, { x: 100, - y: yPosition5 - 25, + y: yPosition5 - 27, width: 100, height: 40, }); From 5a68e18c48524e856c87d03c6d3b049ee16f8c69 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Mon, 6 May 2024 18:46:12 +0530 Subject: [PATCH 22/38] fix: pdf url are not downloadable after some time --- apps/OpenSign/src/components/pdf/PdfHeader.js | 249 +++++++++--------- .../src/primitives/GetReportDisplay.js | 29 +- apps/OpenSignServer/cloud/main.js | 4 +- .../cloud/parsefunction/getSignedUrl.js | 23 ++ 4 files changed, 165 insertions(+), 140 deletions(-) diff --git a/apps/OpenSign/src/components/pdf/PdfHeader.js b/apps/OpenSign/src/components/pdf/PdfHeader.js index 896072f8e..eeac61ee3 100644 --- a/apps/OpenSign/src/components/pdf/PdfHeader.js +++ b/apps/OpenSign/src/components/pdf/PdfHeader.js @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useState } from "react"; import PrevNext from "./PrevNext"; import printModule from "print-js"; import { getBase64FromUrl } from "../../constant/Utils"; @@ -7,8 +7,9 @@ import "../../styles/signature.css"; import * as DropdownMenu from "@radix-ui/react-dropdown-menu"; import { useNavigate } from "react-router-dom"; import { themeColor } from "../../constant/const"; -import Certificate from "./Certificate"; -import { PDFDownloadLink } from "@react-pdf/renderer"; +import axios from "axios"; +import ModalUi from "../../primitives/ModalUi"; +import Parse from "parse"; function Header({ isPdfRequestFiles, @@ -39,6 +40,7 @@ function Header({ signerPos && signerPos?.filter((data) => data.Role !== "prefill"); const isMobile = window.innerWidth < 767; const navigate = useNavigate(); + const [isCertificate, setIsCertificate] = useState(false); const isGuestSigner = localStorage.getItem("isGuestSigner"); //for go to previous page function previousPage() { @@ -61,30 +63,43 @@ function Header({ const handleToPrint = async (event) => { event.preventDefault(); - const pdf = await getBase64FromUrl(pdfUrl); - const isAndroidDevice = navigator.userAgent.match(/Android/i); - const isAppleDevice = - (/iPad|iPhone|iPod/.test(navigator.platform) || - (navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1)) && - !window.MSStream; - if (isAndroidDevice || isAppleDevice) { - const byteArray = Uint8Array.from( - atob(pdf) - .split("") - .map((char) => char.charCodeAt(0)) - ); - const blob = new Blob([byteArray], { type: "application/pdf" }); - const blobUrl = URL.createObjectURL(blob); - window.open(blobUrl, "_blank"); - } else { - printModule({ printable: pdf, type: "pdf", base64: true }); + try { + const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl }); + const pdf = await getBase64FromUrl(url); + const isAndroidDevice = navigator.userAgent.match(/Android/i); + const isAppleDevice = + (/iPad|iPhone|iPod/.test(navigator.platform) || + (navigator.platform === "MacIntel" && + navigator.maxTouchPoints > 1)) && + !window.MSStream; + if (isAndroidDevice || isAppleDevice) { + const byteArray = Uint8Array.from( + atob(pdf) + .split("") + .map((char) => char.charCodeAt(0)) + ); + const blob = new Blob([byteArray], { type: "application/pdf" }); + const blobUrl = URL.createObjectURL(blob); + window.open(blobUrl, "_blank"); + } else { + printModule({ printable: pdf, type: "pdf", base64: true }); + } + } catch (err) { + console.log("err in getsignedurl", err); + alert("something went wrong, please try again later."); } }; //handle download signed pdf - const handleDownloadPdf = () => { + const handleDownloadPdf = async () => { const pdfName = pdfDetails[0] && pdfDetails[0].Name; - saveAs(pdfUrl, `${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`); + try { + const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl }); + saveAs(url, `${sanitizeFileName(pdfName)}_signed_by_OpenSign™.pdf`); + } catch (err) { + console.log("err in getsignedurl", err); + alert("something went wrong, please try again later."); + } }; const sanitizeFileName = (pdfName) => { @@ -102,55 +117,41 @@ function Header({ } catch (err) { console.log("err in download in certificate", err); } + } else { + setIsCertificate(true); + try { + const data = { + docId: pdfDetails[0]?.objectId + }; + const docDetails = await axios.post( + `${localStorage.getItem("baseUrl")}functions/getDocument`, + data, + { + headers: { + "Content-Type": "application/json", + "X-Parse-Application-Id": localStorage.getItem("parseAppId"), + sessionToken: localStorage.getItem("accesstoken") + } + } + ); + if (docDetails.data && docDetails.data.result) { + const doc = docDetails.data.result; + if (doc?.CertificateUrl) { + await fetch(doc?.CertificateUrl); + const certificateUrl = doc?.CertificateUrl; + saveAs(certificateUrl, `Certificate_signed_by_OpenSign™.pdf`); + setIsCertificate(false); + } else { + setIsCertificate(true); + } + } + } catch (err) { + console.log("err in download in certificate", err); + alert("something went wrong, please try again later."); + } } }; - const GenerateCertificate = () => { - //after generate download certifcate pdf - const handleDownload = (pdfBlob, fileName) => { - if (pdfBlob) { - const url = window.URL.createObjectURL(pdfBlob); - // Create a temporary anchor element - const link = document.createElement("a"); - link.href = url; - link.download = fileName; - // Append the anchor to the body - document.body.appendChild(link); - // Programmatically click the anchor to trigger the download - link.click(); - // Remove the anchor from the body - document.body.removeChild(link); - // Release the object URL to free up resources - window.URL.revokeObjectURL(url); - } - }; - - return ( - <PDFDownloadLink - onClick={(e) => e.preventDefault()} - style={{ textDecoration: "none", zIndex: "35" }} - document={<Certificate pdfData={pdfDetails} />} - > - {({ blob, loading }) => ( - <button - onClick={() => - handleDownload( - blob, - `completion certificate-${ - pdfDetails[0] && pdfDetails[0].Name - }.pdf` - ) - } - disabled={loading} - className=" md:bg-[#08bc66] border-none focus:outline-none flex flex-row items-center md:shadow md:rounded-[3px] py-[3px] md:px-[11px] md:text-white text-black md:font-[500] text-[13px] mr-[5px]" - > - <i className="fa-solid fa-award py-[3px]" aria-hidden="true"></i> - <span className="md:hidden lg:block ml-1">Certificate</span> - </button> - )} - </PDFDownloadLink> - ); - }; return ( <div style={{ padding: "5px 0px 5px 0px" }} className="mobileHead"> {isMobile && isShowHeader ? ( @@ -240,32 +241,24 @@ function Header({ </div> </DropdownMenu.Item> {isCompleted && ( - <> - {pdfDetails[0] && pdfDetails[0]?.CertificateUrl ? ( - <DropdownMenu.Item - className="DropdownMenuItem" - onClick={() => handleDownloadCertificate()} - > - <div - style={{ - border: "none", - backgroundColor: "#fff" - }} - > - <i - className="fa-solid fa-award" - style={{ marginRight: "2px" }} - aria-hidden="true" - ></i> - Certificate - </div> - </DropdownMenu.Item> - ) : ( - <DropdownMenu.Item className="DropdownMenuItem"> - <GenerateCertificate /> - </DropdownMenu.Item> - )} - </> + <DropdownMenu.Item + className="DropdownMenuItem" + onClick={() => handleDownloadCertificate()} + > + <div + style={{ + border: "none", + backgroundColor: "#fff" + }} + > + <i + className="fa-solid fa-award" + style={{ marginRight: "2px" }} + aria-hidden="true" + ></i> + Certificate + </div> + </DropdownMenu.Item> )} {isSignYourself && ( <DropdownMenu.Item @@ -442,25 +435,17 @@ function Header({ alreadySign ? ( <div style={{ display: "flex", flexDirection: "row" }}> {isCompleted && ( - <> - {pdfDetails[0] && pdfDetails[0]?.CertificateUrl ? ( - <button - type="button" - onClick={() => handleDownloadCertificate()} - className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#08bc66]" - > - <i - className="fa-solid fa-award py-[3px]" - aria-hidden="true" - ></i> - <span className="hidden lg:block ml-1"> - Certificate - </span> - </button> - ) : ( - <GenerateCertificate /> - )} - </> + <button + type="button" + onClick={() => handleDownloadCertificate()} + className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#08bc66]" + > + <i + className="fa-solid fa-award py-[3px]" + aria-hidden="true" + ></i> + <span className="hidden lg:block ml-1">Certificate</span> + </button> )} <button @@ -520,23 +505,17 @@ function Header({ ) : isCompleted ? ( <div style={{ display: "flex", flexDirection: "row" }}> {isCompleted && ( - <> - {pdfDetails[0] && pdfDetails[0]?.CertificateUrl ? ( - <button - type="button" - onClick={() => handleDownloadCertificate()} - className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#08bc66]" - > - <i - className="fa-solid fa-award py-[3px]" - aria-hidden="true" - ></i> - <span className="hidden lg:block ml-1">Certificate</span> - </button> - ) : ( - <GenerateCertificate /> - )} - </> + <button + type="button" + onClick={() => handleDownloadCertificate()} + className="flex flex-row items-center shadow rounded-[3px] py-[3px] px-[11px] text-white font-[500] text-[13px] mr-[5px] bg-[#08bc66]" + > + <i + className="fa-solid fa-award py-[3px]" + aria-hidden="true" + ></i> + <span className="hidden lg:block ml-1">Certificate</span> + </button> )} <button onClick={handleToPrint} @@ -592,6 +571,16 @@ function Header({ )} </div> )} + <ModalUi + isOpen={isCertificate} + title={"Generating certificate"} + handleClose={() => setIsCertificate(false)} + > + <div className="p-3 md:p-5 text-[13px] md:text-base text-center"> + <p>Completion certificate is generating,</p> + <p>please wait for some time if not download try again later</p> + </div> + </ModalUi> </div> ); } diff --git a/apps/OpenSign/src/primitives/GetReportDisplay.js b/apps/OpenSign/src/primitives/GetReportDisplay.js index 8064e7382..b27e32fdb 100644 --- a/apps/OpenSign/src/primitives/GetReportDisplay.js +++ b/apps/OpenSign/src/primitives/GetReportDisplay.js @@ -10,6 +10,7 @@ import Tooltip from "./Tooltip"; import { RWebShare } from "react-web-share"; import Tour from "reactour"; import Parse from "parse"; +import { saveAs } from "file-saver"; const ReportTable = (props) => { const navigate = useNavigate(); @@ -420,6 +421,20 @@ const ReportTable = (props) => { ); } }; + + // `handleDownload` is used to get valid doc url available in completed report + const handleDownload = async (item) => { + const url = item?.SignedUrl || item?.URL || ""; + if (url) { + try { + const signedUrl = await Parse.Cloud.run("getsignedurl", { url: url }); + saveAs(signedUrl); + } catch (err) { + console.log("err in getsignedurl", err); + alert("something went wrong, please try again later."); + } + } + }; return ( <div className="relative"> {Object.keys(actLoader)?.length > 0 && ( @@ -562,18 +577,14 @@ const ReportTable = (props) => { </td> )} <td className="px-4 py-2"> - <a - target="_blank" - rel="noopener noreferrer" - title={item?.URL} - href={item?.URL} - download={item?.URL} - className="text-[blue] hover:text-[blue] hover:underline" + <button + onClick={() => handleDownload(item)} + className="text-[blue] hover:text-[blue] hover:underline focus:outline-none" + title={"Download"} > {item?.URL ? "Download" : "-"} - </a> + </button> </td> - <td className="px-4 py-2"> {formatRow(item?.ExtUserPtr)} </td> diff --git a/apps/OpenSignServer/cloud/main.js b/apps/OpenSignServer/cloud/main.js index 652e69e53..bc6cb66a2 100644 --- a/apps/OpenSignServer/cloud/main.js +++ b/apps/OpenSignServer/cloud/main.js @@ -36,6 +36,7 @@ import TenantAterFind from './parsefunction/TenantAfterFind.js'; import saveSubscription from './parsefunction/saveSubscription.js'; import VerifyEmail from './parsefunction/VerifyEmail.js'; import encryptedpdf from './parsefunction/encryptedPdf.js'; +import { getSignedUrl } from './parsefunction/getSignedUrl.js'; Parse.Cloud.define('AddUserToRole', addUserToGroups); Parse.Cloud.define('UserGroups', getUserGroups); @@ -74,4 +75,5 @@ Parse.Cloud.afterFind('contracts_Signature', SignatureAfterFind); Parse.Cloud.afterFind('partners_Tenant', TenantAterFind); Parse.Cloud.define('savesubscription', saveSubscription); Parse.Cloud.define('verifyemail', VerifyEmail); -Parse.Cloud.define('encryptedpdf', encryptedpdf) +Parse.Cloud.define('encryptedpdf', encryptedpdf); +Parse.Cloud.define('getsignedurl', getSignedUrl); diff --git a/apps/OpenSignServer/cloud/parsefunction/getSignedUrl.js b/apps/OpenSignServer/cloud/parsefunction/getSignedUrl.js index d2869b549..642b41fdd 100644 --- a/apps/OpenSignServer/cloud/parsefunction/getSignedUrl.js +++ b/apps/OpenSignServer/cloud/parsefunction/getSignedUrl.js @@ -1,4 +1,5 @@ import AWS from 'aws-sdk'; +import { useLocal } from '../../Utils.js'; const credentials = { accessKeyId: process.env.DO_ACCESS_KEY_ID, secretAccessKey: process.env.DO_SECRET_ACCESS_KEY, @@ -24,3 +25,25 @@ export default function getPresignedUrl(url) { }); return presignedGETURL; } + +export async function getSignedUrl(request) { + try { + const url = request.params.url; + if (!request?.user) { + throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.'); + } else { + if (useLocal !== 'true') { + const presignedUrl = getPresignedUrl(url); + return presignedUrl; + } else { + return url; + } + } + } catch (err) { + console.log('error in getsignedurl', err); + const code = err.code || 400; + const msg = err.message; + const error = new Parse.Error(code, msg); + throw error; + } +} From bddebf2758d1c108666d4eb3aa3398d980927584 Mon Sep 17 00:00:00 2001 From: Zathiel | Sys <26553418+Zathiel@users.noreply.github.com> Date: Mon, 6 May 2024 13:18:04 +0000 Subject: [PATCH 23/38] deps : upgrade pdfkit, nodemailer, mongoose, express, eslint package version --- apps/OpenSignServer/package-lock.json | 46 +++++++++++++-------------- apps/OpenSignServer/package.json | 6 ++-- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/apps/OpenSignServer/package-lock.json b/apps/OpenSignServer/package-lock.json index 4d464842a..ba80c14a1 100644 --- a/apps/OpenSignServer/package-lock.json +++ b/apps/OpenSignServer/package-lock.json @@ -14,19 +14,19 @@ "axios": "^1.6.8", "cors": "^2.8.5", "dotenv": "^16.4.5", - "express": "^4.18.3", + "express": "^4.19.2", "express-sse": "^0.5.3", "form-data": "^4.0.0", "generate-api-key": "^1.0.2", "googleapis": "^134.0.0", "jsonschema": "^1.4.1", "mailgun.js": "^10.2.1", - "mongoose": "^8.2.2", + "mongoose": "^8.3.3", "multer": "^1.4.5-lts.1", "multer-s3": "^3.0.1", "node-forge": "^1.3.1", "node-signpdf": "^1.5.1", - "nodemailer": "^6.9.12", + "nodemailer": "^6.9.13", "parse": "^5.0.0", "parse-dbtool": "^1.2.0", "parse-server": "^6.5.5", @@ -4123,9 +4123,9 @@ } }, "node_modules/bson": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/bson/-/bson-6.3.0.tgz", - "integrity": "sha512-balJfqwwTBddxfnidJZagCBPP/f48zj9Sdp3OJswREOgsJzHiQSaOIAtApSgDQFYgHqAvFkp53AFSqjMDZoTFw==", + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.7.0.tgz", + "integrity": "sha512-w2IquM5mYzYZv6rs3uN2DZTOBe2a0zXLj53TGDqwF4l6Sz/XsISrisXOJihArF9+BZ6Cq/GjVht7Sjfmri7ytQ==", "engines": { "node": ">=16.20.1" } @@ -7595,9 +7595,9 @@ } }, "node_modules/kareem": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", - "integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==", + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", + "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==", "engines": { "node": ">=12.0.0" } @@ -7961,12 +7961,12 @@ } }, "node_modules/mongodb": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.3.0.tgz", - "integrity": "sha512-tt0KuGjGtLUhLoU263+xvQmPHEGTw5LbcNC73EoFRYgSHwZt5tsoJC110hDyO1kjQzpgNrpdcSza9PknWN4LrA==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.5.0.tgz", + "integrity": "sha512-Fozq68InT+JKABGLqctgtb8P56pRrJFkbhW0ux+x1mdHeyinor8oNzJqwLjV/t5X5nJGfTlluxfyMnOXNggIUA==", "dependencies": { - "@mongodb-js/saslprep": "^1.1.0", - "bson": "^6.2.0", + "@mongodb-js/saslprep": "^1.1.5", + "bson": "^6.4.0", "mongodb-connection-string-url": "^3.0.0" }, "engines": { @@ -8312,13 +8312,13 @@ } }, "node_modules/mongoose": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.2.2.tgz", - "integrity": "sha512-6sMxe1d3k/dBjiOX4ExNTNOP0g1x0iq8eXyg+ttgIXM3HLnQ0IUyXRwVVAPFFY6O4/8uYN5dB0Ec72FrexbPpw==", + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.3.3.tgz", + "integrity": "sha512-3kSk0db9DM2tLttCdS6WNRqewPleamFEa4Vz/Qldc0dB4Zow/FiZxb9GExHTJjBZQ9T2xiGleQ3GzRrES3hhsA==", "dependencies": { - "bson": "^6.2.0", - "kareem": "2.5.1", - "mongodb": "6.3.0", + "bson": "^6.5.0", + "kareem": "2.6.3", + "mongodb": "6.5.0", "mpath": "0.9.0", "mquery": "5.0.0", "ms": "2.1.3", @@ -8503,9 +8503,9 @@ } }, "node_modules/nodemailer": { - "version": "6.9.12", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.12.tgz", - "integrity": "sha512-pnLo7g37Br3jXbF0bl5DekBJihm2q+3bB3l2o/B060sWmb5l+VqeScAQCBqaQ+5ezRZFzW5SciZNGdRDEbq89w==", + "version": "6.9.13", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.13.tgz", + "integrity": "sha512-7o38Yogx6krdoBf3jCAqnIN4oSQFx+fMa0I7dK1D+me9kBxx12D+/33wSb+fhOCtIxvYJ+4x4IMEhmhCKfAiOA==", "engines": { "node": ">=6.0.0" } diff --git a/apps/OpenSignServer/package.json b/apps/OpenSignServer/package.json index f3773756c..e1546c204 100644 --- a/apps/OpenSignServer/package.json +++ b/apps/OpenSignServer/package.json @@ -23,19 +23,19 @@ "axios": "^1.6.8", "cors": "^2.8.5", "dotenv": "^16.4.5", - "express": "^4.18.3", + "express": "^4.19.2", "express-sse": "^0.5.3", "form-data": "^4.0.0", "generate-api-key": "^1.0.2", "googleapis": "^134.0.0", "jsonschema": "^1.4.1", "mailgun.js": "^10.2.1", - "mongoose": "^8.2.2", + "mongoose": "^8.3.3", "multer": "^1.4.5-lts.1", "multer-s3": "^3.0.1", "node-forge": "^1.3.1", "node-signpdf": "^1.5.1", - "nodemailer": "^6.9.12", + "nodemailer": "^6.9.13", "parse": "^5.0.0", "parse-dbtool": "^1.2.0", "parse-server": "^6.5.5", From 23b48838ae7af9c029a64b26b11ca5640a79e8fd Mon Sep 17 00:00:00 2001 From: Zathiel | Sys <26553418+Zathiel@users.noreply.github.com> Date: Mon, 6 May 2024 13:18:57 +0000 Subject: [PATCH 24/38] deps : upgrade prettier, pretty-quick, postcss, autoprefixer, @babel/runtime-corejs2, reactour, react-rnd, @react-pdf/renderer, @formkit/auto-animate --- apps/OpenSign/package-lock.json | 198 +++++++++++++------------------- apps/OpenSign/package.json | 14 +-- 2 files changed, 86 insertions(+), 126 deletions(-) diff --git a/apps/OpenSign/package-lock.json b/apps/OpenSign/package-lock.json index 4cc4ec3d6..a4b48dc59 100644 --- a/apps/OpenSign/package-lock.json +++ b/apps/OpenSign/package-lock.json @@ -8,9 +8,9 @@ "name": "open_sign", "version": "0.1.0", "dependencies": { - "@formkit/auto-animate": "^0.8.1", + "@formkit/auto-animate": "^0.8.2", "@radix-ui/themes": "^2.0.3", - "@react-pdf/renderer": "^3.4.0", + "@react-pdf/renderer": "^3.4.4", "@reduxjs/toolkit": "^2.2.1", "axios": "^1.6.8", "file-saver": "^2.0.5", @@ -36,7 +36,7 @@ "react-pdf": "^7.7.1", "react-quill": "^2.0.0", "react-redux": "^9.1.0", - "react-rnd": "^10.4.1", + "react-rnd": "^10.4.10", "react-router-dom": "^6.22.3", "react-scripts": "5.0.1", "react-scrollbars-custom": "^4.1.1", @@ -44,7 +44,7 @@ "react-signature-canvas": "^1.0.6", "react-tooltip": "^5.26.3", "react-web-share": "^2.0.2", - "reactour": "^1.19.2", + "reactour": "^1.19.3", "redux": "^5.0.1", "redux-thunk": "^3.1.0", "regex-parser": "^2.3.0", @@ -53,14 +53,14 @@ "web-vitals": "^2.1.4" }, "devDependencies": { - "@babel/runtime-corejs2": "^7.24.1", - "autoprefixer": "^10.4.18", + "@babel/runtime-corejs2": "^7.24.5", + "autoprefixer": "^10.4.19", "commitizen": "^4.3.0", "eslint": "^8.57.0", "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-react": "^7.34.1", "lint-staged": "^15.2.2", - "postcss": "^8.4.37", + "postcss": "^8.4.38", "prettier": "^2.8.8", "pretty-quick": "^3.3.1", "tailwindcss": "^3.4.1" @@ -1994,9 +1994,9 @@ } }, "node_modules/@babel/runtime-corejs2": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.24.1.tgz", - "integrity": "sha512-De0q0utpUPiXnc7+B7Ku86mJ0eDItC/v3uFa/lQkq63XnHyZiytDHeCIvechlnVwwpU2ChjGF7c3I+mBrTudwg==", + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs2/-/runtime-corejs2-7.24.5.tgz", + "integrity": "sha512-cC9jiO6s/IN+xwCHYy1AGrcFJ4bwgIwb8HX1KaoEpRsznLlO4x9eBP6AX7RIeMSWlQqEj2WHox637OS8cDq6Ew==", "dev": true, "dependencies": { "core-js": "^2.6.12", @@ -2989,9 +2989,9 @@ "integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==" }, "node_modules/@formkit/auto-animate": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@formkit/auto-animate/-/auto-animate-0.8.1.tgz", - "integrity": "sha512-0/Z2cuNXWVVIG/l0SpcHAWFhGdvLJ8DRvEfRWvmojtmRWfEy+LWNwgDazbZqY0qQYtkHcoEK3jBLkhiZaB/4Ig==" + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/@formkit/auto-animate/-/auto-animate-0.8.2.tgz", + "integrity": "sha512-SwPWfeRa5veb1hOIBMdzI+73te5puUBHmqqaF1Bu7FjvxlYSz/kJcZKSa9Cg60zL0uRNeJL2SbRxV6Jp6Q1nFQ==" }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", @@ -5381,41 +5381,41 @@ } }, "node_modules/@react-pdf/font": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/@react-pdf/font/-/font-2.4.4.tgz", - "integrity": "sha512-yjK5eSY+LcbxS0m+sOYln8GdgIbUgti4xjwf14kx8OSsOMJQJyHFALHMh2cLcKJR9yZeqVDo1FwCsY6gw1yCkg==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@react-pdf/font/-/font-2.5.1.tgz", + "integrity": "sha512-Hyb2zBb92Glc3lvhmJfy4dO2Mj29KB26Uk12Ua9EhKAdiuCTLBqgP8Oe1cGwrvDI7xA4OOcwvBMdYh0vhOUHzA==", "dependencies": { "@babel/runtime": "^7.20.13", - "@react-pdf/types": "^2.4.1", + "@react-pdf/types": "^2.5.0", "cross-fetch": "^3.1.5", "fontkit": "^2.0.2", "is-url": "^1.2.4" } }, "node_modules/@react-pdf/image": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@react-pdf/image/-/image-2.3.5.tgz", - "integrity": "sha512-YK8oeD4cYTbX2KwdsY5DEDpvsKQoGbf3kwlxAQwYm8wrEol8V/5oCl8H8ZqdZIYQhx2XPp63cocQfMCZGwf+4w==", + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/@react-pdf/image/-/image-2.3.6.tgz", + "integrity": "sha512-7iZDYZrZlJqNzS6huNl2XdMcLFUo68e6mOdzQeJ63d5eApdthhSHBnkGzHfLhH5t8DCpZNtClmklzuLL63ADfw==", "dependencies": { "@babel/runtime": "^7.20.13", "@react-pdf/png-js": "^2.3.1", "cross-fetch": "^3.1.5", - "jay-peg": "^1.0.1" + "jay-peg": "^1.0.2" } }, "node_modules/@react-pdf/layout": { - "version": "3.11.3", - "resolved": "https://registry.npmjs.org/@react-pdf/layout/-/layout-3.11.3.tgz", - "integrity": "sha512-SwfIWmZl3yZDgb4Qa9ebjAZTd/FyI5E2curSfra6SuAkrsEF037cmVfxCTgYCkPwpVMv/omOENPI+WjhOKPofA==", + "version": "3.12.1", + "resolved": "https://registry.npmjs.org/@react-pdf/layout/-/layout-3.12.1.tgz", + "integrity": "sha512-BxSeykDxvADlpe4OGtQ7NH46QXq3uImAYsTHOPLCwbXMniQ1O3uCBx7H+HthxkCNshgYVPp9qS3KyvQv/oIZwg==", "dependencies": { "@babel/runtime": "^7.20.13", "@react-pdf/fns": "2.2.1", - "@react-pdf/image": "^2.3.5", - "@react-pdf/pdfkit": "^3.1.7", + "@react-pdf/image": "^2.3.6", + "@react-pdf/pdfkit": "^3.1.10", "@react-pdf/primitives": "^3.1.1", - "@react-pdf/stylesheet": "^4.2.4", + "@react-pdf/stylesheet": "^4.2.5", "@react-pdf/textkit": "^4.4.1", - "@react-pdf/types": "^2.4.1", + "@react-pdf/types": "^2.5.0", "cross-fetch": "^3.1.5", "emoji-regex": "^10.3.0", "queue": "^6.0.1", @@ -5428,16 +5428,16 @@ "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==" }, "node_modules/@react-pdf/pdfkit": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@react-pdf/pdfkit/-/pdfkit-3.1.7.tgz", - "integrity": "sha512-iXMmATqW5Aycpzm8nNsZOHV/DX2lpd8l5VkaTy1HjzHgQ3MHoV45BHwRph53soEqoPo7rUfpjuSi7X3hngPWGA==", + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/@react-pdf/pdfkit/-/pdfkit-3.1.10.tgz", + "integrity": "sha512-P/qPBtCFo2HDJD0i6NfbmoBRrsOVO8CIogYsefwG4fklTo50zNgnMM5U1WLckTuX8Qt1ThiQuokmTG5arheblA==", "dependencies": { "@babel/runtime": "^7.20.13", "@react-pdf/png-js": "^2.3.1", "browserify-zlib": "^0.2.0", "crypto-js": "^4.2.0", "fontkit": "^2.0.2", - "jay-peg": "^1.0.1", + "jay-peg": "^1.0.2", "vite-compatible-readable-stream": "^3.6.1" } }, @@ -5455,15 +5455,15 @@ "integrity": "sha512-miwjxLwTnO3IjoqkTVeTI+9CdyDggwekmSLhVCw+a/7FoQc+gF3J2dSKwsHvAcVFM0gvU8mzCeTofgw0zPDq0w==" }, "node_modules/@react-pdf/render": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/@react-pdf/render/-/render-3.4.3.tgz", - "integrity": "sha512-9LL059vfwrK1gA0uIA4utpQ/pUH9EW/yia4bb7pCoARs8IlupY5UP265jgax15ua0p+MdUwShZzQ9rilu7kGsw==", + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/@react-pdf/render/-/render-3.4.4.tgz", + "integrity": "sha512-CfGxWmVgrY3JgmB1iMnz2W6Ck+8pisZeFt8vGlxP+JfT+0onr208pQvGSV5KwA9LGhAdABxqc/+y17V3vtKdFA==", "dependencies": { "@babel/runtime": "^7.20.13", "@react-pdf/fns": "2.2.1", "@react-pdf/primitives": "^3.1.1", "@react-pdf/textkit": "^4.4.1", - "@react-pdf/types": "^2.4.1", + "@react-pdf/types": "^2.5.0", "abs-svg-path": "^0.1.1", "color-string": "^1.9.1", "normalize-svg-path": "^1.1.0", @@ -5472,17 +5472,17 @@ } }, "node_modules/@react-pdf/renderer": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/@react-pdf/renderer/-/renderer-3.4.0.tgz", - "integrity": "sha512-8i/B8xJZ4AwINM/RRBnawBXfNACaehQT/bM19TOTUjHoddYaRKM1g1+qOZx58yfxfr5TFRs4fORxEPU1mGmR1A==", + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/@react-pdf/renderer/-/renderer-3.4.4.tgz", + "integrity": "sha512-j1TWMHHXDeHdoQE3xjhBh0MZ2rn7wHIlP/uglr/EJZXqnPbfg6bfLzRJCM6bs+XJV3d8+zLQjHf6sF/fWcBDfg==", "dependencies": { "@babel/runtime": "^7.20.13", - "@react-pdf/font": "^2.4.4", - "@react-pdf/layout": "^3.11.3", - "@react-pdf/pdfkit": "^3.1.7", + "@react-pdf/font": "^2.5.1", + "@react-pdf/layout": "^3.12.1", + "@react-pdf/pdfkit": "^3.1.10", "@react-pdf/primitives": "^3.1.1", - "@react-pdf/render": "^3.4.3", - "@react-pdf/types": "^2.4.1", + "@react-pdf/render": "^3.4.4", + "@react-pdf/types": "^2.5.0", "events": "^3.3.0", "object-assign": "^4.1.1", "prop-types": "^15.6.2", @@ -5503,13 +5503,13 @@ } }, "node_modules/@react-pdf/stylesheet": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@react-pdf/stylesheet/-/stylesheet-4.2.4.tgz", - "integrity": "sha512-CgRfDzeMtnV0GL7zSn381NubmgwqKhFKcK1YrWX3azl/KWVh52jjFd3HWi6dvcETNT862mjWz5MnExe4WOBJXA==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@react-pdf/stylesheet/-/stylesheet-4.2.5.tgz", + "integrity": "sha512-XnmapeCW+hDuNdVwpuvO04WKv71wAs8aH+saIq29Bo2fp1SxznHTcQArTZtK6Wgr/E9BHXeB2iAPpUZuI6G+xA==", "dependencies": { "@babel/runtime": "^7.20.13", "@react-pdf/fns": "2.2.1", - "@react-pdf/types": "^2.4.1", + "@react-pdf/types": "^2.5.0", "color-string": "^1.9.1", "hsl-to-hex": "^1.0.0", "media-engine": "^1.0.3", @@ -5529,9 +5529,9 @@ } }, "node_modules/@react-pdf/types": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@react-pdf/types/-/types-2.4.1.tgz", - "integrity": "sha512-w8pk7svhjVj5f7d7kjEGXSk26ffCqRSQcgWR4DwcFltNpSM18ZJmzmM6WrNeeP437y48LlykLnmGDA3oATakgw==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@react-pdf/types/-/types-2.5.0.tgz", + "integrity": "sha512-XsVRkt0hQ60I4e3leAVt+aZR3KJCaJd179BfJHAv4F4x6Vq3yqkry8lcbUWKGKDw1j3/8sW4FsgGR41SFvsG9A==" }, "node_modules/@reduxjs/toolkit": { "version": "2.2.1", @@ -7237,9 +7237,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.18", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.18.tgz", - "integrity": "sha512-1DKbDfsr6KUElM6wg+0zRNkB/Q7WcKYAaK+pzXn+Xqmszm/5Xa9coeNdtP88Vi+dPzZnMjhge8GIV49ZQkDa+g==", + "version": "10.4.19", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", + "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", "funding": [ { "type": "opencollective", @@ -7256,7 +7256,7 @@ ], "dependencies": { "browserslist": "^4.23.0", - "caniuse-lite": "^1.0.30001591", + "caniuse-lite": "^1.0.30001599", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", @@ -8108,9 +8108,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001593", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001593.tgz", - "integrity": "sha512-UWM1zlo3cZfkpBysd7AS+z+v007q9G1+fLTUU42rQnY6t2axoogPW/xol6T7juU5EUoOhML4WgBIdG+9yYqAjQ==", + "version": "1.0.30001616", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001616.tgz", + "integrity": "sha512-RHVYKov7IcdNjVHJFNY/78RdG4oGVjbayxv8u5IO74Wv7Hlq4PnJE6mo/OjFijjVFNy5ijnCt6H3IIo4t+wfEw==", "funding": [ { "type": "opencollective", @@ -11097,11 +11097,6 @@ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" }, - "node_modules/fast-memoize": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/fast-memoize/-/fast-memoize-2.5.2.tgz", - "integrity": "sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==" - }, "node_modules/fast-url-parser": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz", @@ -13385,9 +13380,9 @@ } }, "node_modules/jay-peg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/jay-peg/-/jay-peg-1.0.1.tgz", - "integrity": "sha512-zBfjkGbuuNXk8JW+rEePpPEbRRjupS8q+5yPak7kjy3e2GvvNwsLle9okEFvfGyZA6HvtSSiYrVd1/jgnYebaQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/jay-peg/-/jay-peg-1.0.2.tgz", + "integrity": "sha512-fyV3NVvv6pTys/3BTapBUGAWAuU9rM2gRcgijZHzptd5KKL+s+S7hESFN+wOsbDH1MzFwdlRAXi0aGxS6uiMKg==", "dependencies": { "restructure": "^3.0.0" } @@ -15942,11 +15937,6 @@ "dev": true, "optional": true }, - "node_modules/lodash.pick": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", - "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==" - }, "node_modules/lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -17650,9 +17640,9 @@ } }, "node_modules/postcss": { - "version": "8.4.37", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.37.tgz", - "integrity": "sha512-7iB/v/r7Woof0glKLH8b1SPHrsX7uhdO+Geb41QpF/+mWZHU3uxxSlN+UXGVit1PawOYDToO+AbZzhBzWRDwbQ==", + "version": "8.4.38", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", + "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", "funding": [ { "type": "opencollective", @@ -19373,12 +19363,9 @@ } }, "node_modules/re-resizable": { - "version": "6.9.6", - "resolved": "https://registry.npmjs.org/re-resizable/-/re-resizable-6.9.6.tgz", - "integrity": "sha512-0xYKS5+Z0zk+vICQlcZW+g54CcJTTmHluA7JUUgvERDxnKAnytylcyPsA+BSFi759s5hPlHmBRegFrwXs2FuBQ==", - "dependencies": { - "fast-memoize": "^2.5.1" - }, + "version": "6.9.14", + "resolved": "https://registry.npmjs.org/re-resizable/-/re-resizable-6.9.14.tgz", + "integrity": "sha512-2UbPrpezMr6gkHKNCRA/N6QGGU237SKOZ78yMHId204A/oXWSAREAIuGZNQ9qlrJosewzcsv2CphZH3u7hC6ng==", "peerDependencies": { "react": "^16.13.1 || ^17.0.0 || ^18.0.0", "react-dom": "^16.13.1 || ^17.0.0 || ^18.0.0" @@ -19971,45 +19958,19 @@ } }, "node_modules/react-rnd": { - "version": "10.4.1", - "resolved": "https://registry.npmjs.org/react-rnd/-/react-rnd-10.4.1.tgz", - "integrity": "sha512-0m887AjQZr6p2ADLNnipquqsDq4XJu/uqVqI3zuoGD19tRm6uB83HmZWydtkilNp5EWsOHbLGF4IjWMdd5du8Q==", + "version": "10.4.10", + "resolved": "https://registry.npmjs.org/react-rnd/-/react-rnd-10.4.10.tgz", + "integrity": "sha512-YjQAgEeSbNUoOXSD9ZBvIiLVizFb+bNhpDk8DbIRHA557NW02CXbwsAeOTpJQnsdhEL+NP2I+Ssrwejqcodtjg==", "dependencies": { - "re-resizable": "6.9.6", - "react-draggable": "4.4.5", - "tslib": "2.3.1" + "re-resizable": "6.9.14", + "react-draggable": "4.4.6", + "tslib": "2.6.2" }, "peerDependencies": { "react": ">=16.3.0", "react-dom": ">=16.3.0" } }, - "node_modules/react-rnd/node_modules/clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/react-rnd/node_modules/react-draggable": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.5.tgz", - "integrity": "sha512-OMHzJdyJbYTZo4uQE393fHcqqPYsEtkjfMgvCHr6rejT+Ezn4OZbNyGH50vv+SunC1RMvwOTSWkEODQLzw1M9g==", - "dependencies": { - "clsx": "^1.1.1", - "prop-types": "^15.8.1" - }, - "peerDependencies": { - "react": ">= 16.3.0", - "react-dom": ">= 16.3.0" - } - }, - "node_modules/react-rnd/node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" - }, "node_modules/react-router": { "version": "6.22.3", "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz", @@ -20236,15 +20197,14 @@ } }, "node_modules/reactour": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/reactour/-/reactour-1.19.2.tgz", - "integrity": "sha512-gbbUiLn8LbpbDW6k+QlVySkblvOICBd3AWMGRKPQghhmuMlOHNqgjW32JrT406lZHUvVlA8+umYD/2MmCHkNOQ==", + "version": "1.19.3", + "resolved": "https://registry.npmjs.org/reactour/-/reactour-1.19.3.tgz", + "integrity": "sha512-a2/j+xmbGlPMQhMy/zdpSmJN1STc0PcLUxhJbAC+bOcYdWHStFR02j6dhNuF/+IeqpX/d44PvlrRXSTt4LGk6w==", "dependencies": { "@rooks/use-mutation-observer": "4.11.2", "classnames": "2.3.1", "focus-outline-manager": "^1.0.2", "lodash.debounce": "4.0.8", - "lodash.pick": "4.4.0", "prop-types": "15.7.2", "react-focus-lock": "2.5.2", "scroll-smooth": "1.1.1", @@ -20702,9 +20662,9 @@ } }, "node_modules/restructure": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.0.tgz", - "integrity": "sha512-Xj8/MEIhhfj9X2rmD9iJ4Gga9EFqVlpMj3vfLnV2r/Mh5jRMryNV+6lWh9GdJtDBcBSPIqzRdfBQ3wDtNFv/uw==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.1.tgz", + "integrity": "sha512-6neDpI/yE9eogQo22qmWwKIA9wFPRyYjQleDEh6zaNAf2ZPqLJYUvNBJBWEWNoBlCeQMQkvIOe2YI/K2GOag+g==" }, "node_modules/retry": { "version": "0.13.1", diff --git a/apps/OpenSign/package.json b/apps/OpenSign/package.json index d7a59becc..cf4c66752 100644 --- a/apps/OpenSign/package.json +++ b/apps/OpenSign/package.json @@ -3,9 +3,9 @@ "version": "0.1.0", "private": true, "dependencies": { - "@formkit/auto-animate": "^0.8.1", + "@formkit/auto-animate": "^0.8.2", "@radix-ui/themes": "^2.0.3", - "@react-pdf/renderer": "^3.4.0", + "@react-pdf/renderer": "^3.4.4", "@reduxjs/toolkit": "^2.2.1", "axios": "^1.6.8", "file-saver": "^2.0.5", @@ -31,7 +31,7 @@ "react-pdf": "^7.7.1", "react-quill": "^2.0.0", "react-redux": "^9.1.0", - "react-rnd": "^10.4.1", + "react-rnd": "^10.4.10", "react-router-dom": "^6.22.3", "react-scripts": "5.0.1", "react-scrollbars-custom": "^4.1.1", @@ -39,7 +39,7 @@ "react-signature-canvas": "^1.0.6", "react-tooltip": "^5.26.3", "react-web-share": "^2.0.2", - "reactour": "^1.19.2", + "reactour": "^1.19.3", "redux": "^5.0.1", "redux-thunk": "^3.1.0", "regex-parser": "^2.3.0", @@ -83,14 +83,14 @@ } }, "devDependencies": { - "@babel/runtime-corejs2": "^7.24.1", - "autoprefixer": "^10.4.18", + "@babel/runtime-corejs2": "^7.24.5", + "autoprefixer": "^10.4.19", "commitizen": "^4.3.0", "eslint": "^8.57.0", "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-react": "^7.34.1", "lint-staged": "^15.2.2", - "postcss": "^8.4.37", + "postcss": "^8.4.38", "prettier": "^2.8.8", "pretty-quick": "^3.3.1", "tailwindcss": "^3.4.1" From 3a28b1cdd7ec132ab675af6d0cb8a74d5d9bb295 Mon Sep 17 00:00:00 2001 From: RaktimaNXG <raktima.c@nxglabs.com> Date: Mon, 6 May 2024 18:51:47 +0530 Subject: [PATCH 25/38] fix: encode in base64 of guest signing url and decode it in guest login page --- apps/OpenSign/src/App.js | 4 ++ apps/OpenSign/src/pages/GuestLogin.js | 39 +++++++++++++------ apps/OpenSign/src/pages/PdfRequestFiles.js | 16 +++++--- apps/OpenSign/src/pages/PlaceHolderSign.js | 25 +++++------- .../src/primitives/GetReportDisplay.js | 16 +++++--- 5 files changed, 63 insertions(+), 37 deletions(-) diff --git a/apps/OpenSign/src/App.js b/apps/OpenSign/src/App.js index 929008576..40bebb3de 100644 --- a/apps/OpenSign/src/App.js +++ b/apps/OpenSign/src/App.js @@ -109,6 +109,10 @@ function App() { path="/login/:id/:userMail/:contactBookId/:serverUrl" element={<LazyPage Page={GuestLogin} />} /> + <Route + path="/login/:base64url" + element={<LazyPage Page={GuestLogin} />} + /> <Route path="/debugpdf" element={<LazyPage Page={DebugPdf} />} /> <Route path="/forgetpassword" diff --git a/apps/OpenSign/src/pages/GuestLogin.js b/apps/OpenSign/src/pages/GuestLogin.js index 7cbe4851e..78f4ce2fa 100644 --- a/apps/OpenSign/src/pages/GuestLogin.js +++ b/apps/OpenSign/src/pages/GuestLogin.js @@ -6,17 +6,19 @@ import axios from "axios"; import { isEnableSubscription, themeColor } from "../constant/const"; import { contractUsers, getAppLogo } from "../constant/Utils"; import logo from "../assets/images/logo.png"; +import { appInfo } from "../constant/appinfo"; function GuestLogin() { - const { id, userMail, contactBookId, serverUrl } = useParams(); + const { id, userMail, contactBookId, serverUrl, base64url } = useParams(); let navigate = useNavigate(); - const [email, setEmail] = useState(userMail); + const [email, setEmail] = useState(""); const [OTP, setOTP] = useState(""); const [EnterOTP, setEnterOtp] = useState(false); const [loading, setLoading] = useState(false); const [isLoading, setIsLoading] = useState(true); const [appLogo, setAppLogo] = useState(""); - + const [documentId, setDocumentId] = useState(id); + const [contactId, setContactId] = useState(contactBookId); useEffect(() => { handleServerUrl(); @@ -36,14 +38,29 @@ function GuestLogin() { setAppLogo(logo); } - //split url in array from '&' localStorage.clear(); - const checkSplit = serverUrl.split("&"); - const server = checkSplit[0]; - const parseId = checkSplit[1]; - const appName = checkSplit[2]; + let parseId, appName, newServer; + //first condition is used to handle old route data url that already save + if (id) { + //split url in array from '&' + const checkSplit = serverUrl.split("&"); + const server = checkSplit[0]; + parseId = checkSplit[1]; + appName = checkSplit[2]; + newServer = server.replaceAll("%2F", "/"); + setEmail(userMail); + } else { + parseId = appInfo.appId; + newServer = `${appInfo.baseUrl}/`; + appName = appInfo.appname; + const decodebase64 = atob(base64url); + //split url in array from '/' + const checkSplit = decodebase64.split("/"); + setEmail(checkSplit[1]); + setDocumentId(checkSplit[0]); + setContactId(checkSplit[2]); + } - const newServer = server.replaceAll("%2F", "/"); localStorage.setItem("baseUrl", newServer); localStorage.setItem("parseAppId", parseId); localStorage.setItem("_appName", appName); @@ -74,7 +91,7 @@ function GuestLogin() { }; let body = { email: email.toString(), - docId: id + docId: documentId }; let Otp = await axios.post(url, body, { headers: headers }); @@ -137,7 +154,7 @@ function GuestLogin() { //save isGuestSigner true in local to handle login flow header in mobile view localStorage.setItem("isGuestSigner", true); setLoading(false); - navigate(`/load/recipientSignPdf/${id}/${contactBookId}`); + navigate(`/load/recipientSignPdf/${documentId}/${contactId}`); } } catch (error) { console.log("err ", error); diff --git a/apps/OpenSign/src/pages/PdfRequestFiles.js b/apps/OpenSign/src/pages/PdfRequestFiles.js index 4b4cb0781..421e32c17 100644 --- a/apps/OpenSign/src/pages/PdfRequestFiles.js +++ b/apps/OpenSign/src/pages/PdfRequestFiles.js @@ -741,14 +741,18 @@ function PdfRequestFiles() { localStorage.getItem("parseAppId"), sessionToken: localStorage.getItem("accesstoken") }; - const serverUrl = localStorage.getItem("baseUrl"); - const newServer = serverUrl.replaceAll("/", "%2F"); + // const serverUrl = localStorage.getItem("baseUrl"); + // const newServer = serverUrl.replaceAll("/", "%2F"); const objectId = user.objectId; - const serverParams = `${newServer}&${localStorage.getItem( - "parseAppId" - )}&${localStorage.getItem("_appName")}`; + // const serverParams = `${newServer}&${localStorage.getItem( + // "parseAppId" + // )}&${localStorage.getItem("_appName")}`; const hostUrl = window.location.origin; - let signPdf = `${hostUrl}/login/${pdfDetails?.[0].objectId}/${user.Email}/${objectId}/${serverParams}`; + const encodeBase64 = btoa( + `${pdfDetails?.[0].objectId}/${user.Email}/${objectId}` + ); + let signPdf = `${hostUrl}/login/${encodeBase64}`; + // let signPdf = `${hostUrl}/login/${pdfDetails?.[0].objectId}/${user.Email}/${objectId}/${serverParams}`; const openSignUrl = "https://www.opensignlabs.com/contact-us"; const orgName = pdfDetails[0]?.ExtUserPtr.Company diff --git a/apps/OpenSign/src/pages/PlaceHolderSign.js b/apps/OpenSign/src/pages/PlaceHolderSign.js index 1eaddc43c..516ffa68c 100644 --- a/apps/OpenSign/src/pages/PlaceHolderSign.js +++ b/apps/OpenSign/src/pages/PlaceHolderSign.js @@ -251,7 +251,6 @@ function PlaceHolderSign() { navigate(`/subscription`); } } - //function for get document details const getDocumentDetails = async () => { fetchTenantDetails(); @@ -968,15 +967,13 @@ function PlaceHolderSign() { const shareLinkList = []; let signerMail = signersdata; for (let i = 0; i < signerMail.length; i++) { - const serverUrl = localStorage.getItem("baseUrl"); - const newServer = serverUrl.replaceAll("/", "%2F"); const objectId = signerMail[i].objectId; - const serverParams = `${newServer}&${localStorage.getItem( - "parseAppId" - )}&${localStorage.getItem("_appName")}`; - const hostUrl = window.location.origin; - let signPdf = `${hostUrl}/login/${pdfDetails?.[0].objectId}/${signerMail[i].Email}/${objectId}/${serverParams}`; + //convert this url documentId/signersEmail/signerObjId base64 and send url + const encodeBase64 = btoa( + `${pdfDetails?.[0].objectId}/${signerMail[i].Email}/${objectId}` + ); + let signPdf = `${hostUrl}/login/${encodeBase64}`; shareLinkList.push({ signerEmail: signerMail[i].Email, url: signPdf }); } return shareLinkList.map((data, ind) => { @@ -1050,15 +1047,13 @@ function PlaceHolderSign() { "X-Parse-Application-Id": localStorage.getItem("parseAppId"), sessionToken: localStorage.getItem("accesstoken") }; - const serverUrl = localStorage.getItem("baseUrl"); - const newServer = serverUrl.replaceAll("/", "%2F"); const objectId = signerMail[i].objectId; - const serverParams = `${newServer}&${localStorage.getItem( - "parseAppId" - )}&${localStorage.getItem("_appName")}`; - const hostUrl = window.location.origin; - let signPdf = `${hostUrl}/login/${pdfDetails?.[0].objectId}/${signerMail[i].Email}/${objectId}/${serverParams}`; + //convert this url documentId/signersEmail/signerObjId base64 and send url + const encodeBase64 = btoa( + `${pdfDetails?.[0].objectId}/${signerMail[i].Email}/${objectId}` + ); + let signPdf = `${hostUrl}/login/${encodeBase64}`; const openSignUrl = "https://www.opensignlabs.com/"; const orgName = pdfDetails[0]?.ExtUserPtr.Company ? pdfDetails[0].ExtUserPtr.Company diff --git a/apps/OpenSign/src/primitives/GetReportDisplay.js b/apps/OpenSign/src/primitives/GetReportDisplay.js index 8064e7382..da8ed1d95 100644 --- a/apps/OpenSign/src/primitives/GetReportDisplay.js +++ b/apps/OpenSign/src/primitives/GetReportDisplay.js @@ -286,13 +286,19 @@ const ReportTable = (props) => { const handleShare = (item) => { setActLoader({ [item.objectId]: true }); const host = window.location.origin; - const serverUrl = process.env.REACT_APP_SERVERURL - ? process.env.REACT_APP_SERVERURL - : window.location.origin + "/api/app"; - const baseURL = serverUrl.replace(/\//g, "%2F"); + // const serverUrl = process.env.REACT_APP_SERVERURL + // ? process.env.REACT_APP_SERVERURL + // : window.location.origin + "/api/app"; + // const baseURL = serverUrl.replace(/\//g, "%2F"); + + const getUrl = (x) => { + const encodeBase64 = btoa(`${item.objectId}/${x.Email}/${x.objectId}`); + return `${host}/login/${encodeBase64}`; + }; + const urls = item.Signers.map((x) => ({ email: x.Email, - url: `${host}/login/${item.objectId}/${x.Email}/${x.objectId}/${baseURL}%2F&opensign&contracts` + url: getUrl(x) })); setShareUrls(urls); setIsShare({ [item.objectId]: true }); From 6afa97a76bed44d1a0b7c3437e6123810cdcf7fc Mon Sep 17 00:00:00 2001 From: RaktimaNXG <raktima.c@nxglabs.com> Date: Mon, 6 May 2024 18:54:07 +0530 Subject: [PATCH 26/38] fix: encode in base64 of guest signing url and decode it in guest login page --- .../customRoute/v1/routes/CreateDocumentWithTemplate.js | 8 +++++--- .../customRoute/v1/routes/createDocumentwithCoordinate.js | 7 +++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js b/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js index b3b6f2179..bc5343487 100644 --- a/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js +++ b/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js @@ -217,9 +217,10 @@ export default async function createDocumentWithTemplate(request, response) { }; const objectId = contactMail[i].contactPtr.objectId; - const hostUrl = baseUrl.origin; - let signPdf = `${hostUrl}/login/${res.id}/${contactMail[i].email}/${objectId}/${serverParams}`; + // let signPdf = `${hostUrl}/login/${res.id}/${contactMail[i].email}/${objectId}/${serverParams}`; + const encodeBase64 = btoa(`${res.id}/${contactMail[i].email}/${objectId}`); + let signPdf = `${hostUrl}/login/${encodeBase64}`; const openSignUrl = 'https://www.opensignlabs.com/contact-us'; const orgName = template.ExtUserPtr.Company ? template.ExtUserPtr.Company : ''; const themeBGcolor = '#47a3ad'; @@ -316,11 +317,12 @@ export default async function createDocumentWithTemplate(request, response) { properties: { response_code: 200 }, }); } + const encodeBase64 = btoa(`${res.id}/${x.email}/${x.contactPtr.objectId}`); return response.json({ objectId: res.id, signurl: contact.map(x => ({ email: x.email, - url: `${baseUrl.origin}/login/${res.id}/${x.email}/${x.contactPtr.objectId}/${serverParams}`, + url: `${baseUrl.origin}/login/${encodeBase64}`, })), message: 'Document sent successfully!', }); diff --git a/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js b/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js index f6656f6fb..435bda342 100644 --- a/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js +++ b/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js @@ -278,7 +278,9 @@ export default async function createDocumentwithCoordinate(request, response) { const objectId = contactMail[i].contactPtr.objectId; const hostUrl = baseUrl.origin; - let signPdf = `${hostUrl}/login/${res.id}/${contactMail[i].email}/${objectId}/${serverParams}`; + const encodeBase64 = btoa(`${response.id}/${contactMail[i].email}/${objectId}`); + let signPdf = `${hostUrl}/login/${encodeBase64}`; + // let signPdf = `${hostUrl}/login/${res.id}/${contactMail[i].email}/${objectId}/${serverParams}`; const openSignUrl = 'https://www.opensignlabs.com/contact-us'; const orgName = parseExtUser.Company ? parseExtUser.Company : ''; const themeBGcolor = '#47a3ad'; @@ -359,11 +361,12 @@ export default async function createDocumentwithCoordinate(request, response) { properties: { response_code: 200 }, }); } + const encodeBase64 = btoa(`${res.id}/${x.email}/${x.contactPtr.objectId}`); return response.json({ objectId: res.id, signurl: contact.map(x => ({ email: x.email, - url: `${baseUrl.origin}/login/${res.id}/${x.email}/${x.contactPtr.objectId}/${serverParams}`, + url: `${baseUrl.origin}/login/${encodeBase64}`, })), message: 'Document sent successfully!', }); From 66bf577676be48a82790756f8a26841ff19916f4 Mon Sep 17 00:00:00 2001 From: RaktimaNXG <raktima.c@nxglabs.com> Date: Tue, 7 May 2024 09:54:10 +0530 Subject: [PATCH 27/38] refactor: add comment --- apps/OpenSign/src/pages/GuestLogin.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/OpenSign/src/pages/GuestLogin.js b/apps/OpenSign/src/pages/GuestLogin.js index 78f4ce2fa..409b29fb0 100644 --- a/apps/OpenSign/src/pages/GuestLogin.js +++ b/apps/OpenSign/src/pages/GuestLogin.js @@ -40,7 +40,7 @@ function GuestLogin() { localStorage.clear(); let parseId, appName, newServer; - //first condition is used to handle old route data url that already save + //first condition is used to manage previously stored URL data for the old route. if (id) { //split url in array from '&' const checkSplit = serverUrl.split("&"); @@ -50,9 +50,11 @@ function GuestLogin() { newServer = server.replaceAll("%2F", "/"); setEmail(userMail); } else { + //in this condition decode base64 route to in string and save required dataf. parseId = appInfo.appId; newServer = `${appInfo.baseUrl}/`; appName = appInfo.appname; + //`atob` function is used to decode base64 const decodebase64 = atob(base64url); //split url in array from '/' const checkSplit = decodebase64.split("/"); From 4a7108e4f65e92871a398bb34abba5c9ac894a07 Mon Sep 17 00:00:00 2001 From: Daniel Mutwiri <daniel@otbafrica.com> Date: Tue, 7 May 2024 08:48:55 +0300 Subject: [PATCH 28/38] Bug Fix: saveFileUsage is not defined. saveFileUsage imported from Utils.js --- .../OpenSignServer/cloud/customRoute/v1/routes/draftDocument.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/OpenSignServer/cloud/customRoute/v1/routes/draftDocument.js b/apps/OpenSignServer/cloud/customRoute/v1/routes/draftDocument.js index 7f31b66bc..7651c580e 100644 --- a/apps/OpenSignServer/cloud/customRoute/v1/routes/draftDocument.js +++ b/apps/OpenSignServer/cloud/customRoute/v1/routes/draftDocument.js @@ -1,5 +1,5 @@ import axios from 'axios'; -import { customAPIurl } from '../../../../Utils.js'; +import { customAPIurl, saveFileUsage } from '../../../../Utils.js'; // const randomId = () => Math.floor(1000 + Math.random() * 9000); export default async function draftDocument(request, response) { From 37b73e36ca2b9a809cab942a9857ffb69c6a4fe6 Mon Sep 17 00:00:00 2001 From: RaktimaNXG <raktima.c@nxglabs.com> Date: Tue, 7 May 2024 11:42:48 +0530 Subject: [PATCH 29/38] fix: document error No PDF file specified --- apps/OpenSign/src/pages/PdfRequestFiles.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/OpenSign/src/pages/PdfRequestFiles.js b/apps/OpenSign/src/pages/PdfRequestFiles.js index 4b4cb0781..0eb54aeb0 100644 --- a/apps/OpenSign/src/pages/PdfRequestFiles.js +++ b/apps/OpenSign/src/pages/PdfRequestFiles.js @@ -1331,7 +1331,10 @@ function PdfRequestFiles() { </ModalUi> {/* this component used to render all pdf pages in left side */} <RenderAllPdfPage - signPdfUrl={pdfDetails[0] && pdfDetails[0].SignedUrl} + signPdfUrl={ + pdfDetails[0] && + (pdfDetails[0].SignedUrl || pdfDetails[0].URL) + } allPages={allPages} setAllPages={setAllPages} setPageNumber={setPageNumber} From a69682cebf2f7eb0c66825ee507420ce9ca9bfff Mon Sep 17 00:00:00 2001 From: Amol <amol@nxglabs.in> Date: Tue, 7 May 2024 12:20:43 +0530 Subject: [PATCH 30/38] Revert "fix: Document loading error `No PDF file specified`" --- apps/OpenSign/src/pages/PdfRequestFiles.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/OpenSign/src/pages/PdfRequestFiles.js b/apps/OpenSign/src/pages/PdfRequestFiles.js index 0eb54aeb0..4b4cb0781 100644 --- a/apps/OpenSign/src/pages/PdfRequestFiles.js +++ b/apps/OpenSign/src/pages/PdfRequestFiles.js @@ -1331,10 +1331,7 @@ function PdfRequestFiles() { </ModalUi> {/* this component used to render all pdf pages in left side */} <RenderAllPdfPage - signPdfUrl={ - pdfDetails[0] && - (pdfDetails[0].SignedUrl || pdfDetails[0].URL) - } + signPdfUrl={pdfDetails[0] && pdfDetails[0].SignedUrl} allPages={allPages} setAllPages={setAllPages} setPageNumber={setPageNumber} From 07382822bd91a0adf9595a29bb976ff0b507ca24 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Tue, 7 May 2024 13:39:39 +0530 Subject: [PATCH 31/38] fix: issue of showing currently incrypted file not supported when try to sign document --- apps/OpenSign/src/components/pdf/SignPad.js | 15 ++++---- apps/OpenSign/src/constant/Utils.js | 17 +++++---- apps/OpenSign/src/pages/PdfRequestFiles.js | 23 ++++++++----- apps/OpenSign/src/pages/SignyourselfPdf.js | 38 +++++++++++++-------- 4 files changed, 57 insertions(+), 36 deletions(-) diff --git a/apps/OpenSign/src/components/pdf/SignPad.js b/apps/OpenSign/src/components/pdf/SignPad.js index 9103a3d8f..3fd03328a 100644 --- a/apps/OpenSign/src/components/pdf/SignPad.js +++ b/apps/OpenSign/src/components/pdf/SignPad.js @@ -214,8 +214,8 @@ function SignPad({ const fontfamily = fontStyle ? fontStyle : fontSelect - ? fontSelect - : "Fasthand"; + ? fontSelect + : "Fasthand"; //creating span for getting text content width const span = document.createElement("span"); @@ -267,10 +267,10 @@ function SignPad({ key === 0 && penColor === "blue" ? "2px solid blue" : key === 1 && penColor === "red" - ? "2px solid red" - : key === 2 && penColor === "black" - ? "2px solid black" - : "2px solid white" + ? "2px solid red" + : key === 2 && penColor === "black" + ? "2px solid black" + : "2px solid white" }} onClick={() => { props?.convertToImg && @@ -536,7 +536,6 @@ function SignPad({ style={{ border: "1.3px solid #007bff", borderRadius: "2px", - display: "flex", flexDirection: "column", justifyContent: "center", @@ -553,7 +552,7 @@ function SignPad({ type="file" onChange={onImageChange} className="filetype" - accept="image/*" + accept="image/png,image/jpeg" ref={imageRef} hidden /> diff --git a/apps/OpenSign/src/constant/Utils.js b/apps/OpenSign/src/constant/Utils.js index 63289af0b..f342dff8d 100644 --- a/apps/OpenSign/src/constant/Utils.js +++ b/apps/OpenSign/src/constant/Utils.js @@ -561,17 +561,22 @@ export const signPdfFun = async ( isCustomCompletionMail = true; } } - + + // below for loop is used to get first signature of user to send if to signpdf + // for adding it in completion certificate 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"); + getSignature = item.pos.find((data) => data?.type === "signature"); + break; } else { - getSignature = item.pos.filter((data) => !data.isStamp); + getSignature = item.pos.find((data) => !data.isStamp); + break; } } - let base64Sign = getSignature[0].SignUrl; + + let base64Sign = getSignature.SignUrl; //check https type signature (default signature exist) then convert in base64 const isUrl = base64Sign.includes("https"); if (isUrl) { @@ -1344,8 +1349,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/PdfRequestFiles.js b/apps/OpenSign/src/pages/PdfRequestFiles.js index 0eb54aeb0..b7f6384b1 100644 --- a/apps/OpenSign/src/pages/PdfRequestFiles.js +++ b/apps/OpenSign/src/pages/PdfRequestFiles.js @@ -647,7 +647,6 @@ function PdfRequestFiles() { const existingPdfBytes = pdfArrayBuffer; try { const pdfDoc = await PDFDocument.load(existingPdfBytes); - const isSignYourSelfFlow = false; const extUserPtr = pdfDetails[0].ExtUserPtr; const HeaderDocId = extUserPtr?.HeaderDocId; @@ -843,10 +842,18 @@ function PdfRequestFiles() { } } catch (err) { setIsUiLoading(false); - setIsAlert({ - isShow: true, - alertMessage: `Currently encrypted pdf files are not supported.` - }); + if (err && err.message.includes("is encrypted.")) { + setIsAlert({ + isShow: true, + alertMessage: `Currently encrypted pdf files are not supported.` + }); + } else { + console.log("err in request signing", err); + setIsAlert({ + isShow: true, + alertMessage: `Something went wrong.` + }); + } } } setIsSignPad(false); @@ -1238,9 +1245,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 6ad106d93..7fe144910 100644 --- a/apps/OpenSign/src/pages/SignyourselfPdf.js +++ b/apps/OpenSign/src/pages/SignyourselfPdf.js @@ -480,13 +480,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) }; @@ -649,9 +649,7 @@ function SignYourSelf() { const existingPdfBytes = pdfArrayBuffer; // Load a PDFDocument from the existing PDF bytes try { - const pdfDoc = await PDFDocument.load(existingPdfBytes, { - ignoreEncryption: true - }); + const pdfDoc = await PDFDocument.load(existingPdfBytes); const isSignYourSelfFlow = true; const extUserPtr = pdfDetails[0].ExtUserPtr; const HeaderDocId = extUserPtr?.HeaderDocId; @@ -672,10 +670,18 @@ function SignYourSelf() { await signPdfFun(pdfBytes, documentId); } catch (err) { setIsUiLoading(false); - setIsAlert({ - isShow: true, - alertMessage: `Currently encrypted pdf files are not supported.` - }); + if (err && err.message.includes("is encrypted.")) { + setIsAlert({ + isShow: true, + alertMessage: `Currently encrypted pdf files are not supported.` + }); + } else { + console.log("err in signing", err); + setIsAlert({ + isShow: true, + alertMessage: `Something went wrong.` + }); + } } } } catch (err) { @@ -703,16 +709,20 @@ function SignYourSelf() { isCustomCompletionMail = true; } } + // below for loop is used to get first signature of user to send if to signpdf + // for adding it in completion certificate 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"); + getSignature = item.pos.find((data) => data?.type === "signature"); + break; } else { - getSignature = item.pos.filter((data) => !data.isStamp); + getSignature = item.pos.find((data) => !data.isStamp); + break; } } - let base64Sign = getSignature[0].SignUrl; + let base64Sign = getSignature.SignUrl; //check https type signature (default signature exist) then convert in base64 const isUrl = base64Sign.includes("https"); if (isUrl) { From 51c3a0f71ad63f5d1e05fa4cfa6328965b2c1158 Mon Sep 17 00:00:00 2001 From: RaktimaNXG <raktima.c@nxglabs.com> Date: Tue, 7 May 2024 15:55:46 +0530 Subject: [PATCH 32/38] fix: in opensign-drive unnecessary render tour message and onclick same folder route then it show no data found issue --- apps/OpenSign/src/pages/Opensigndrive.js | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/apps/OpenSign/src/pages/Opensigndrive.js b/apps/OpenSign/src/pages/Opensigndrive.js index 0803cb8bb..12d1c0f61 100644 --- a/apps/OpenSign/src/pages/Opensigndrive.js +++ b/apps/OpenSign/src/pages/Opensigndrive.js @@ -64,6 +64,7 @@ function Opensigndrive() { const sortingValue = ["Name", "Date"]; const [isDontShow, setIsDontShow] = useState(false); const [tourData, setTourData] = useState(); + const [showTourFirstTIme, setShowTourFirstTime] = useState(true); const orderName = { Ascending: "Ascending", Descending: "Descending", @@ -134,7 +135,9 @@ function Opensigndrive() { //function for get all pdf document list const getPdfDocumentList = async (disbaleLoading) => { setLoading(true); - checkTourStatus(); + if (showTourFirstTIme) { + checkTourStatus(); + } if (!disbaleLoading) { setIsLoading({ isLoad: true, message: "This might take some time" }); } @@ -250,17 +253,23 @@ function Opensigndrive() { }, [loading, sortingOrder, selectedSort]); // Add/remove scroll event listener when loading changes //function for handle folder name path - const handleRoute = (index) => { - setPdfData([]); + const handleRoute = (index, folderData) => { setSkip(0); + // after onclick on route filter route from that index const updateFolderName = folderName.filter((x, i) => { if (i <= index) { return x; } }); setFolderName(updateFolderName); + //get route details after onclick path of folder name + const getCurrentId = folderData[index]; const getLastId = updateFolderName[updateFolderName.length - 1]; - setDocId(getLastId.objectId); + //below condition is used to check if user click on same route which already open then don't change any thing + if (docId !== getCurrentId.objectId) { + setPdfData([]); + setDocId(getLastId.objectId); + } }; //function for add new folder name @@ -439,7 +448,7 @@ function Opensigndrive() { return ( <React.Fragment key={id}> <span - onClick={() => handleRoute(id)} + onClick={() => handleRoute(id, folderData)} style={{ color: "#a64b4e", fontWeight: "400", @@ -474,6 +483,7 @@ function Opensigndrive() { const closeTour = async () => { setIsTour(false); + setShowTourFirstTime(false); if (isDontShow) { const serverUrl = localStorage.getItem("baseUrl"); const appId = localStorage.getItem("parseAppId"); From d9126d6701cb74cf36c24c31b805bf5f5f8f78ae Mon Sep 17 00:00:00 2001 From: RaktimaNXG <raktima.c@nxglabs.com> Date: Tue, 7 May 2024 16:04:47 +0530 Subject: [PATCH 33/38] refactor: add comment --- apps/OpenSign/src/pages/PdfRequestFiles.js | 7 ++++--- apps/OpenSign/src/pages/PlaceHolderSign.js | 4 ++-- apps/OpenSign/src/primitives/GetReportDisplay.js | 1 + .../customRoute/v1/routes/CreateDocumentWithTemplate.js | 2 ++ .../customRoute/v1/routes/createDocumentwithCoordinate.js | 2 ++ 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/apps/OpenSign/src/pages/PdfRequestFiles.js b/apps/OpenSign/src/pages/PdfRequestFiles.js index 2a71a2f4c..a1d768fad 100644 --- a/apps/OpenSign/src/pages/PdfRequestFiles.js +++ b/apps/OpenSign/src/pages/PdfRequestFiles.js @@ -747,6 +747,7 @@ function PdfRequestFiles() { // "parseAppId" // )}&${localStorage.getItem("_appName")}`; const hostUrl = window.location.origin; + //encode this url value `${pdfDetails?.[0].objectId}/${user.Email}/${objectId}` to base64 using `btoa` function const encodeBase64 = btoa( `${pdfDetails?.[0].objectId}/${user.Email}/${objectId}` ); @@ -1249,9 +1250,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/PlaceHolderSign.js b/apps/OpenSign/src/pages/PlaceHolderSign.js index 516ffa68c..978eec980 100644 --- a/apps/OpenSign/src/pages/PlaceHolderSign.js +++ b/apps/OpenSign/src/pages/PlaceHolderSign.js @@ -969,7 +969,7 @@ function PlaceHolderSign() { for (let i = 0; i < signerMail.length; i++) { const objectId = signerMail[i].objectId; const hostUrl = window.location.origin; - //convert this url documentId/signersEmail/signerObjId base64 and send url + //encode this url value `${pdfDetails?.[0].objectId}/${signerMail[i].Email}/${objectId}` to base64 using `btoa` function const encodeBase64 = btoa( `${pdfDetails?.[0].objectId}/${signerMail[i].Email}/${objectId}` ); @@ -1049,7 +1049,7 @@ function PlaceHolderSign() { }; const objectId = signerMail[i].objectId; const hostUrl = window.location.origin; - //convert this url documentId/signersEmail/signerObjId base64 and send url + //encode this url value `${pdfDetails?.[0].objectId}/${signerMail[i].Email}/${objectId}` to base64 using `btoa` function const encodeBase64 = btoa( `${pdfDetails?.[0].objectId}/${signerMail[i].Email}/${objectId}` ); diff --git a/apps/OpenSign/src/primitives/GetReportDisplay.js b/apps/OpenSign/src/primitives/GetReportDisplay.js index d492b83e4..fe54580cd 100644 --- a/apps/OpenSign/src/primitives/GetReportDisplay.js +++ b/apps/OpenSign/src/primitives/GetReportDisplay.js @@ -293,6 +293,7 @@ const ReportTable = (props) => { // const baseURL = serverUrl.replace(/\//g, "%2F"); const getUrl = (x) => { + //encode this url value `${item.objectId}/${x.Email}/${x.objectId}` to base64 using `btoa` function const encodeBase64 = btoa(`${item.objectId}/${x.Email}/${x.objectId}`); return `${host}/login/${encodeBase64}`; }; diff --git a/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js b/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js index bc5343487..a075f3aae 100644 --- a/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js +++ b/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js @@ -219,6 +219,7 @@ export default async function createDocumentWithTemplate(request, response) { const objectId = contactMail[i].contactPtr.objectId; const hostUrl = baseUrl.origin; // let signPdf = `${hostUrl}/login/${res.id}/${contactMail[i].email}/${objectId}/${serverParams}`; + //encode this url value `${res.id}/${contactMail[i].email}/${objectId}` to base64 using `btoa` function const encodeBase64 = btoa(`${res.id}/${contactMail[i].email}/${objectId}`); let signPdf = `${hostUrl}/login/${encodeBase64}`; const openSignUrl = 'https://www.opensignlabs.com/contact-us'; @@ -317,6 +318,7 @@ export default async function createDocumentWithTemplate(request, response) { properties: { response_code: 200 }, }); } + //encode this url value `${res.id}/${x.email}/${x.contactPtr.objectId}` to base64 using `btoa` function const encodeBase64 = btoa(`${res.id}/${x.email}/${x.contactPtr.objectId}`); return response.json({ objectId: res.id, diff --git a/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js b/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js index 435bda342..b954e046e 100644 --- a/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js +++ b/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js @@ -278,6 +278,7 @@ export default async function createDocumentwithCoordinate(request, response) { const objectId = contactMail[i].contactPtr.objectId; const hostUrl = baseUrl.origin; + //encode this url value `${response.id}/${contactMail[i].email}/${objectId}` to base64 using `btoa` function const encodeBase64 = btoa(`${response.id}/${contactMail[i].email}/${objectId}`); let signPdf = `${hostUrl}/login/${encodeBase64}`; // let signPdf = `${hostUrl}/login/${res.id}/${contactMail[i].email}/${objectId}/${serverParams}`; @@ -361,6 +362,7 @@ export default async function createDocumentwithCoordinate(request, response) { properties: { response_code: 200 }, }); } + //encode this url value `${res.id}/${x.email}/${x.contactPtr.objectId}` to base64 using `btoa` function const encodeBase64 = btoa(`${res.id}/${x.email}/${x.contactPtr.objectId}`); return response.json({ objectId: res.id, From 350212c02f3304d4014ad64bee59f235c0f66c43 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs <prafull.navkar@nxglabs.com> Date: Tue, 7 May 2024 16:06:31 +0530 Subject: [PATCH 34/38] refactor: changes in subject of request signature mail --- apps/OpenSign/src/components/pdf/EmailBody.js | 2 +- apps/OpenSign/src/pages/PdfRequestFiles.js | 2 +- apps/OpenSign/src/pages/PlaceHolderSign.js | 8 ++++---- .../customRoute/v1/routes/CreateDocumentWithTemplate.js | 4 ++-- .../customRoute/v1/routes/createDocumentwithCoordinate.js | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/OpenSign/src/components/pdf/EmailBody.js b/apps/OpenSign/src/components/pdf/EmailBody.js index 629213c3f..ce0f8442b 100644 --- a/apps/OpenSign/src/components/pdf/EmailBody.js +++ b/apps/OpenSign/src/components/pdf/EmailBody.js @@ -20,7 +20,7 @@ export function EmailBody(props) { required value={props.requestSubject} onChange={(e) => props.setRequestSubject(e.target.value)} - placeholder="${senderName} has requested you to sign ${documentName}" + placeholder='${senderName} has requested you to sign "${documentName}"' className="px-3 py-2 w-full border-[1px] border-gray-300 rounded focus:outline-none text-xs" /> <label className="text-sm ml-2 mt-3"> diff --git a/apps/OpenSign/src/pages/PdfRequestFiles.js b/apps/OpenSign/src/pages/PdfRequestFiles.js index b7f6384b1..d16ab1abf 100644 --- a/apps/OpenSign/src/pages/PdfRequestFiles.js +++ b/apps/OpenSign/src/pages/PdfRequestFiles.js @@ -794,7 +794,7 @@ function PdfRequestFiles() { recipient: user.Email, subject: requestSubject ? replaceVar?.subject - : `${pdfDetails?.[0].ExtUserPtr.Name} has requested you to sign ${pdfDetails?.[0].Name}`, + : `${pdfDetails?.[0].ExtUserPtr.Name} has requested you to sign "${pdfDetails?.[0].Name}"`, from: senderEmail, html: requestBody ? replaceVar?.body diff --git a/apps/OpenSign/src/pages/PlaceHolderSign.js b/apps/OpenSign/src/pages/PlaceHolderSign.js index 1eaddc43c..d3f240120 100644 --- a/apps/OpenSign/src/pages/PlaceHolderSign.js +++ b/apps/OpenSign/src/pages/PlaceHolderSign.js @@ -201,16 +201,16 @@ function PlaceHolderSign() { setDefaultBody(defaultRequestBody); setDefaultSubject( - `{{sender_name}} has requested you to sign {{document_title}}` + `{{sender_name}} has requested you to sign "{{document_title}}"` ); } else { setRequestBody(defaultRequestBody); setRequestSubject( - `{{sender_name}} has requested you to sign {{document_title}}` + `{{sender_name}} has requested you to sign "{{document_title}}"` ); setDefaultBody(defaultRequestBody); setDefaultSubject( - `{{sender_name}}has requested you to sign {{document_title}}` + `{{sender_name}}has requested you to sign "{{document_title}}"` ); } } @@ -1104,7 +1104,7 @@ function PlaceHolderSign() { recipient: signerMail[i].Email, subject: isCustomize ? replaceVar?.subject - : `${senderName} has requested you to sign ${documentName}`, + : `${senderName} has requested you to sign "${documentName}"`, from: senderEmail, html: isCustomize ? replaceVar?.body diff --git a/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js b/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js index b3b6f2179..851b837c8 100644 --- a/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js +++ b/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js @@ -266,13 +266,13 @@ export default async function createDocumentWithTemplate(request, response) { replaceVar = { subject: replaceVar.subject, body: email_html }; } else if (email_body) { replaceVar = replaceMailVaribles( - `${template.ExtUserPtr.Name} has requested you to sign ${template.Name}`, + `${template.ExtUserPtr.Name} has requested you to sign "${template.Name}"`, email_body, variables ); } else { replaceVar = { - subject: `${template.ExtUserPtr.Name} has requested you to sign ${template.Name}`, + subject: `${template.ExtUserPtr.Name} has requested you to sign "${template.Name}"`, body: email_html, }; } diff --git a/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js b/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js index f6656f6fb..29197ecb1 100644 --- a/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js +++ b/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js @@ -324,13 +324,13 @@ export default async function createDocumentwithCoordinate(request, response) { replaceVar = { subject: replaceVar.subject, body: email_html }; } else if (email_body) { replaceVar = replaceMailVaribles( - `${parseExtUser.Name} has requested you to sign ${name}`, + `${parseExtUser.Name} has requested you to sign "${name}"`, email_body, variables ); } else { replaceVar = { - subject: `${parseExtUser.Name} has requested you to sign ${parseExtUser.Name}`, + subject: `${parseExtUser.Name} has requested you to sign "${parseExtUser.Name}"`, body: email_html, }; } From 65d5c7f33791dd036d3512ae8ab9161b7b12d3c8 Mon Sep 17 00:00:00 2001 From: RaktimaNXG <raktima.c@nxglabs.com> Date: Tue, 7 May 2024 18:30:06 +0530 Subject: [PATCH 35/38] refactor: remove commented code and remove extra condition --- apps/OpenSign/src/pages/GuestLogin.js | 32 +++++++------------ apps/OpenSign/src/pages/PdfRequestFiles.js | 6 ---- .../src/primitives/GetReportDisplay.js | 5 --- .../v1/routes/CreateDocumentWithTemplate.js | 1 - .../v1/routes/createDocumentwithCoordinate.js | 1 - 5 files changed, 11 insertions(+), 34 deletions(-) diff --git a/apps/OpenSign/src/pages/GuestLogin.js b/apps/OpenSign/src/pages/GuestLogin.js index 409b29fb0..6df724448 100644 --- a/apps/OpenSign/src/pages/GuestLogin.js +++ b/apps/OpenSign/src/pages/GuestLogin.js @@ -9,9 +9,9 @@ import logo from "../assets/images/logo.png"; import { appInfo } from "../constant/appinfo"; function GuestLogin() { - const { id, userMail, contactBookId, serverUrl, base64url } = useParams(); + const { id, userMail, contactBookId, base64url } = useParams(); let navigate = useNavigate(); - const [email, setEmail] = useState(""); + const [email, setEmail] = useState(userMail); const [OTP, setOTP] = useState(""); const [EnterOTP, setEnterOtp] = useState(false); const [loading, setLoading] = useState(false); @@ -39,33 +39,23 @@ function GuestLogin() { } localStorage.clear(); - let parseId, appName, newServer; - //first condition is used to manage previously stored URL data for the old route. - if (id) { - //split url in array from '&' - const checkSplit = serverUrl.split("&"); - const server = checkSplit[0]; - parseId = checkSplit[1]; - appName = checkSplit[2]; - newServer = server.replaceAll("%2F", "/"); - setEmail(userMail); - } else { - //in this condition decode base64 route to in string and save required dataf. - parseId = appInfo.appId; - newServer = `${appInfo.baseUrl}/`; - appName = appInfo.appname; + const parseId = appInfo.appId; + const newServer = `${appInfo.baseUrl}/`; + const appName = appInfo.appname; + localStorage.setItem("baseUrl", newServer); + localStorage.setItem("parseAppId", parseId); + localStorage.setItem("_appName", appName); + //first condition is used decode base64 to string and get userEmail,documentId, contactBoookId data. + if (!id) { //`atob` function is used to decode base64 const decodebase64 = atob(base64url); //split url in array from '/' const checkSplit = decodebase64.split("/"); - setEmail(checkSplit[1]); setDocumentId(checkSplit[0]); + setEmail(checkSplit[1]); setContactId(checkSplit[2]); } - localStorage.setItem("baseUrl", newServer); - localStorage.setItem("parseAppId", parseId); - localStorage.setItem("_appName", appName); setIsLoading(false); }; diff --git a/apps/OpenSign/src/pages/PdfRequestFiles.js b/apps/OpenSign/src/pages/PdfRequestFiles.js index a1d768fad..57a6bef83 100644 --- a/apps/OpenSign/src/pages/PdfRequestFiles.js +++ b/apps/OpenSign/src/pages/PdfRequestFiles.js @@ -740,19 +740,13 @@ function PdfRequestFiles() { localStorage.getItem("parseAppId"), sessionToken: localStorage.getItem("accesstoken") }; - // const serverUrl = localStorage.getItem("baseUrl"); - // const newServer = serverUrl.replaceAll("/", "%2F"); const objectId = user.objectId; - // const serverParams = `${newServer}&${localStorage.getItem( - // "parseAppId" - // )}&${localStorage.getItem("_appName")}`; const hostUrl = window.location.origin; //encode this url value `${pdfDetails?.[0].objectId}/${user.Email}/${objectId}` to base64 using `btoa` function const encodeBase64 = btoa( `${pdfDetails?.[0].objectId}/${user.Email}/${objectId}` ); let signPdf = `${hostUrl}/login/${encodeBase64}`; - // let signPdf = `${hostUrl}/login/${pdfDetails?.[0].objectId}/${user.Email}/${objectId}/${serverParams}`; const openSignUrl = "https://www.opensignlabs.com/contact-us"; const orgName = pdfDetails[0]?.ExtUserPtr.Company diff --git a/apps/OpenSign/src/primitives/GetReportDisplay.js b/apps/OpenSign/src/primitives/GetReportDisplay.js index fe54580cd..927a0e14f 100644 --- a/apps/OpenSign/src/primitives/GetReportDisplay.js +++ b/apps/OpenSign/src/primitives/GetReportDisplay.js @@ -287,11 +287,6 @@ const ReportTable = (props) => { const handleShare = (item) => { setActLoader({ [item.objectId]: true }); const host = window.location.origin; - // const serverUrl = process.env.REACT_APP_SERVERURL - // ? process.env.REACT_APP_SERVERURL - // : window.location.origin + "/api/app"; - // const baseURL = serverUrl.replace(/\//g, "%2F"); - const getUrl = (x) => { //encode this url value `${item.objectId}/${x.Email}/${x.objectId}` to base64 using `btoa` function const encodeBase64 = btoa(`${item.objectId}/${x.Email}/${x.objectId}`); diff --git a/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js b/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js index a075f3aae..f70466a9f 100644 --- a/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js +++ b/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js @@ -218,7 +218,6 @@ export default async function createDocumentWithTemplate(request, response) { const objectId = contactMail[i].contactPtr.objectId; const hostUrl = baseUrl.origin; - // let signPdf = `${hostUrl}/login/${res.id}/${contactMail[i].email}/${objectId}/${serverParams}`; //encode this url value `${res.id}/${contactMail[i].email}/${objectId}` to base64 using `btoa` function const encodeBase64 = btoa(`${res.id}/${contactMail[i].email}/${objectId}`); let signPdf = `${hostUrl}/login/${encodeBase64}`; diff --git a/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js b/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js index b954e046e..52c00ef33 100644 --- a/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js +++ b/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js @@ -281,7 +281,6 @@ export default async function createDocumentwithCoordinate(request, response) { //encode this url value `${response.id}/${contactMail[i].email}/${objectId}` to base64 using `btoa` function const encodeBase64 = btoa(`${response.id}/${contactMail[i].email}/${objectId}`); let signPdf = `${hostUrl}/login/${encodeBase64}`; - // let signPdf = `${hostUrl}/login/${res.id}/${contactMail[i].email}/${objectId}/${serverParams}`; const openSignUrl = 'https://www.opensignlabs.com/contact-us'; const orgName = parseExtUser.Company ? parseExtUser.Company : ''; const themeBGcolor = '#47a3ad'; From 0f6701300543cee6985a4b5b28633c6c1846fa12 Mon Sep 17 00:00:00 2001 From: RaktimaNXG <raktima.c@nxglabs.com> Date: Tue, 7 May 2024 18:30:45 +0530 Subject: [PATCH 36/38] refactor: remove commented code and remove extra condition --- apps/OpenSign/src/pages/GuestLogin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/OpenSign/src/pages/GuestLogin.js b/apps/OpenSign/src/pages/GuestLogin.js index 6df724448..003ef60bb 100644 --- a/apps/OpenSign/src/pages/GuestLogin.js +++ b/apps/OpenSign/src/pages/GuestLogin.js @@ -45,7 +45,7 @@ function GuestLogin() { localStorage.setItem("baseUrl", newServer); localStorage.setItem("parseAppId", parseId); localStorage.setItem("_appName", appName); - //first condition is used decode base64 to string and get userEmail,documentId, contactBoookId data. + //this condition is used decode base64 to string and get userEmail,documentId, contactBoookId data. if (!id) { //`atob` function is used to decode base64 const decodebase64 = atob(base64url); From a26e65f76f3d81cf672d7d8814979e9ee08f070a Mon Sep 17 00:00:00 2001 From: RaktimaNXG <raktima.c@nxglabs.com> Date: Tue, 7 May 2024 19:29:09 +0530 Subject: [PATCH 37/38] fix: x is not defined error in createDocumentWithTemplate and createDocumentwithCoordinate function --- .../customRoute/v1/routes/CreateDocumentWithTemplate.js | 6 +++--- .../customRoute/v1/routes/createDocumentwithCoordinate.js | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js b/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js index f70466a9f..49615a6cc 100644 --- a/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js +++ b/apps/OpenSignServer/cloud/customRoute/v1/routes/CreateDocumentWithTemplate.js @@ -317,13 +317,13 @@ export default async function createDocumentWithTemplate(request, response) { properties: { response_code: 200 }, }); } - //encode this url value `${res.id}/${x.email}/${x.contactPtr.objectId}` to base64 using `btoa` function - const encodeBase64 = btoa(`${res.id}/${x.email}/${x.contactPtr.objectId}`); return response.json({ objectId: res.id, signurl: contact.map(x => ({ email: x.email, - url: `${baseUrl.origin}/login/${encodeBase64}`, + url: `${baseUrl.origin}/login/${btoa( + `${res.id}/${x.email}/${x.contactPtr.objectId}` + )}`, })), message: 'Document sent successfully!', }); diff --git a/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js b/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js index 52c00ef33..560b114aa 100644 --- a/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js +++ b/apps/OpenSignServer/cloud/customRoute/v1/routes/createDocumentwithCoordinate.js @@ -361,13 +361,11 @@ export default async function createDocumentwithCoordinate(request, response) { properties: { response_code: 200 }, }); } - //encode this url value `${res.id}/${x.email}/${x.contactPtr.objectId}` to base64 using `btoa` function - const encodeBase64 = btoa(`${res.id}/${x.email}/${x.contactPtr.objectId}`); return response.json({ objectId: res.id, signurl: contact.map(x => ({ email: x.email, - url: `${baseUrl.origin}/login/${encodeBase64}`, + url: `${baseUrl.origin}/login/${btoa(`${res.id}/${x.email}/${x.contactPtr.objectId}`)}`, })), message: 'Document sent successfully!', }); From 179c3c5dfe5e7a4e02472120a818e9b970a8bab6 Mon Sep 17 00:00:00 2001 From: RaktimaNXG <raktima.c@nxglabs.com> Date: Tue, 7 May 2024 19:57:39 +0530 Subject: [PATCH 38/38] fix: document loading error No PDF file specified --- apps/OpenSign/src/pages/PdfRequestFiles.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/OpenSign/src/pages/PdfRequestFiles.js b/apps/OpenSign/src/pages/PdfRequestFiles.js index 57a6bef83..c5ab5659d 100644 --- a/apps/OpenSign/src/pages/PdfRequestFiles.js +++ b/apps/OpenSign/src/pages/PdfRequestFiles.js @@ -1337,7 +1337,10 @@ function PdfRequestFiles() { </ModalUi> {/* this component used to render all pdf pages in left side */} <RenderAllPdfPage - signPdfUrl={pdfDetails[0] && pdfDetails[0].SignedUrl} + signPdfUrl={ + pdfDetails[0] && + (pdfDetails[0]?.SignedUrl || pdfDetails[0]?.URL) + } allPages={allPages} setAllPages={setAllPages} setPageNumber={setPageNumber}