From da053658e8eae37a27ab73a74c0ea5a338029703 Mon Sep 17 00:00:00 2001 From: prafull-opensignlabs Date: Mon, 2 Sep 2024 19:29:36 +0530 Subject: [PATCH] refactor: handle uncuaght err in send mail --- .../public/locales/en/translation.json | 8 +- .../public/locales/fr/translation.json | 8 +- .../src/components/pdf/EmailComponent.js | 19 ++- apps/OpenSign/src/pages/PdfRequestFiles.js | 3 + apps/OpenSign/src/pages/PlaceHolderSign.js | 109 ++++++++++-------- apps/OpenSign/src/pages/SignyourselfPdf.js | 73 ++++++------ apps/OpenSign/src/pages/Webhook.js | 7 +- apps/OpenSign/src/primitives/ModalUi.js | 8 +- apps/OpenSign/src/primitives/QuotaCard.js | 53 +++++++++ apps/OpenSignServer/Utils.js | 24 ++-- .../parsefunction/sendMailGmailProvider.js | 3 +- .../cloud/parsefunction/sendMailv3.js | 103 +++++++++++------ 12 files changed, 275 insertions(+), 143 deletions(-) create mode 100644 apps/OpenSign/src/primitives/QuotaCard.js diff --git a/apps/OpenSign/public/locales/en/translation.json b/apps/OpenSign/public/locales/en/translation.json index ee282f354..02df68661 100644 --- a/apps/OpenSign/public/locales/en/translation.json +++ b/apps/OpenSign/public/locales/en/translation.json @@ -623,5 +623,11 @@ "generate-test-token":"Generate Test Token", "regenerate-test-token":"Regenerate Test Token", "help-test-token":"This token can be used to test the APIs at the https://sandbox.opensignlabs.com/api/v1 endpoint, allowing you to conduct unlimited document signatures. Please note that the sandbox API will sign your documents with self-signed certificates, which may not be recognized as valid by Adobe. Once you’ve completed your testing, you can upgrade to one of our paid plans to generate a production token.", - "help-api-token":"This token can be used to access the production APIs at the {{origin}}/api/v1 endpoint. It can only be generated on one of our paid plans." + "help-api-token":"This token can be used to access the production APIs at the {{origin}}/api/v1 endpoint. It can only be generated on one of our paid plans.", + "Add-Webhook":"Add Webhook", + "quotamailselfsign": "You've reached your limit of 20 emails for this month. Upgrade now to continue sending emails directly.", + "quotamail": "You've reached your limit of 20 signature request emails for this month. Upgrade now to continue sending emails directly.", + "quotamailTip":"Tip: You can still sign unlimited documents by manually sharing the signing request links below.", + "quotamailhead":"Quota Reached" + } diff --git a/apps/OpenSign/public/locales/fr/translation.json b/apps/OpenSign/public/locales/fr/translation.json index 72a30b619..4faac0193 100644 --- a/apps/OpenSign/public/locales/fr/translation.json +++ b/apps/OpenSign/public/locales/fr/translation.json @@ -624,5 +624,11 @@ "generate-test-token": "Générer jeton de test", "regenerate-test-token":"Régénérer le jeton de test", "help-test-token":"Ce jeton peut être utilisé pour tester les API au niveau du point de terminaison https://sandbox.opensignlabs.com/api/v1, vous permettant ainsi d'effectuer un nombre illimité de signatures de documents. Veuillez noter que l'API sandbox signera vos documents avec des certificats auto-signés, qui peuvent ne pas être reconnus comme valides par Adobe. Une fois vos tests terminés, vous pouvez passer à l’un de nos forfaits payants pour générer un jeton de production.", - "help-api-token":"Ce jeton peut être utilisé pour accéder aux API de production au point de terminaison {{origin}}/api/v1. Il ne peut être généré que sur l'un de nos forfaits payants." + "help-api-token":"Ce jeton peut être utilisé pour accéder aux API de production au point de terminaison {{origin}}/api/v1. Il ne peut être généré que sur l'un de nos forfaits payants.", + "Add-Webhook":"Ajouter Webhook", + "quotamailselfsign": "Vous avez atteint votre limite de 20 e-mails pour ce mois. Mettez à niveau maintenant pour continuer à envoyer des e-mails directement.", + "quotamail": "Vous avez atteint votre limite de 20 e-mails de demande de signature pour ce mois. Mettez à niveau maintenant pour continuer à envoyer des e-mails directement.", + "quotamailTip":"Astuce : Vous pouvez toujours signer un nombre illimité de documents en partageant manuellement les liens de demande de signature ci-dessous.", + "quotamailhead":"Quota atteint" + } diff --git a/apps/OpenSign/src/components/pdf/EmailComponent.js b/apps/OpenSign/src/components/pdf/EmailComponent.js index 948f58994..f0404439c 100644 --- a/apps/OpenSign/src/components/pdf/EmailComponent.js +++ b/apps/OpenSign/src/components/pdf/EmailComponent.js @@ -15,7 +15,8 @@ function EmailComponent({ sender, setIsAlert, extUserId, - activeMailAdapter + activeMailAdapter, + planCode }) { const { t } = useTranslation(); const [emailList, setEmailList] = useState([]); @@ -34,7 +35,7 @@ function EmailComponent({ const imgPng = "https://qikinnovation.ams3.digitaloceanspaces.com/logo.png"; - let url = `${localStorage.getItem("baseUrl")}functions/sendmailv3/`; + let url = `${localStorage.getItem("baseUrl")}functions/sendmailv3`; const headers = { "Content-Type": "application/json", "X-Parse-Application-Id": localStorage.getItem("parseAppId"), @@ -50,6 +51,7 @@ function EmailComponent({ recipient: emailList[i], subject: `${sender.name} has signed the doc - ${pdfName}`, from: sender.email, + plan: planCode, html: "
{ setSuccessEmail(false); - setIsEmail(false); setEmailValue(""); setEmailList([]); }, 1500); - setIsLoading(false); - } else if (sendMail && sendMail.data.result.status === "error") { + } else if (sendMail?.data?.result?.status === "quota-reached") { setIsLoading(false); setIsEmail(false); - setIsAlert({ - isShow: true, - alertMessage: t("something-went-wrong-mssg") - }); + setIsAlert({ isShow: true, alertMessage: "quotareached" }); setEmailValue(""); setEmailList([]); } else { diff --git a/apps/OpenSign/src/pages/PdfRequestFiles.js b/apps/OpenSign/src/pages/PdfRequestFiles.js index 3dacc088e..e7c0e3230 100644 --- a/apps/OpenSign/src/pages/PdfRequestFiles.js +++ b/apps/OpenSign/src/pages/PdfRequestFiles.js @@ -133,6 +133,7 @@ function PdfRequestFiles(props) { const [documentId, setDocumentId] = useState(""); const [isPublicContact, setIsPublicContact] = useState(false); const [pdfArrayBuffer, setPdfArrayBuffer] = useState(""); + const [plancode, setPlanCode] = useState(""); const isHeader = useSelector((state) => state.showHeader); const divRef = useRef(null); @@ -246,6 +247,7 @@ function PdfRequestFiles(props) { const plan = res.plan; const billingDate = res?.billingDate; const status = res?.status; + setPlanCode(plan); if (plan === "freeplan") { return true; } else if (billingDate) { @@ -986,6 +988,7 @@ function PdfRequestFiles(props) { ? replaceVar?.subject : `${pdfDetails?.[0].ExtUserPtr.Name} has requested you to sign "${pdfDetails?.[0].Name}"`, from: senderEmail, + plan: plancode, html: requestBody ? replaceVar?.body : "
{}) - .catch((err) => { - console.log("axois err ", err); - }); + ); + } catch (err) { + console.log("axois err ", err); + } } catch (e) { console.log("error", e); } - setIsSend(true); setIsMailSend(true); - const loadObj = { - isLoad: false - }; - setIsLoading(loadObj); + setIsLoading({ isLoad: false }); + setIsUiLoading(false); + } else if (sendMail?.data?.result?.status === "quota-reached") { + setMailStatus("quotareached"); + setIsSend(true); + setIsMailSend(true); setIsUiLoading(false); } else { setMailStatus("failed"); @@ -1829,7 +1831,7 @@ function PlaceHolderSign() { {t("or")}
-
{handleShareList()}
+
{handleShareList()}
)}
@@ -1838,7 +1840,11 @@ function PlaceHolderSign() { {/* this modal is used show send mail message and after send mail success message */} { setIsSend(false); setSignerPos([]); @@ -1852,40 +1858,53 @@ function PlaceHolderSign() {

{t("placeholder-alert-4")}

{isCurrUser &&

{t("placeholder-alert-5")}

}
+ ) : mailStatus === "quotareached" ? ( +
+ { + setIsSend(false); + setSignerPos([]); + navigate("/report/1MwEuxLEkF"); + }} + /> +
{handleShareList()}
+
) : (

{t("placeholder-alert-6")}

)} {!mailStatus && (
)} -
- {isCurrUser && ( - - )} - -
+ {isCurrUser && ( + + )} + +
+ )}
state.showHeader); const [scale, setScale] = useState(1); const [pdfRotateBase64, setPdfRotatese64] = useState(""); - const [isRotate, setIsRotate] = useState({ - status: false, - degree: 0 - }); + const [isRotate, setIsRotate] = useState({ status: false, degree: 0 }); + const [isSubscribe, setIsSubscribe] = useState({ plan: "", isValid: true }); const divRef = useRef(null); const nodeRef = useRef(null); const [, drop] = useDrop({ accept: "BOX", drop: (item, monitor) => addPositionOfSignature(item, monitor), - collect: (monitor) => ({ - isOver: !!monitor.isOver() - }) + collect: (monitor) => ({ isOver: !!monitor.isOver() }) }); const pdfRef = useRef(); const [{ isDragSign }, dragSignature] = useDrag({ type: "BOX", - item: { - id: 1, - text: "signature" - }, - collect: (monitor) => ({ - isDragSign: !!monitor.isDragging() - }) + item: { id: 1, text: "signature" }, + collect: (monitor) => ({ isDragSign: !!monitor.isDragging() }) }); - const [{ isDragStamp }, dragStamp] = useDrag({ type: "BOX", - item: { - id: 2, - text: "stamp" - }, - - collect: (monitor) => ({ - isDragStamp: !!monitor.isDragging() - }) + item: { id: 2, text: "stamp" }, + collect: (monitor) => ({ isDragStamp: !!monitor.isDragging() }) }); const index = xyPostion?.findIndex((object) => { return object.pageNumber === pageNumber; }); - // rowlevel={JSON.parse(localStorage.getItem("rowlevel"))} const rowLevel = localStorage.getItem("rowlevel") && JSON.parse(localStorage.getItem("rowlevel")); - const signObjId = rowLevel && rowLevel?.id ? rowLevel.id : rowLevel?.objectId && rowLevel.objectId; - const documentId = docId ? docId : signObjId && signObjId; const senderUser = localStorage.getItem( @@ -217,6 +199,12 @@ function SignYourSelf() { //function for get document details for perticular signer with signer'object id const getDocumentDetails = async (showComplete) => { + try { + const subscribe = await checkIsSubscribed(); + setIsSubscribe(subscribe); + } catch (err) { + console.log("err in fetch sub", err); + } try { let isCompleted; //getting document details @@ -585,8 +573,7 @@ function SignYourSelf() { docCls.id = documentId; docCls.set("Placeholders", xyPostion); docCls.set("IsSignyourself", true); - const res = await docCls.save(); - console.log("Res", res); + await docCls.save(); } catch (e) { console.log("error", e); alert(t("something-went-wrong-mssg")); @@ -730,7 +717,7 @@ function SignYourSelf() { //function for get digital signature const signPdfFun = async (base64Url, documentId) => { let isCustomCompletionMail = false; - const getIsSubscribe = await checkIsSubscribed(); + const tenantDetails = await getTenantDetails(jsonSender.objectId); if (tenantDetails && tenantDetails === "user does not exist!") { alert(t("user-not-exist")); @@ -738,7 +725,7 @@ function SignYourSelf() { if ( tenantDetails?.CompletionBody && tenantDetails?.CompletionSubject && - getIsSubscribe.isValid + isSubscribe?.isValid ) { isCustomCompletionMail = true; } @@ -1255,14 +1242,29 @@ function SignYourSelf() {
- setIsAlert({ isShow: false, alertMessage: "" }) + isAlert.alertMessage === "quotareached" + ? false + : setIsAlert({ isShow: false, alertMessage: "" }) } > -
-

