mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-08-21 15:12:34 +02:00
Merge pull request
This commit is contained in:
@@ -10,37 +10,28 @@ async function DocumentAfterFind(request) {
|
||||
const certificateUrl = obj.get('CertificateUrl') && obj.get('CertificateUrl');
|
||||
const isPrefillExist = obj?.get('Placeholders')?.some(x => x.Role === 'prefill');
|
||||
const Placeholder = obj?.get('Placeholders') || [];
|
||||
if (useLocal !== 'true') {
|
||||
if (isPrefillExist) {
|
||||
const updatedPlaceHolder = await handleValidImage(Placeholder);
|
||||
obj.set('Placeholders', updatedPlaceHolder);
|
||||
|
||||
const shouldUsePresigned = useLocal !== 'true';
|
||||
const isLocal = useLocal == 'true';
|
||||
|
||||
const resolveUrl = async rawUrl => {
|
||||
if (!rawUrl) return rawUrl;
|
||||
if (shouldUsePresigned) {
|
||||
return await getPresignedUrl(rawUrl);
|
||||
} else if (isLocal) {
|
||||
return presignedlocalUrl(rawUrl);
|
||||
}
|
||||
if (SignedUrl) {
|
||||
obj.set('SignedUrl', getPresignedUrl(SignedUrl));
|
||||
}
|
||||
if (Url) {
|
||||
obj.set('URL', getPresignedUrl(Url));
|
||||
}
|
||||
if (certificateUrl) {
|
||||
obj.set('CertificateUrl', getPresignedUrl(certificateUrl));
|
||||
}
|
||||
return [obj];
|
||||
} else if (useLocal == 'true') {
|
||||
if (isPrefillExist) {
|
||||
const updatedPlaceHolder = await handleValidImage(Placeholder);
|
||||
obj.set('Placeholders', updatedPlaceHolder);
|
||||
}
|
||||
if (SignedUrl) {
|
||||
obj.set('SignedUrl', presignedlocalUrl(SignedUrl));
|
||||
}
|
||||
if (Url) {
|
||||
obj.set('URL', presignedlocalUrl(Url));
|
||||
}
|
||||
if (certificateUrl) {
|
||||
obj.set('CertificateUrl', presignedlocalUrl(certificateUrl));
|
||||
}
|
||||
return [obj];
|
||||
};
|
||||
|
||||
if (isPrefillExist) {
|
||||
const updatedPlaceHolder = await handleValidImage(Placeholder);
|
||||
obj.set('Placeholders', updatedPlaceHolder);
|
||||
}
|
||||
|
||||
if (SignedUrl) obj.set('SignedUrl', await resolveUrl(SignedUrl));
|
||||
if (Url) obj.set('URL', await resolveUrl(Url));
|
||||
if (certificateUrl) obj.set('CertificateUrl', await resolveUrl(certificateUrl));
|
||||
return [obj];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ export default async function forwardDoc(request) {
|
||||
let mailRes;
|
||||
for (let i = 0; i < recipients.length; i++) {
|
||||
const logo = `<img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' style='padding:20px'/>`;
|
||||
const opurl = ` <a href='mailto:complaint@opensiglabs.com' target=_blank>here</a>`;
|
||||
|
||||
const themeColor = '#47a3ad';
|
||||
|
||||
let params = {
|
||||
@@ -51,8 +51,7 @@ export default async function forwardDoc(request) {
|
||||
`<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'/></head><body><div style='background-color:#f5f5f5;padding:20px'><div style='background-color:white'><div>` +
|
||||
`${logo}</div><div style='padding:2px;font-family:system-ui;background-color:${themeColor}'><p style='font-size:20px;font-weight:400;color:white;padding-left:20px'>Document Copy</p></div><div>` +
|
||||
`<p style='padding:20px;font-family:system-ui;font-size:14px'>A copy of the document <strong>${docName}</strong> is attached to this email. Kindly download the document from the attachment.</p>` +
|
||||
`</div></div><div><p>This is an automated email from ${TenantAppName}. For any queries regarding this email, please contact the sender ${replyTo} directly. ` +
|
||||
`If you think this email is inappropriate or spam, you may file a complaints with ${TenantAppName}${opurl}.</p></div></div></body></html>`,
|
||||
`</div></div><div><p>This is an automated email from ${TenantAppName}. For any queries regarding this email, please contact the sender ${replyTo} directly.</p></div></div></body></html>`,
|
||||
};
|
||||
mailRes = await axios.post(`${cloudServerUrl}/functions/sendmailv3`, params, {
|
||||
headers: {
|
||||
|
||||
@@ -11,6 +11,7 @@ export default async function GetLogoByDomain(request) {
|
||||
const updateRes = JSON.parse(JSON.stringify(res));
|
||||
return {
|
||||
logo: updateRes?.Logo,
|
||||
favicon: updateRes?.Favicon || updateRes?.Logo,
|
||||
appname: appName,
|
||||
user: 'exist',
|
||||
};
|
||||
|
||||
@@ -1,44 +1,28 @@
|
||||
import { useLocal } from '../../Utils.js';
|
||||
import getPresignedUrl, { presignedlocalUrl } from './getSignedUrl.js';
|
||||
|
||||
const resolveUrl = async rawUrl => {
|
||||
const isLocal = useLocal == 'true';
|
||||
const shouldUsePresigned = useLocal !== 'true';
|
||||
if (!rawUrl) return rawUrl;
|
||||
if (shouldUsePresigned) {
|
||||
return await getPresignedUrl(rawUrl);
|
||||
} else if (isLocal) {
|
||||
return presignedlocalUrl(rawUrl);
|
||||
}
|
||||
};
|
||||
|
||||
async function SignatureAfterFind(request) {
|
||||
if (useLocal !== 'true') {
|
||||
if (request.objects.length === 1) {
|
||||
if (request.objects) {
|
||||
const obj = request.objects[0];
|
||||
const ImageURL = obj?.get('ImageURL') && obj?.get('ImageURL');
|
||||
const Initials = obj?.get('Initials') && obj?.get('Initials');
|
||||
const Stamp = obj?.get('Stamp') && obj?.get('Stamp');
|
||||
if (ImageURL) {
|
||||
obj.set('ImageURL', getPresignedUrl(ImageURL));
|
||||
}
|
||||
if (Initials) {
|
||||
obj.set('Initials', getPresignedUrl(Initials));
|
||||
}
|
||||
if (Stamp) {
|
||||
obj.set('Stamp', getPresignedUrl(Stamp));
|
||||
}
|
||||
return [obj];
|
||||
}
|
||||
}
|
||||
} else if (useLocal == 'true') {
|
||||
if (request.objects.length === 1) {
|
||||
if (request.objects) {
|
||||
const obj = request.objects[0];
|
||||
const ImageURL = obj?.get('ImageURL') && obj?.get('ImageURL');
|
||||
const Initials = obj?.get('Initials') && obj?.get('Initials');
|
||||
const Stamp = obj?.get('Stamp') && obj?.get('Stamp');
|
||||
if (ImageURL) {
|
||||
obj.set('ImageURL', presignedlocalUrl(ImageURL));
|
||||
}
|
||||
if (Initials) {
|
||||
obj.set('Initials', presignedlocalUrl(Initials));
|
||||
}
|
||||
if (Stamp) {
|
||||
obj.set('Stamp', presignedlocalUrl(Stamp));
|
||||
}
|
||||
return [obj];
|
||||
}
|
||||
if (request.objects.length === 1) {
|
||||
if (request.objects) {
|
||||
const obj = request.objects[0];
|
||||
const ImageURL = obj?.get('ImageURL') && obj?.get('ImageURL');
|
||||
const Initials = obj?.get('Initials') && obj?.get('Initials');
|
||||
const Stamp = obj?.get('Stamp') && obj?.get('Stamp');
|
||||
if (ImageURL) obj.set('ImageURL', await resolveUrl(ImageURL));
|
||||
if (Initials) obj.set('Initials', await resolveUrl(Initials));
|
||||
if (Stamp) obj.set('Stamp', await resolveUrl(Stamp));
|
||||
return [obj];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,37 +10,26 @@ async function TemplateAfterFind(request) {
|
||||
const certificateUrl = obj.get('CertificateUrl') && obj?.get('CertificateUrl');
|
||||
const isPrefillExist = obj?.get('Placeholders')?.some(x => x.Role === 'prefill');
|
||||
const Placeholder = obj?.get('Placeholders') || [];
|
||||
if (useLocal !== 'true') {
|
||||
if (isPrefillExist) {
|
||||
const updatedPlaceHolder = await handleValidImage(Placeholder);
|
||||
obj.set('Placeholders', updatedPlaceHolder);
|
||||
const shouldUsePresigned = useLocal !== 'true';
|
||||
const isLocal = useLocal == 'true';
|
||||
|
||||
const resolveUrl = async rawUrl => {
|
||||
if (!rawUrl) return rawUrl;
|
||||
if (shouldUsePresigned) {
|
||||
return await getPresignedUrl(rawUrl);
|
||||
} else if (isLocal) {
|
||||
return presignedlocalUrl(rawUrl);
|
||||
}
|
||||
if (SignedUrl) {
|
||||
obj.set('SignedUrl', getPresignedUrl(SignedUrl));
|
||||
}
|
||||
if (Url) {
|
||||
obj.set('URL', getPresignedUrl(Url));
|
||||
}
|
||||
if (certificateUrl) {
|
||||
obj.set('CertificateUrl', getPresignedUrl(certificateUrl));
|
||||
}
|
||||
return [obj];
|
||||
} else if (useLocal == 'true') {
|
||||
if (isPrefillExist) {
|
||||
const updatedPlaceHolder = await handleValidImage(Placeholder);
|
||||
obj.set('Placeholders', updatedPlaceHolder);
|
||||
}
|
||||
if (SignedUrl) {
|
||||
obj.set('SignedUrl', presignedlocalUrl(SignedUrl));
|
||||
}
|
||||
if (Url) {
|
||||
obj.set('URL', presignedlocalUrl(Url));
|
||||
}
|
||||
if (certificateUrl) {
|
||||
obj.set('CertificateUrl', presignedlocalUrl(certificateUrl));
|
||||
}
|
||||
return [obj];
|
||||
};
|
||||
|
||||
if (isPrefillExist) {
|
||||
const updatedPlaceHolder = await handleValidImage(Placeholder);
|
||||
obj.set('Placeholders', updatedPlaceHolder);
|
||||
}
|
||||
if (SignedUrl) obj.set('SignedUrl', await resolveUrl(SignedUrl));
|
||||
if (Url) obj.set('URL', await resolveUrl(Url));
|
||||
if (certificateUrl) obj.set('CertificateUrl', await resolveUrl(certificateUrl));
|
||||
return [obj];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,27 @@
|
||||
import { useLocal } from '../../Utils.js';
|
||||
import getPresignedUrl, { presignedlocalUrl } from './getSignedUrl.js';
|
||||
|
||||
const resolveUrl = async rawUrl => {
|
||||
const isLocal = useLocal == 'true';
|
||||
const shouldUsePresigned = useLocal !== 'true';
|
||||
if (!rawUrl) return rawUrl;
|
||||
if (shouldUsePresigned) {
|
||||
return await getPresignedUrl(rawUrl);
|
||||
} else if (isLocal) {
|
||||
return presignedlocalUrl(rawUrl);
|
||||
}
|
||||
};
|
||||
|
||||
async function TenantAterFind(request) {
|
||||
if (useLocal !== 'true') {
|
||||
if (request.objects.length === 1) {
|
||||
if (request.objects) {
|
||||
const obj = request.objects[0];
|
||||
const Logo = obj?.get('Logo') && obj?.get('Logo');
|
||||
if (Logo) {
|
||||
obj.set('Logo', getPresignedUrl(Logo));
|
||||
}
|
||||
return [obj];
|
||||
}
|
||||
}
|
||||
} else if (useLocal == 'true') {
|
||||
if (request.objects.length === 1) {
|
||||
if (request.objects) {
|
||||
const obj = request.objects[0];
|
||||
const Logo = obj?.get('Logo') && obj?.get('Logo');
|
||||
if (Logo) {
|
||||
obj.set('Logo', presignedlocalUrl(Logo));
|
||||
}
|
||||
return [obj];
|
||||
}
|
||||
if (request.objects.length === 1) {
|
||||
if (request.objects) {
|
||||
const obj = request.objects[0];
|
||||
const Logo = obj?.get('Logo') && obj?.get('Logo');
|
||||
const Favicon = obj?.get('Favicon') && obj?.get('Favicon');
|
||||
|
||||
if (Logo) obj.set('Logo', await resolveUrl(Logo));
|
||||
if (Favicon) obj.set('Favicon', await resolveUrl(Favicon));
|
||||
return [obj];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,24 @@
|
||||
import { useLocal } from '../../Utils.js';
|
||||
import getPresignedUrl, { presignedlocalUrl } from './getSignedUrl.js';
|
||||
|
||||
const resolveUrl = async rawUrl => {
|
||||
const isLocal = useLocal == 'true';
|
||||
const shouldUsePresigned = useLocal !== 'true';
|
||||
if (!rawUrl) return rawUrl;
|
||||
if (shouldUsePresigned) {
|
||||
return await getPresignedUrl(rawUrl);
|
||||
} else if (isLocal) {
|
||||
return presignedlocalUrl(rawUrl);
|
||||
}
|
||||
};
|
||||
|
||||
async function UserAfterFind(request) {
|
||||
if (useLocal !== 'true') {
|
||||
if (request.objects.length === 1) {
|
||||
if (request.objects) {
|
||||
const obj = request.objects[0];
|
||||
const ProfilePic = obj?.get('ProfilePic') && obj?.get('ProfilePic');
|
||||
if (ProfilePic) {
|
||||
obj.set('ProfilePic', getPresignedUrl(ProfilePic));
|
||||
}
|
||||
return [obj];
|
||||
}
|
||||
}
|
||||
} else if (useLocal == 'true') {
|
||||
if (request.objects.length === 1) {
|
||||
if (request.objects) {
|
||||
const obj = request.objects[0];
|
||||
const ProfilePic = obj?.get('ProfilePic') && obj?.get('ProfilePic');
|
||||
if (ProfilePic) {
|
||||
obj.set('ProfilePic', presignedlocalUrl(ProfilePic));
|
||||
}
|
||||
return [obj];
|
||||
}
|
||||
if (request.objects.length === 1) {
|
||||
if (request.objects) {
|
||||
const obj = request.objects[0];
|
||||
const ProfilePic = obj?.get('ProfilePic') && obj?.get('ProfilePic');
|
||||
if (ProfilePic) obj.set('ProfilePic', await resolveUrl(ProfilePic));
|
||||
return [obj];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,19 +2,103 @@ import axios from 'axios';
|
||||
import { cloudServerUrl, mailTemplate, replaceMailVaribles, serverAppId } from '../../Utils.js';
|
||||
import { setDocumentCount } from '../../utils/CountUtils.js';
|
||||
|
||||
import crypto from 'crypto';
|
||||
|
||||
function chunkArray(arr, size) {
|
||||
const out = [];
|
||||
for (let i = 0; i < arr.length; i += size) out.push(arr.slice(i, i + size));
|
||||
return out;
|
||||
}
|
||||
|
||||
async function mapWithConcurrency(items, concurrency, fn) {
|
||||
const results = new Array(items.length);
|
||||
let idx = 0;
|
||||
|
||||
async function worker() {
|
||||
while (true) {
|
||||
const current = idx++;
|
||||
if (current >= items.length) return;
|
||||
results[current] = await fn(items[current], current);
|
||||
}
|
||||
}
|
||||
|
||||
const workers = [];
|
||||
for (let i = 0; i < Math.min(concurrency, items.length); i++) {
|
||||
workers.push(worker());
|
||||
}
|
||||
await Promise.all(workers);
|
||||
return results;
|
||||
}
|
||||
|
||||
function toBase64(str) {
|
||||
return Buffer.from(str, 'utf8').toString('base64');
|
||||
}
|
||||
|
||||
function uuid() {
|
||||
return crypto.randomUUID ? crypto.randomUUID() : crypto.randomBytes(16).toString('hex');
|
||||
}
|
||||
|
||||
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
|
||||
const appId = serverAppId;
|
||||
|
||||
async function sendOwnerSummaryEmail({
|
||||
ownerEmail,
|
||||
ownerName,
|
||||
total,
|
||||
created,
|
||||
failed,
|
||||
failedList,
|
||||
}) {
|
||||
try {
|
||||
const url = `${serverUrl}/functions/sendmailv3`;
|
||||
const headers = { 'Content-Type': 'application/json', 'X-Parse-Application-Id': appId };
|
||||
|
||||
const subject = `Bulk send finished: ${failed} of ${total} failed to create`;
|
||||
|
||||
const failureHtml = failedList?.length
|
||||
? `<ul>${failedList
|
||||
.slice(0, 50)
|
||||
.map(f => `<li>#${f.index + 1}: ${String(f.error).slice(0, 200)}</li>`)
|
||||
.join('')}</ul>
|
||||
${failedList.length > 50 ? `<p>…and ${failedList.length - 50} more.</p>` : ''}`
|
||||
: `<p>No failures.</p>`;
|
||||
|
||||
const html = `
|
||||
<p>Hi ${ownerName || ''},</p>
|
||||
<p>Your bulk send processing is complete.</p>
|
||||
<p><b>Total requested:</b> ${total}<br/>
|
||||
<b>Created:</b> ${created}<br/>
|
||||
<b>Failed to create:</b> ${failed}</p>
|
||||
<h4>Failure details</h4>
|
||||
${failureHtml}
|
||||
`;
|
||||
|
||||
const params = {
|
||||
// keep provider selection consistent with your system; if you can’t decide, omit it.
|
||||
isbulksend: true,
|
||||
recipient: ownerEmail,
|
||||
subject,
|
||||
from: ownerEmail, // or use a tenant/from address if required by your provider
|
||||
replyto: ownerEmail,
|
||||
html,
|
||||
};
|
||||
|
||||
await axios.post(url, params, { headers });
|
||||
} catch (e) {
|
||||
console.log('batchdoc Failed to send owner summary email:', e?.message || e);
|
||||
}
|
||||
}
|
||||
|
||||
async function deductcount(docsCount, extUserId) {
|
||||
try {
|
||||
if (extUserId) {
|
||||
setDocumentCount(extUserId);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Err in deduct in quick send', err);
|
||||
console.log('batchdoc deductcount error: ', err);
|
||||
}
|
||||
}
|
||||
async function sendMail(document, publicUrl) {
|
||||
//sessionToken
|
||||
const baseUrl = new URL(publicUrl);
|
||||
const timeToCompleteDays = document?.TimeToCompleteDays || 15;
|
||||
const ExpireDate = new Date(document.createdAt);
|
||||
@@ -43,9 +127,9 @@ async function sendMail(document, publicUrl) {
|
||||
let existSigner = {};
|
||||
if (objectId) {
|
||||
existSigner = document?.Signers?.find(user => user.objectId === objectId);
|
||||
encodeBase64 = btoa(`${document.objectId}/${existSigner?.Email}/${objectId}`);
|
||||
encodeBase64 = toBase64(`${document.objectId}/${existSigner?.Email}/${objectId}`);
|
||||
} else {
|
||||
encodeBase64 = btoa(`${document.objectId}/${signerMail[i].email}`);
|
||||
encodeBase64 = toBase64(`${document.objectId}/${signerMail[i].email}`);
|
||||
}
|
||||
let signPdf = `${hostUrl}/login/${encodeBase64}`;
|
||||
const orgName = document.ExtUserPtr.Company ? document.ExtUserPtr.Company : '';
|
||||
@@ -91,139 +175,131 @@ async function sendMail(document, publicUrl) {
|
||||
replyto: senderEmail || '',
|
||||
html: replaceVar?.body ? replaceVar?.body : mailTemplate(mailparam).body,
|
||||
};
|
||||
const sendMail = await axios.post(url, params, { headers: headers });
|
||||
// if (sendMail.data.result.status === 'success') {
|
||||
// console.log('batch login mail sent');
|
||||
// }
|
||||
await axios.post(url, params, { headers: headers });
|
||||
} catch (error) {
|
||||
console.log('error', error);
|
||||
console.log('batchdoc sendmail error: ', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
async function batchQuery(userId, Documents, Ip, parseConfig, type, publicUrl) {
|
||||
|
||||
async function startBulkSendInBackground(userId, Documents, Ip, parseConfig, type, publicUrl) {
|
||||
const BATCH_LIMIT = 50; // Parse batch limit (safe)
|
||||
const DOC_MAIL_CONCURRENCY = 5;
|
||||
|
||||
// Find ext user
|
||||
const extCls = new Parse.Query('contracts_Users');
|
||||
extCls.equalTo('UserId', { __type: 'Pointer', className: '_User', objectId: userId });
|
||||
const resExt = await extCls.first({ useMasterKey: true });
|
||||
if (resExt) {
|
||||
const _resExt = JSON.parse(JSON.stringify(resExt));
|
||||
try {
|
||||
const requests = Documents.map(x => {
|
||||
const Signers = x.Signers;
|
||||
const placeholders = x?.Placeholders?.filter(p => p?.Role !== 'prefill');
|
||||
const allSigner = placeholders
|
||||
?.map(
|
||||
item => Signers?.find(e => item?.signerPtr?.objectId === e?.objectId) || item?.signerPtr
|
||||
)
|
||||
.filter(signer => Object.keys(signer).length > 0);
|
||||
const date = new Date();
|
||||
const isoDate = date.toISOString();
|
||||
let Acl = { [x.CreatedBy.objectId]: { read: true, write: true } };
|
||||
if (allSigner && allSigner.length > 0) {
|
||||
allSigner.forEach(x => {
|
||||
if (x?.CreatedBy?.objectId) {
|
||||
const obj = { [x.CreatedBy.objectId]: { read: true, write: true } };
|
||||
Acl = { ...Acl, ...obj };
|
||||
}
|
||||
});
|
||||
if (!resExt) throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
|
||||
|
||||
const _resExt = JSON.parse(JSON.stringify(resExt));
|
||||
|
||||
// Build Parse /batch requests from your existing mapping (same as your current code)
|
||||
const requests = Documents.map(x => {
|
||||
const Signers = x.Signers;
|
||||
const placeholders = x?.Placeholders?.filter(p => p?.Role !== 'prefill');
|
||||
const allSigner = placeholders
|
||||
?.map(
|
||||
item => Signers?.find(e => item?.signerPtr?.objectId === e?.objectId) || item?.signerPtr
|
||||
)
|
||||
.filter(signer => signer && Object.keys(signer).length > 0);
|
||||
const date = new Date();
|
||||
const isoDate = date.toISOString();
|
||||
let Acl = { [x.CreatedBy.objectId]: { read: true, write: true } };
|
||||
if (allSigner && allSigner.length > 0) {
|
||||
allSigner.forEach(x => {
|
||||
if (x?.CreatedBy?.objectId) {
|
||||
Acl = { ...Acl, [x.CreatedBy.objectId]: { read: true, write: true } };
|
||||
}
|
||||
let mailBody = x?.ExtUserPtr?.TenantId?.RequestBody || '';
|
||||
let mailSubject = x?.ExtUserPtr?.TenantId?.RequestSubject || '';
|
||||
return {
|
||||
method: 'POST',
|
||||
path: '/app/classes/contracts_Document',
|
||||
body: {
|
||||
Name: x.Name,
|
||||
URL: x.URL,
|
||||
Note: x.Note,
|
||||
Description: x.Description,
|
||||
CreatedBy: x.CreatedBy,
|
||||
SendinOrder: x.SendinOrder || true,
|
||||
ExtUserPtr: {
|
||||
__type: 'Pointer',
|
||||
className: x.ExtUserPtr.className,
|
||||
objectId: x.ExtUserPtr?.objectId,
|
||||
},
|
||||
Placeholders: placeholders.map(y =>
|
||||
y?.signerPtr?.objectId
|
||||
? {
|
||||
...y,
|
||||
signerPtr: {
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Contactbook',
|
||||
objectId: y.signerPtr.objectId,
|
||||
},
|
||||
signerObjId: y.signerObjId,
|
||||
email: y?.signerPtr?.Email || y?.email || '',
|
||||
}
|
||||
: { ...y, signerPtr: {}, signerObjId: '', email: y.email || '' }
|
||||
),
|
||||
SignedUrl: x.URL || x.SignedUrl,
|
||||
SentToOthers: true,
|
||||
Signers: allSigner?.map(y => ({
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Contactbook',
|
||||
objectId: y.objectId,
|
||||
})),
|
||||
ACL: Acl,
|
||||
SentToOthers: true,
|
||||
RemindOnceInEvery: x.RemindOnceInEvery ? parseInt(x.RemindOnceInEvery) : 5,
|
||||
AutomaticReminders: x.AutomaticReminders || false,
|
||||
TimeToCompleteDays: x.TimeToCompleteDays ? parseInt(x.TimeToCompleteDays) : 15,
|
||||
OriginIp: Ip,
|
||||
DocSentAt: { __type: 'Date', iso: isoDate },
|
||||
IsEnableOTP: x?.IsEnableOTP || false,
|
||||
IsTourEnabled: x?.IsTourEnabled || false,
|
||||
AllowModifications: x?.AllowModifications || false,
|
||||
...(x?.SignatureType ? { SignatureType: x?.SignatureType } : {}),
|
||||
...(x?.NotifyOnSignatures ? { NotifyOnSignatures: x?.NotifyOnSignatures } : {}),
|
||||
...(x?.Bcc?.length > 0 ? { Bcc: x?.Bcc } : {}),
|
||||
...(x?.RedirectUrl ? { RedirectUrl: x?.RedirectUrl } : {}),
|
||||
...(mailBody ? { RequestBody: mailBody } : {}),
|
||||
...(mailSubject ? { RequestSubject: mailSubject } : {}),
|
||||
...(x?.objectId
|
||||
? {
|
||||
TemplateId: {
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Template',
|
||||
objectId: x?.objectId,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
...(x?.PenColors?.length > 0 ? { PenColors: x?.PenColors } : {}),
|
||||
},
|
||||
};
|
||||
});
|
||||
if (requests?.length > 0) {
|
||||
const newrequests = [requests?.[0]];
|
||||
const response = await axios.post('batch', { requests: newrequests }, parseConfig);
|
||||
// Handle the batch query response
|
||||
// console.log('Batch query response:', response.data);
|
||||
if (response.data && response.data.length > 0) {
|
||||
const document = Documents?.[0];
|
||||
const updateDocuments = {
|
||||
...document,
|
||||
objectId: response.data[0]?.success?.objectId,
|
||||
createdAt: response.data[0]?.success?.createdAt,
|
||||
};
|
||||
deductcount(response.data.length, resExt.id);
|
||||
sendMail(updateDocuments, publicUrl); //sessionToken
|
||||
return 'success';
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
const code = error?.response?.data?.code || error?.response?.status || error?.code || 400;
|
||||
const msg =
|
||||
error?.response?.data?.error ||
|
||||
error?.response?.data ||
|
||||
error?.message ||
|
||||
'Something went wrong.';
|
||||
console.log('Error performing batch query:', code, msg);
|
||||
throw new Parse.Error(code, msg);
|
||||
}
|
||||
} else {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
|
||||
let mailBody = x?.ExtUserPtr?.TenantId?.RequestBody || '';
|
||||
let mailSubject = x?.ExtUserPtr?.TenantId?.RequestSubject || '';
|
||||
return {
|
||||
method: 'POST',
|
||||
path: '/app/classes/contracts_Document',
|
||||
body: {
|
||||
Name: x.Name,
|
||||
URL: x.URL,
|
||||
Note: x.Note,
|
||||
Description: x.Description,
|
||||
CreatedBy: x.CreatedBy,
|
||||
SendinOrder: x.SendinOrder || true,
|
||||
ExtUserPtr: {
|
||||
__type: 'Pointer',
|
||||
className: x.ExtUserPtr.className,
|
||||
objectId: x.ExtUserPtr?.objectId,
|
||||
},
|
||||
Placeholders: placeholders.map(y =>
|
||||
y?.signerPtr?.objectId
|
||||
? {
|
||||
...y,
|
||||
signerPtr: {
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Contactbook',
|
||||
objectId: y.signerPtr.objectId,
|
||||
},
|
||||
signerObjId: y.signerObjId,
|
||||
email: y?.signerPtr?.Email || y?.email || '',
|
||||
}
|
||||
: { ...y, signerPtr: {}, signerObjId: '', email: y.email || '' }
|
||||
),
|
||||
SignedUrl: x.URL || x.SignedUrl,
|
||||
SentToOthers: true,
|
||||
Signers: allSigner?.map(y => ({
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Contactbook',
|
||||
objectId: y.objectId,
|
||||
})),
|
||||
ACL: Acl,
|
||||
SentToOthers: true,
|
||||
RemindOnceInEvery: x.RemindOnceInEvery ? parseInt(x.RemindOnceInEvery) : 5,
|
||||
AutomaticReminders: x.AutomaticReminders || false,
|
||||
TimeToCompleteDays: x.TimeToCompleteDays ? parseInt(x.TimeToCompleteDays) : 15,
|
||||
OriginIp: Ip,
|
||||
DocSentAt: { __type: 'Date', iso: isoDate },
|
||||
IsEnableOTP: x?.IsEnableOTP || false,
|
||||
IsTourEnabled: x?.IsTourEnabled || false,
|
||||
AllowModifications: x?.AllowModifications || false,
|
||||
...(x?.SignatureType ? { SignatureType: x?.SignatureType } : {}),
|
||||
...(x?.NotifyOnSignatures ? { NotifyOnSignatures: x?.NotifyOnSignatures } : {}),
|
||||
...(x?.Bcc?.length > 0 ? { Bcc: x?.Bcc } : {}),
|
||||
...(x?.RedirectUrl ? { RedirectUrl: x?.RedirectUrl } : {}),
|
||||
...(mailBody ? { RequestBody: mailBody } : {}),
|
||||
...(mailSubject ? { RequestSubject: mailSubject } : {}),
|
||||
...(x?.objectId
|
||||
? {
|
||||
TemplateId: {
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Template',
|
||||
objectId: x?.objectId,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
...(x?.PenColors?.length > 0 ? { PenColors: x?.PenColors } : {}),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
if (requests?.length > 0) {
|
||||
const newrequests = [requests?.[0]];
|
||||
const response = await axios.post('batch', { requests: newrequests }, parseConfig);
|
||||
// Handle the batch query response
|
||||
// console.log('Batch query response:', response.data);
|
||||
if (response.data && response.data.length > 0) {
|
||||
const document = Documents?.[0];
|
||||
const updateDocuments = {
|
||||
...document,
|
||||
objectId: response.data[0]?.success?.objectId,
|
||||
createdAt: response.data[0]?.success?.createdAt,
|
||||
};
|
||||
deductcount(response.data.length, resExt.id);
|
||||
sendMail(updateDocuments, publicUrl); //sessionToken
|
||||
return { total: 1, created: 1, failed: 0 };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default async function createBatchDocs(request) {
|
||||
const strDocuments = request.params.Documents;
|
||||
const sessionToken = request.headers?.sessiontoken;
|
||||
@@ -242,13 +318,20 @@ export default async function createBatchDocs(request) {
|
||||
},
|
||||
};
|
||||
try {
|
||||
let userId = '';
|
||||
|
||||
if (request?.user) {
|
||||
return await batchQuery(request.user.id, Documents, Ip, parseConfig, type, publicUrl);
|
||||
} else {
|
||||
userId = request.user.id;
|
||||
// return await batchQuery(request.user.id, Documents, Ip, parseConfig, type, publicUrl);
|
||||
}
|
||||
if (!userId) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
|
||||
}
|
||||
|
||||
// quicksend
|
||||
return await startBulkSendInBackground(userId, Documents, Ip, parseConfig, type, publicUrl);
|
||||
} catch (err) {
|
||||
console.log('err in createbatchdoc', err);
|
||||
console.log('createbatchdoc error: ', err);
|
||||
const code = err?.code || 400;
|
||||
const msg = err?.message || 'Something went wrong.';
|
||||
throw new Parse.Error(code, msg);
|
||||
|
||||
@@ -14,7 +14,7 @@ async function sendDeclineMail(doc, publicUrl, userId, reason) {
|
||||
const TenantAppName = appName;
|
||||
const logo =
|
||||
"<img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' style='padding:20px'/>";
|
||||
const opurl = ` <a href='mailto:complaint@opensiglabs.com' target=_blank>here</a>`;
|
||||
|
||||
const removePrefill =
|
||||
doc?.Placeholders?.length > 0 && doc?.Placeholders?.filter(x => x?.Role !== 'prefill');
|
||||
const signUser =
|
||||
@@ -36,7 +36,7 @@ async function sendDeclineMail(doc, publicUrl, userId, reason) {
|
||||
`<p>${pdfName} has been declined by ${signerName} "${signerEmail}" on ${new Date().toLocaleDateString()}.</p>` +
|
||||
`<p>Decline Reason: ${reason || 'Not specified'}</p>` +
|
||||
`<p><a href=${viewDocUrl} target=_blank>View Document</a></p></div></div><div><p>This is an automated email from ${TenantAppName}. For any queries regarding this email, ` +
|
||||
`please contact the sender ${creatorEmail} directly. If you think this email is inappropriate or spam, you may file a complaints with ${TenantAppName}${opurl}.</p></div></div></body></html>`;
|
||||
`please contact the sender ${creatorEmail} directly.</p></div></div></body></html>`;
|
||||
|
||||
const params = {
|
||||
extUserId: sender.objectId,
|
||||
|
||||
@@ -1,39 +1,63 @@
|
||||
import AWS from 'aws-sdk';
|
||||
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3';
|
||||
import { getSignedUrl as presign } from '@aws-sdk/s3-request-presigner';
|
||||
import { useLocal } from '../../Utils.js';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import dotenv from 'dotenv';
|
||||
import { isAuthenticated } from '../../utils/AuthUtils.js';
|
||||
dotenv.config({ quiet: true });
|
||||
|
||||
export default function getPresignedUrl(url) {
|
||||
function extractKeyFromUrl(url) {
|
||||
// Create a new URL object
|
||||
const parsedUrl = new URL(url);
|
||||
// Get the pathname of the URL
|
||||
const pathname = parsedUrl.pathname; // e.g. /mybucket/path/to/file.pdf (depends on baseUrl style)
|
||||
// Extract the filename from the pathname
|
||||
const filename = pathname.substring(pathname.lastIndexOf('/') + 1);
|
||||
return filename;
|
||||
}
|
||||
|
||||
function makeEndpoint(endpoint) {
|
||||
if (!endpoint) return '';
|
||||
|
||||
if (endpoint.startsWith('http://') || endpoint.startsWith('https://')) {
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
return `https://${endpoint}`;
|
||||
}
|
||||
|
||||
function makeS3Client() {
|
||||
const accessKeyId = process.env.DO_ACCESS_KEY_ID;
|
||||
|
||||
const secretAccessKey = process.env.DO_SECRET_ACCESS_KEY;
|
||||
|
||||
const region = process.env.DO_REGION;
|
||||
|
||||
const endpoint = makeEndpoint(process.env.DO_ENDPOINT);
|
||||
|
||||
return new S3Client({
|
||||
region,
|
||||
endpoint, // endpoint should be Url e.g. https://blr1.digitaloceanspaces.com)
|
||||
credentials: { accessKeyId, secretAccessKey },
|
||||
});
|
||||
}
|
||||
|
||||
export default async function getPresignedUrl(url) {
|
||||
if (url?.includes('files')) {
|
||||
return presignedlocalUrl(url);
|
||||
} else {
|
||||
const credentials = {
|
||||
accessKeyId: process.env.DO_ACCESS_KEY_ID,
|
||||
secretAccessKey: process.env.DO_SECRET_ACCESS_KEY,
|
||||
};
|
||||
AWS.config.update({
|
||||
credentials: credentials,
|
||||
region: process.env.DO_REGION,
|
||||
});
|
||||
const spacesEndpoint = new AWS.Endpoint(process.env.DO_ENDPOINT);
|
||||
const client = makeS3Client();
|
||||
|
||||
const s3 = new AWS.S3({ endpoint: spacesEndpoint, signatureVersion: 'v4' });
|
||||
const bucket = process.env.DO_SPACE;
|
||||
|
||||
// Create a new URL object
|
||||
const parsedUrl = new URL(url);
|
||||
// Get the pathname of the URL
|
||||
const pathname = parsedUrl.pathname;
|
||||
// Extract the filename from the pathname
|
||||
const filename = pathname.substring(pathname.lastIndexOf('/') + 1);
|
||||
const key = extractKeyFromUrl(url);
|
||||
|
||||
const command = new GetObjectCommand({ Bucket: bucket, Key: key });
|
||||
// Expires: 160 seconds
|
||||
const expiresIn = 160;
|
||||
|
||||
// presignedGETURL return presignedUrl with expires time
|
||||
const presignedGETURL = s3.getSignedUrl('getObject', {
|
||||
Bucket: process.env.DO_SPACE,
|
||||
Key: filename, //filename
|
||||
Expires: 160, //time to expire in seconds
|
||||
});
|
||||
const presignedGETURL = await presign(client, command, { expiresIn });
|
||||
return presignedGETURL;
|
||||
}
|
||||
}
|
||||
@@ -68,7 +92,7 @@ export async function getSignedUrl(request) {
|
||||
}
|
||||
}
|
||||
|
||||
const presignedUrl = getPresignedUrl(url);
|
||||
const presignedUrl = await getPresignedUrl(url);
|
||||
return presignedUrl;
|
||||
} else {
|
||||
return url;
|
||||
@@ -85,7 +109,7 @@ export async function getSignedUrl(request) {
|
||||
if (url?.includes('files')) {
|
||||
return presignedlocalUrl(url);
|
||||
} else if (useLocal !== 'true') {
|
||||
const presignedUrl = getPresignedUrl(url);
|
||||
const presignedUrl = await getPresignedUrl(url);
|
||||
return presignedUrl;
|
||||
} else {
|
||||
return url;
|
||||
|
||||
@@ -122,7 +122,7 @@ async function sendNotifyMail(doc, signUser, mailProvider, publicUrl) {
|
||||
const TenantAppName = appName;
|
||||
const logo =
|
||||
"<img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' style='padding:20px'/>";
|
||||
const opurl = ` <a href='mailto:complaint@opensiglabs.com' target=_blank>here</a>`;
|
||||
|
||||
const auditTrailCount = doc?.AuditTrail?.filter(x => x.Activity === 'Signed')?.length || 0;
|
||||
const removePrefill =
|
||||
doc?.Placeholders?.length > 0 && doc?.Placeholders?.filter(x => x?.Role !== 'prefill');
|
||||
@@ -142,7 +142,7 @@ async function sendNotifyMail(doc, signUser, mailProvider, publicUrl) {
|
||||
`<div>${logo}</div><div style='padding:2px;font-family:system-ui;background-color:#47a3ad'><p style='font-size:20px;font-weight:400;color:white;padding-left:20px'>Document signed by ${signerName}</p>` +
|
||||
`</div><div style='padding:20px;font-family:system-ui;font-size:14px'><p>Dear ${creatorName},</p><p>${pdfName} has been signed by ${signerName} "${signerEmail}" successfully</p>` +
|
||||
`<p><a href=${viewDocUrl} target=_blank>View Document</a></p></div></div><div><p>This is an automated email from ${TenantAppName}. For any queries regarding this email, ` +
|
||||
`please contact the sender ${creatorEmail} directly. If you think this email is inappropriate or spam, you may file a complaints with ${TenantAppName}${opurl}.</p></div></div></body></html>`;
|
||||
`please contact the sender ${creatorEmail} directly.</p></div></div></body></html>`;
|
||||
|
||||
const params = {
|
||||
extUserId: sender.objectId,
|
||||
@@ -169,7 +169,7 @@ async function sendCompletedMail(obj) {
|
||||
const TenantAppName = appName;
|
||||
const logo =
|
||||
"<img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' style='padding:20px'/>";
|
||||
const opurl = ` <a href='mailto:complaint@opensiglabs.com' target=_blank>here</a>`;
|
||||
|
||||
let signersMail;
|
||||
if (doc?.Signers?.length > 0) {
|
||||
const isOwnerExistsinSigners = doc?.Signers?.find(x => x.Email === sender.Email);
|
||||
@@ -185,8 +185,7 @@ async function sendCompletedMail(obj) {
|
||||
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body><div style='background-color:#f5f5f5;padding:20px'><div style='background-color:white'>" +
|
||||
`<div>${logo}</div><div style='padding:2px;font-family:system-ui;background-color:#47a3ad'><p style='font-size:20px;font-weight:400;color:white;padding-left:20px'>Document signed successfully</p></div><div>` +
|
||||
`<p style='padding:20px;font-family:system-ui;font-size:14px'>All parties have successfully signed the document <b>"${pdfName}"</b>. Kindly download the document from the attachment.</p>` +
|
||||
`</div></div><div><p>This is an automated email from ${TenantAppName}. For any queries regarding this email, please contact the sender ${sender.Email} directly.` +
|
||||
`If you think this email is inappropriate or spam, you may file a complaints with ${TenantAppName}${opurl}.</p></div></div></body></html>`;
|
||||
`</div></div><div><p>This is an automated email from ${TenantAppName}. For any queries regarding this email, please contact the sender ${sender.Email} directly.</p></div></div></body></html>`;
|
||||
|
||||
if (obj?.isCustomMail) {
|
||||
const tenant = sender?.TenantId;
|
||||
@@ -224,6 +223,7 @@ async function sendCompletedMail(obj) {
|
||||
|
||||
const variables = {
|
||||
document_title: pdfName,
|
||||
note: doc?.Note,
|
||||
sender_name: sender.Name,
|
||||
sender_mail: doc?.SenderMail || sender.Email,
|
||||
sender_phone: sender?.Phone || '',
|
||||
|
||||
@@ -10,6 +10,8 @@ export default function reportJson(id, currentUserId) {
|
||||
'ExtUserPtr.Name',
|
||||
'ExtUserPtr.Email',
|
||||
'ExtUserPtr.DownloadFilenameFormat',
|
||||
'ExtUserPtr.Company',
|
||||
'ExtUserPtr.Phone',
|
||||
'Signers.Name',
|
||||
'Signers.Email',
|
||||
'Signers.Phone',
|
||||
|
||||
@@ -2,10 +2,14 @@ import fs from 'node:fs';
|
||||
import https from 'https';
|
||||
import formData from 'form-data';
|
||||
import Mailgun from 'mailgun.js';
|
||||
import { smtpenable, smtpsecure, updateMailCount } from '../../Utils.js';
|
||||
import { appName, smtpenable, smtpsecure, updateMailCount } from '../../Utils.js';
|
||||
import { createTransport } from 'nodemailer';
|
||||
import axios from 'axios';
|
||||
async function sendMailProvider(req, plan, monthchange) {
|
||||
async function sendMailProvider(req) {
|
||||
const app = appName;
|
||||
const extUserId = req.params?.extUserId || '';
|
||||
const reportMsg = `<p style="font-size: 13px; color:grey; text-align: center;">If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href="mailto:complaints@opensignlabs.com?subject=Spam%20report%20for%20user%20ID%20${extUserId}&body=Hello%20Support%20Team%2C%0D%0A%0D%0AI%E2%80%99m%20reporting%20spam%20activity%20coming%20from%20a%20sender%20using%20your%20platform.%0D%0A%0D%0AThe%20messages%20I%20received%20appear%20unsolicited%20and%20suspicious.%20The%20user%20ID%20associated%20with%20the%20emails%20is%3A%20${extUserId}.%20Please%20investigate%20this%20account%20and%20take%20appropriate%20action%20to%20prevent%20further%20abuse.%0D%0A%0D%0AIf%20you%20need%20additional%20details%2C%20I%E2%80%99m%20happy%20to%20provide%20the%20original%20email%20headers%20or%20screenshots.%0D%0A%0D%0AThank%20you%20for%20looking%20into%20this.%0D%0A%0D%0ABest%20regards%2C%0D%0A%5BYour%20Name%5D">here</a>.</p>`;
|
||||
|
||||
const mailgunApiKey = process.env.MAILGUN_API_KEY;
|
||||
try {
|
||||
let transporterSMTP;
|
||||
@@ -125,7 +129,7 @@ async function sendMailProvider(req, plan, monthchange) {
|
||||
to: req.params.recipient,
|
||||
subject: req.params.subject,
|
||||
text: req.params.text || 'mail',
|
||||
html: req.params.html || '',
|
||||
html: req.params?.html ? req.params.html + reportMsg : '',
|
||||
attachments: smtpenable ? attachment : undefined,
|
||||
attachment: smtpenable ? undefined : attachment,
|
||||
bcc: req.params.bcc ? req.params.bcc : undefined,
|
||||
@@ -135,8 +139,8 @@ async function sendMailProvider(req, plan, monthchange) {
|
||||
const res = await transporterSMTP.sendMail(messageParams);
|
||||
console.log('smtp transporter res: ', res?.response);
|
||||
if (!res.err) {
|
||||
if (req.params?.extUserId) {
|
||||
await updateMailCount(req.params.extUserId, plan, monthchange);
|
||||
if (extUserId) {
|
||||
await updateMailCount(extUserId);
|
||||
}
|
||||
if (fs.existsSync(certificatePath)) {
|
||||
try {
|
||||
@@ -159,8 +163,8 @@ async function sendMailProvider(req, plan, monthchange) {
|
||||
const res = await mailgunClient.messages.create(mailgunDomain, messageParams);
|
||||
console.log('mailgun res: ', res?.status);
|
||||
if (res.status === 200) {
|
||||
if (req.params?.extUserId) {
|
||||
await updateMailCount(req.params.extUserId, plan, monthchange);
|
||||
if (extUserId) {
|
||||
await updateMailCount(extUserId);
|
||||
}
|
||||
if (fs.existsSync(certificatePath)) {
|
||||
try {
|
||||
@@ -219,7 +223,7 @@ async function sendMailProvider(req, plan, monthchange) {
|
||||
to: req.params.recipient,
|
||||
subject: req.params.subject,
|
||||
text: req.params.text || 'mail',
|
||||
html: req.params.html || '',
|
||||
html: req.params?.html ? req.params.html + reportMsg : '',
|
||||
bcc: req.params.bcc ? req.params.bcc : undefined,
|
||||
replyTo: replyto ? replyto : undefined,
|
||||
};
|
||||
@@ -228,8 +232,8 @@ async function sendMailProvider(req, plan, monthchange) {
|
||||
const res = await transporterSMTP.sendMail(messageParams);
|
||||
console.log('smtp transporter res: ', res?.response);
|
||||
if (!res.err) {
|
||||
if (req.params?.extUserId) {
|
||||
await updateMailCount(req.params.extUserId, plan, monthchange);
|
||||
if (extUserId) {
|
||||
await updateMailCount(extUserId);
|
||||
}
|
||||
return { status: 'success' };
|
||||
}
|
||||
@@ -238,8 +242,8 @@ async function sendMailProvider(req, plan, monthchange) {
|
||||
const res = await mailgunClient.messages.create(mailgunDomain, messageParams);
|
||||
console.log('mailgun res: ', res?.status);
|
||||
if (res.status === 200) {
|
||||
if (req.params?.extUserId) {
|
||||
await updateMailCount(req.params.extUserId, plan, monthchange);
|
||||
if (extUserId) {
|
||||
await updateMailCount(extUserId);
|
||||
}
|
||||
return { status: 'success' };
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ export default async function triggerEvent(request) {
|
||||
const docRes = await docQuery.get(docId, { useMasterKey: true });
|
||||
const _docRes = docRes && docRes?.toJSON();
|
||||
const isEnableOTP = docRes?.get('IsEnableOTP') || false;
|
||||
const ipAddress = request.headers['x-real-ip'] || '';
|
||||
|
||||
let userId;
|
||||
if (isEnableOTP) {
|
||||
let userId;
|
||||
if (sessiontoken) {
|
||||
@@ -36,29 +36,44 @@ export default async function triggerEvent(request) {
|
||||
|
||||
if (event === 'viewed' && contactId) {
|
||||
const auditTrail = Array.isArray(_docRes.AuditTrail) ? _docRes.AuditTrail : [];
|
||||
const isUserExist = auditTrail.some(x => x?.UserPtr?.objectId === contactId && x?.ViewedOn);
|
||||
if (!isUserExist) {
|
||||
const contactPtr = {
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Contactbook',
|
||||
objectId: contactId,
|
||||
};
|
||||
const contactPtr = {
|
||||
__type: 'Pointer',
|
||||
className: 'contracts_Contactbook',
|
||||
objectId: contactId,
|
||||
};
|
||||
const date = new Date().toISOString();
|
||||
const newEntry = {
|
||||
UserPtr: contactPtr,
|
||||
SignedUrl: _docRes?.SignedUrl || '',
|
||||
Activity: 'Viewed',
|
||||
ipAddress,
|
||||
ViewedOn: date,
|
||||
};
|
||||
|
||||
const date = new Date().toISOString();
|
||||
const newEntry = {
|
||||
UserPtr: contactPtr,
|
||||
SignedUrl: _docRes?.SignedUrl || '',
|
||||
const existingIndex = auditTrail.findIndex(x => x?.UserPtr?.objectId === contactId);
|
||||
|
||||
let updatedAuditTrail;
|
||||
|
||||
if (existingIndex !== -1) {
|
||||
// update existing entry
|
||||
updatedAuditTrail = [...auditTrail];
|
||||
updatedAuditTrail[existingIndex] = {
|
||||
...updatedAuditTrail[existingIndex],
|
||||
SignedUrl: _docRes?.SignedUrl || updatedAuditTrail[existingIndex]?.SignedUrl || '',
|
||||
Activity: 'Viewed',
|
||||
ipAddress: request.headers['x-real-ip'],
|
||||
ipAddress,
|
||||
ViewedOn: date,
|
||||
};
|
||||
|
||||
// update Audit trail entry
|
||||
const updateDoc = new Parse.Object('contracts_Document');
|
||||
updateDoc.id = docRes.id;
|
||||
updateDoc.set('AuditTrail', [...auditTrail, newEntry]);
|
||||
await updateDoc.save(null, { useMasterKey: true });
|
||||
} else {
|
||||
// add new entry
|
||||
updatedAuditTrail = [...auditTrail, newEntry];
|
||||
}
|
||||
|
||||
// save only once
|
||||
const updateDoc = new Parse.Object('contracts_Document');
|
||||
updateDoc.id = docRes.id;
|
||||
updateDoc.set('AuditTrail', updatedAuditTrail);
|
||||
await updateDoc.save(null, { useMasterKey: true });
|
||||
}
|
||||
|
||||
return { message: 'event called!' };
|
||||
|
||||
Reference in New Issue
Block a user