{isAlert.alertMessage}

-
+ {isAlert.alertMessage === "quotareached" ? ( + + setIsAlert({ isShow: false, alertMessage: "" }) + } + /> + ) : ( +
+

{isAlert.alertMessage}

+
+ )}
{/* this modal is used show this document is already sign */} @@ -1342,6 +1344,7 @@ function SignYourSelf() { setIsAlert={setIsAlert} extUserId={extUserId} activeMailAdapter={activeMailAdapter} + planCode={isSubscribe?.plan} /> {/* pdf header which contain funish back button */}
{ - if (!validplan[isSubscribe.plan] && isEnableSubscription) { + if (!isSubscribe?.isValid && isEnableSubscription) { setIsTour(true); } else { setIsModal(!isModal); @@ -146,7 +145,7 @@ function Webhook() {
{error && {error}} @@ -178,7 +177,7 @@ function Webhook() {
- {!validplan[isSubscribe.plan] && isEnableSubscription && ( + {!isSubscribe?.isValid && isEnableSubscription && (
diff --git a/apps/OpenSign/src/primitives/ModalUi.js b/apps/OpenSign/src/primitives/ModalUi.js index c11094627..2f03c0d33 100644 --- a/apps/OpenSign/src/primitives/ModalUi.js +++ b/apps/OpenSign/src/primitives/ModalUi.js @@ -21,9 +21,11 @@ const ModalUi = ({ > {showHeader && ( <> -

- {title} -

+ {title && ( +

+ {title} +

+ )} {showClose && ( +
+
+ + + ) : ( + <> +
+
+

{t("quotamail")}

+

{t("quotamailTip")}

+
+ +
+
+
+ + ); +}; + +export default QuotaCard; diff --git a/apps/OpenSignServer/Utils.js b/apps/OpenSignServer/Utils.js index 3e2660a21..2ff086bdd 100644 --- a/apps/OpenSignServer/Utils.js +++ b/apps/OpenSignServer/Utils.js @@ -91,7 +91,7 @@ const saveDataFile = async (size, fileUrl, tenantPtr) => { } }; -export const updateMailCount = async extUserId => { +export const updateMailCount = async (extUserId, plan, monthchange) => { // Update count in contracts_Users class const query = new Parse.Query('contracts_Users'); query.equalTo('objectId', extUserId); @@ -100,13 +100,23 @@ export const updateMailCount = async extUserId => { const contractUser = await query.first({ useMasterKey: true }); if (contractUser) { contractUser.increment('EmailCount', 1); + if (plan === 'freeplan') { + if (monthchange) { + contractUser.set('LastEmailCountReset', new Date()); + contractUser.set('MontlyfreeEmails', 1); + } else { + if (contractUser?.get('MontlyfreeEmails')) { + contractUser.increment('MontlyfreeEmails', 1); + if (contractUser?.get('LastEmailCountReset')) { + contractUser.set('LastEmailCountReset', new Date()); + } + } else { + contractUser.set('MontlyfreeEmails', 1); + contractUser.set('LastEmailCountReset', new Date()); + } + } + } await contractUser.save(null, { useMasterKey: true }); - } else { - // Create new entry if not found - const ContractsUsers = Parse.Object.extend('contracts_Users'); - const newContractUser = new ContractsUsers(); - newContractUser.set('EmailCount', 1); - await newContractUser.save(null, { useMasterKey: true }); } } catch (error) { console.log('Error updating EmailCount in contracts_Users: ' + error.message); diff --git a/apps/OpenSignServer/cloud/parsefunction/sendMailGmailProvider.js b/apps/OpenSignServer/cloud/parsefunction/sendMailGmailProvider.js index 0a826f701..f614e5754 100644 --- a/apps/OpenSignServer/cloud/parsefunction/sendMailGmailProvider.js +++ b/apps/OpenSignServer/cloud/parsefunction/sendMailGmailProvider.js @@ -142,8 +142,7 @@ export default async function sendMailGmailProvider(_extRes, template) { raw: email, }, }); - // console.log('response ', response); - + console.log('gmail provider res: ', response?.status); return { code: 200, message: 'Email sent successfully' }; } catch (error) { console.error('Error sending email:', error); diff --git a/apps/OpenSignServer/cloud/parsefunction/sendMailv3.js b/apps/OpenSignServer/cloud/parsefunction/sendMailv3.js index eb23be4e4..4cef012d6 100644 --- a/apps/OpenSignServer/cloud/parsefunction/sendMailv3.js +++ b/apps/OpenSignServer/cloud/parsefunction/sendMailv3.js @@ -6,7 +6,7 @@ import Mailgun from 'mailgun.js'; import { smtpenable, smtpsecure, updateMailCount, useLocal } from '../../Utils.js'; import sendMailGmailProvider from './sendMailGmailProvider.js'; import { createTransport } from 'nodemailer'; -async function sendMailProvider(req) { +async function sendMailProvider(req, plan, monthchange) { const mailgunApiKey = process.env.MAILGUN_API_KEY; try { let transporterSMTP; @@ -69,18 +69,23 @@ async function sendMailProvider(req) { }; let attachment; - // `certificateBuffer` used to create buffer from pdf file - try { - const certificateBuffer = fs.readFileSync('./exports/certificate.pdf'); - const certificate = { - filename: 'certificate.pdf', - content: smtpenable ? certificateBuffer : undefined, //fs.readFileSync('./exports/exported_file_1223.pdf'), - data: smtpenable ? undefined : certificateBuffer, - }; - attachment = [file, certificate]; - } catch (err) { + const certificatePath = './exports/certificate.pdf'; + if (fs.existsSync(certificatePath)) { + try { + // `certificateBuffer` used to create buffer from pdf file + const certificateBuffer = fs.readFileSync(certificatePath); + const certificate = { + filename: 'certificate.pdf', + content: smtpenable ? certificateBuffer : undefined, //fs.readFileSync('./exports/exported_file_1223.pdf'), + data: smtpenable ? undefined : certificateBuffer, + }; + attachment = [file, certificate]; + } catch (err) { + attachment = [file]; + console.log('Err in read certificate sendmailv3', err); + } + } else { attachment = [file]; - console.log('Err in read certificate sendmailv3', err); } const from = req.params.from || ''; const mailsender = smtpenable ? process.env.SMTP_USER_EMAIL : process.env.MAILGUN_SENDER; @@ -96,38 +101,44 @@ async function sendMailProvider(req) { }; if (transporterSMTP) { const res = await transporterSMTP.sendMail(messageParams); - console.log('Res ', res); + console.log('smtp transporter res: ', res?.response); if (!res.err) { if (req.params?.extUserId) { - await updateMailCount(req.params.extUserId); + await updateMailCount(req.params.extUserId, plan, monthchange); } - try { - fs.unlinkSync('./exports/certificate.pdf'); - } catch (err) { - console.log('Err in unlink certificate sendmailv3'); + if (fs.existsSync(certificatePath)) { + try { + fs.unlinkSync(certificatePath); + } catch (err) { + console.log('Err in unlink certificate sendmailv3'); + } } return { status: 'success' }; } } else { if (mailgunApiKey) { const res = await mailgunClient.messages.create(mailgunDomain, messageParams); - console.log('Res ', res); + console.log('mailgun res: ', res?.status); if (res.status === 200) { if (req.params?.extUserId) { - await updateMailCount(req.params.extUserId); + await updateMailCount(req.params.extUserId, plan, monthchange); } - try { - fs.unlinkSync('./exports/certificate.pdf'); - } catch (err) { - console.log('Err in unlink certificate sendmailv3'); + if (fs.existsSync(certificatePath)) { + try { + fs.unlinkSync(certificatePath); + } catch (err) { + console.log('Err in unlink certificate sendmailv3'); + } } return { status: 'success' }; } } else { - try { - fs.unlinkSync('./exports/certificate.pdf'); - } catch (err) { - console.log('Err in unlink certificate sendmailv3'); + if (fs.existsSync(certificatePath)) { + try { + fs.unlinkSync(certificatePath); + } catch (err) { + console.log('Err in unlink certificate sendmailv3'); + } } return { status: 'error' }; } @@ -147,20 +158,20 @@ async function sendMailProvider(req) { if (transporterSMTP) { const res = await transporterSMTP.sendMail(messageParams); - console.log('Res ', res); + console.log('smtp transporter res: ', res?.response); if (!res.err) { if (req.params?.extUserId) { - await updateMailCount(req.params.extUserId); + await updateMailCount(req.params.extUserId, plan, monthchange); } return { status: 'success' }; } } else { if (mailgunApiKey) { const res = await mailgunClient.messages.create(mailgunDomain, messageParams); - console.log('Res ', res); + console.log('mailgun res: ', res?.status); if (res.status === 200) { if (req.params?.extUserId) { - await updateMailCount(req.params.extUserId); + await updateMailCount(req.params.extUserId, plan, monthchange); } return { status: 'success' }; } @@ -180,6 +191,7 @@ async function sendmailv3(req) { const mailProvider = req.params.mailProvider || 'default'; if (mailProvider) { try { + const Plan = req.params.plan; const extUserId = req.params.extUserId || ''; const pdfName = req.params.pdfName || ''; const template = { @@ -203,8 +215,31 @@ async function sendmailv3(req) { return { status: 'error' }; } } else { - const nonCustomMail = await sendMailProvider(req); - return nonCustomMail; + if (Plan && Plan === 'freeplan') { + let MontlyfreeEmails = _extRes?.MontlyfreeEmails || 0; + if (_extRes?.LastEmailCountReset?.iso) { + const lastDate = new Date(_extRes?.LastEmailCountReset?.iso); + const newDate = new Date(); + const isMonthChange = newDate.getMonth() > lastDate.getMonth(); + if (isMonthChange) { + const nonCustomMail = await sendMailProvider(req, Plan, true); + return nonCustomMail; + } else { + if (MontlyfreeEmails >= 20) { + return { status: 'quota-reached' }; + } else { + const nonCustomMail = await sendMailProvider(req, Plan); + return nonCustomMail; + } + } + } else { + const nonCustomMail = await sendMailProvider(req, Plan); + return nonCustomMail; + } + } else { + const nonCustomMail = await sendMailProvider(req, ''); + return nonCustomMail; + } } } } catch (err) {