Merge pull request #1250 from nxglabs/sync-to-public_repo-17512737859

Merge pull request
This commit is contained in:
prafull-opensignlabs
2025-09-08 04:57:25 +00:00
parent 85bc2fbbb0
commit b01d8125d9
176 changed files with 16765 additions and 27633 deletions
@@ -1,45 +1,35 @@
import { useLocal } from '../../Utils.js';
import { handleValidImage, useLocal } from '../../Utils.js';
import getPresignedUrl, { presignedlocalUrl } from './getSignedUrl.js';
async function DocumentAfterFind(request) {
if (request.objects.length === 1) {
if (request.objects) {
const obj = request.objects[0];
if (
useLocal !== 'true'
) {
const SignedUrl = obj?.get('SignedUrl') && obj?.get('SignedUrl');
const Url = obj?.get('URL') && obj?.get('URL');
const certificateUrl = obj.get('CertificateUrl') && obj.get('CertificateUrl');
const SignedUrl = obj?.get('SignedUrl') && obj?.get('SignedUrl');
const Url = obj?.get('URL') && obj?.get('URL');
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);
}
if (SignedUrl) {
obj.set(
'SignedUrl',
getPresignedUrl(
SignedUrl,
)
);
obj.set('SignedUrl', getPresignedUrl(SignedUrl));
}
if (Url) {
obj.set(
'URL',
getPresignedUrl(
Url,
)
);
obj.set('URL', getPresignedUrl(Url));
}
if (certificateUrl) {
obj.set(
'CertificateUrl',
getPresignedUrl(
certificateUrl,
)
);
obj.set('CertificateUrl', getPresignedUrl(certificateUrl));
}
return [obj];
} else if (useLocal == 'true') {
const SignedUrl = obj?.get('SignedUrl') && obj?.get('SignedUrl');
const Url = obj?.get('URL') && obj?.get('URL');
const certificateUrl = obj.get('CertificateUrl') && obj.get('CertificateUrl');
if (isPrefillExist) {
const updatedPlaceHolder = await handleValidImage(Placeholder);
obj.set('Placeholders', updatedPlaceHolder);
}
if (SignedUrl) {
obj.set('SignedUrl', presignedlocalUrl(SignedUrl));
}
@@ -26,7 +26,6 @@ export default async function forwardDoc(request) {
}
const _docRes = docRes?.toJSON();
const docName = _docRes.Name;
const fileAdapterId = _docRes?.FileAdapterId || '';
const extUserId = _docRes?.ExtUserPtr?.objectId;
const TenantAppName = appName;
const from = _docRes?.ExtUserPtr?.Email;
@@ -1,45 +1,35 @@
import { useLocal } from '../../Utils.js';
import { handleValidImage, useLocal } from '../../Utils.js';
import getPresignedUrl, { presignedlocalUrl } from './getSignedUrl.js';
async function TemplateAfterFind(request) {
if (request.objects.length === 1) {
if (request.objects) {
const obj = request.objects[0];
if (
useLocal !== 'true'
) {
const SignedUrl = obj?.get('SignedUrl') && obj?.get('SignedUrl');
const Url = obj?.get('URL') && obj?.get('URL');
const certificateUrl = obj.get('CertificateUrl') && obj.get('CertificateUrl');
const SignedUrl = obj?.get('SignedUrl') && obj?.get('SignedUrl');
const Url = obj?.get('URL') && obj?.get('URL');
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);
}
if (SignedUrl) {
obj.set(
'SignedUrl',
getPresignedUrl(
SignedUrl,
)
);
obj.set('SignedUrl', getPresignedUrl(SignedUrl));
}
if (Url) {
obj.set(
'URL',
getPresignedUrl(
Url,
)
);
obj.set('URL', getPresignedUrl(Url));
}
if (certificateUrl) {
obj.set(
'CertificateUrl',
getPresignedUrl(
certificateUrl,
)
);
obj.set('CertificateUrl', getPresignedUrl(certificateUrl));
}
return [obj];
} else if (useLocal == 'true') {
const SignedUrl = obj?.get('SignedUrl') && obj?.get('SignedUrl');
const Url = obj?.get('URL') && obj?.get('URL');
const certificateUrl = obj.get('CertificateUrl') && obj.get('CertificateUrl');
if (isPrefillExist) {
const updatedPlaceHolder = await handleValidImage(Placeholder);
obj.set('Placeholders', updatedPlaceHolder);
}
if (SignedUrl) {
obj.set('SignedUrl', presignedlocalUrl(SignedUrl));
}
@@ -20,8 +20,10 @@ export default async function createBatchContact(req) {
UserRole: 'contracts_Guest',
TenantId: { __type: 'Pointer', className: 'partners_Tenant', objectId: x.TenantId },
CreatedBy: { __type: 'Pointer', className: '_User', objectId: req.user.id },
Name: x.Name,
Name: x.Name?.trim(),
Email: x.Email?.toLowerCase()?.replace(/\s/g, ''),
Company: x?.Company?.trim(),
JobTitle: x?.JobTitle?.trim(),
IsDeleted: false,
IsImported: true,
...(x?.Phone ? { Phone: `${x?.Phone}` } : {}),
@@ -1,5 +1,7 @@
export default async function editContact(request) {
const { contactId, name, email, phone, tenantId } = request.params;
const company = request.params?.company;
const jobTitle = request.params?.jobTitle;
if (!request?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
@@ -25,6 +27,12 @@ export default async function editContact(request) {
if (phone) {
contactQuery.set('Phone', phone);
}
if (company) {
contactQuery.set('Company', company);
}
if (jobTitle) {
contactQuery.set('JobTitle', jobTitle);
}
contactQuery.set('Email', email?.toLowerCase()?.replace(/\s/g, ''));
contactQuery.set('UserRole', 'contracts_Guest');
contactQuery.set('IsDeleted', false);
@@ -55,7 +63,7 @@ export default async function editContact(request) {
acl.setWriteAccess(createdBy.objectId, true);
contactQuery.setACL(acl);
const res = await contactQuery.save();
const res = await contactQuery.save(null, { useMasterKey: true });
const parseData = JSON.parse(JSON.stringify(res));
return parseData;
}
@@ -76,7 +84,7 @@ export default async function editContact(request) {
acl.setReadAccess(createdBy.objectId, true);
acl.setWriteAccess(createdBy.objectId, true);
contactQuery.setACL(acl);
const res = await contactQuery.save();
const res = await contactQuery.save(null, { useMasterKey: true });
const parseData = JSON.parse(JSON.stringify(res));
return parseData;
}
@@ -7,7 +7,7 @@ import fs from 'node:fs';
import dotenv from 'dotenv';
import GenerateCertificate from './pdf/GenerateCertificate.js';
import { getSecureUrl } from '../../Utils.js';
dotenv.config();
dotenv.config({ quiet: true });
const eSignName = 'OpenSign';
const eSigncontact = 'hello@opensignlabs.com';
@@ -3,6 +3,7 @@ import { cloudServerUrl, serverAppId } from '../../Utils.js';
export default async function getDocument(request) {
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const docId = request.params.docId;
const include = request?.params?.include || '';
const sessiontoken = request?.headers?.sessiontoken || '';
try {
if (docId) {
@@ -17,6 +18,9 @@ export default async function getDocument(request) {
query.include('Placeholders');
query.include('DeclineBy');
query.notEqualTo('IsArchive', true);
if (include) {
query?.include(include);
}
const res = await query.first({ useMasterKey: true });
if (res) {
const IsEnableOTP = res?.get('IsEnableOTP') || false;
@@ -2,27 +2,21 @@ import AWS from 'aws-sdk';
import { useLocal } from '../../Utils.js';
import jwt from 'jsonwebtoken';
import dotenv from 'dotenv';
dotenv.config();
dotenv.config({ quiet: true });
export default function getPresignedUrl(
url,
) {
export default 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,
accessKeyId: process.env.DO_ACCESS_KEY_ID,
secretAccessKey: process.env.DO_SECRET_ACCESS_KEY,
};
AWS.config.update({
credentials: credentials,
region:
process.env.DO_REGION,
region: process.env.DO_REGION,
});
const spacesEndpoint =
new AWS.Endpoint(process.env.DO_ENDPOINT);
const spacesEndpoint = new AWS.Endpoint(process.env.DO_ENDPOINT);
const s3 = new AWS.S3({ endpoint: spacesEndpoint, signatureVersion: 'v4' });
@@ -35,8 +29,7 @@ export default function getPresignedUrl(
// presignedGETURL return presignedUrl with expires time
const presignedGETURL = s3.getSignedUrl('getObject', {
Bucket:
process.env.DO_SPACE,
Bucket: process.env.DO_SPACE,
Key: filename, //filename
Expires: 160, //time to expire in seconds
});
@@ -53,35 +46,22 @@ export async function getSignedUrl(request) {
try {
if (url?.includes('files')) {
return presignedlocalUrl(url);
} else if (
useLocal !== 'true'
) {
} else if (useLocal !== 'true') {
const query = new Parse.Query(docId ? 'contracts_Document' : 'contracts_Template');
query.equalTo('objectId', docId ? docId : templateId);
query.include('ExtUserPtr.TenantId');
query.notEqualTo('IsArchive', true);
const res = await query.first({ useMasterKey: true });
if (res) {
const _resDoc = JSON.parse(JSON.stringify(res));
if (_resDoc?.IsEnableOTP) {
if (!request?.user) {
throw new Parse.Error(
Parse.Error.INVALID_SESSION_TOKEN,
'User is not authenticated.'
);
} else {
const presignedUrl = getPresignedUrl(
url,
);
return presignedUrl;
}
} else {
const presignedUrl = getPresignedUrl(
url,
);
return presignedUrl;
}
if (!res) return url;
const _resDoc = res?.toJSON();
// Ensure user is authenticated if OTP is required
if (_resDoc?.IsEnableOTP && !request?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
const presignedUrl = getPresignedUrl(url);
return presignedUrl;
} else {
return url;
}
@@ -35,8 +35,7 @@ export default async function getSigners(request) {
searchObj.CreatedBy = { __type: 'Pointer', className: '_User', objectId: request?.user?.id };
searchObj.sessionToken = request.user.getSessionToken();
return await getContacts(searchObj);
}
else {
} else {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid session token');
}
} catch (err) {
@@ -1,4 +1,3 @@
async function getTenantByUserId(userId, contactId) {
try {
if (contactId) {
@@ -53,8 +52,7 @@ export default async function getTenant(request) {
if (userId || contactId) {
return await getTenantByUserId(userId, contactId);
}
else {
} else {
return {};
}
}
@@ -36,8 +36,7 @@ async function getUserDetails(request) {
const msg = err?.message || 'Something went wrong.';
throw new Parse.Error(code, msg);
}
}
else {
} else {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
}
@@ -1,4 +1,3 @@
export default async function isUserInContactBook(request) {
try {
if (request.user) {
@@ -6,6 +6,12 @@ const saveRoleContact = async contact => {
if (contact?.Phone) {
contactQuery.set('Phone', contact.Phone);
}
if (contact?.JobTitle) {
contactQuery.set('JobTitle', contact.JobTitle);
}
if (contact?.Company) {
contactQuery.set('Company', contact.Company);
}
contactQuery.set('CreatedBy', contact.CreatedBy);
contactQuery.set('UserId', contact.UserId);
contactQuery.set('UserRole', 'contracts_Guest');
@@ -23,7 +29,7 @@ const saveRoleContact = async contact => {
acl.setReadAccess(contact.UserId.objectId, true);
acl.setWriteAccess(contact.UserId.objectId, true);
contactQuery.setACL(acl);
const contactRes = await contactQuery.save();
const contactRes = await contactQuery.save(null, { useMasterKey: true });
if (contactRes) {
return contactRes;
}
@@ -37,6 +43,8 @@ export default async function linkContactToDoc(req) {
const docId = req.params.docId;
const name = req.params.name;
const phone = req.params.phone;
const jobTitle = req.params.jobTitle;
const company = req.params.company;
try {
if (docId) {
// Execute the query to get the document with the specified 'docId'
@@ -110,6 +118,8 @@ export default async function linkContactToDoc(req) {
UserId: _extUser.UserId,
Name: _extUser.Name,
Email: email,
JobTitle: _extUser?.JobTitle || '',
Company: _extUser?.Company || '',
Phone: _extUser?.Phone ? _extUser.Phone : '',
CreatedBy: _docRes.CreatedBy,
TenantId: _docRes.ExtUserPtr?.TenantId?.objectId,
@@ -163,6 +173,8 @@ export default async function linkContactToDoc(req) {
Name: name,
Email: email,
Phone: phone,
JobTitle: jobTitle,
Company: company,
CreatedBy: _docRes.CreatedBy,
TenantId: _docRes.ExtUserPtr?.TenantId?.objectId,
};
@@ -220,6 +232,8 @@ export default async function linkContactToDoc(req) {
Name: name,
Email: email,
Phone: phone,
JobTitle: jobTitle,
Company: company,
CreatedBy: _docRes.CreatedBy,
TenantId: _docRes.ExtUserPtr?.TenantId?.objectId,
};
@@ -14,6 +14,7 @@ import { pdflibAddPlaceholder } from '@signpdf/placeholder-pdf-lib';
import { Placeholder } from './Placeholder.js';
import { SignPdf } from '@signpdf/signpdf';
import { P12Signer } from '@signpdf/signer-p12';
import { buildDownloadFilename } from '../../../utils/fileUtils.js';
const serverUrl = cloudServerUrl; // process.env.SERVER_URL;
const APPID = serverAppId;
@@ -79,7 +80,10 @@ async function updateDoc(docId, url, userId, ipAddress, data, className, sign) {
const auditTrail = updateAuditTrail.filter(x => x.Activity === 'Signed');
let isCompleted = false;
if (data.Signers && data.Signers.length > 0) {
if (auditTrail.length === data.Placeholders.length) {
//'removePrefill' is used to remove prefill role from placeholders filed then compare length to change status of document
const removePrefill =
data.Placeholders.length > 0 && data.Placeholders.filter(x => x.Role !== 'prefill');
if (auditTrail.length === removePrefill?.length) {
isCompleted = true;
}
} else {
@@ -108,9 +112,11 @@ async function sendNotifyMail(doc, signUser, mailProvider, publicUrl) {
"<img src='https://qikinnovation.ams3.digitaloceanspaces.com/logo.png' height='50' style='padding:20px'/>";
const opurl = ` <a href=www.opensignlabs.com target=_blank>here</a>`;
const auditTrailCount = doc?.AuditTrail?.filter(x => x.Activity === 'Signed')?.length || 0;
const signersCount = doc?.Placeholders?.length;
const remaingsign = signersCount - auditTrailCount;
if (remaingsign > 1 && doc?.NotifyOnSignatures) {
const removePrefill =
doc?.Placeholders?.length > 0 && doc?.Placeholders?.filter(x => x?.Role !== 'prefill');
const signersCount = removePrefill?.length;
const remainingSign = signersCount - auditTrailCount;
if (remainingSign > 1 && doc?.NotifyOnSignatures) {
const sender = doc.ExtUserPtr;
const pdfName = doc.Name;
const creatorName = doc.ExtUserPtr.Name;
@@ -227,6 +233,13 @@ async function sendCompletedMail(obj) {
}
const Bcc = doc?.Bcc?.length > 0 ? doc.Bcc.map(x => x.Email) : [];
const updatedBcc = doc?.SenderMail ? [...Bcc, doc?.SenderMail] : Bcc;
const formatId = doc?.ExtUserPtr?.DownloadFilenameFormat;
const filename = pdfName?.length > 100 ? pdfName?.slice(0, 100) : pdfName;
const docName = buildDownloadFilename(formatId, {
docName: filename,
email: doc?.ExtUserPtr?.Email,
isSigned: true,
});
const params = {
extUserId: sender.objectId,
url: url,
@@ -239,7 +252,7 @@ async function sendCompletedMail(obj) {
mailProvider: obj.mailProvider,
bcc: updatedBcc?.length > 0 ? updatedBcc : '',
certificatePath: `./exports/signed_certificate_${doc.objectId}.pdf`,
filename: obj?.filename,
filename: docName,
};
try {
const res = await axios.post(serverUrl + '/functions/sendmailv3', params, {
@@ -1,15 +1,41 @@
export default function reportJson(id, userId) {
const currentUserId = userId;
export default function reportJson(id, currentUserId) {
const commanKeys = [
'IsSignyourself',
'URL',
'Name',
'Note',
'SignedUrl',
'AuditTrail',
'Folder.Name',
'ExtUserPtr.Name',
'ExtUserPtr.Email',
'ExtUserPtr.DownloadFilenameFormat',
'Signers.Name',
'Signers.Email',
'Signers.Phone',
'Placeholders',
'TemplateId',
'ExpiryDate',
];
const inProgressKeys = [
...commanKeys,
'AuditTrail.UserPtr',
'SendMail',
'RequestBody',
'RequestSubject',
'ExtUserPtr.TenantId.RequestBody',
'ExtUserPtr.TenantId.RequestSubject',
];
const filterKeys = [
'TimeToCompleteDays',
'AllowModifications',
'IsEnableOTP',
'IsTourEnabled',
'NotifyOnSignatures',
'RedirectUrl',
'SendinOrder',
];
const needYourSignKeys = [...commanKeys, 'Signers.UserId'];
switch (id) {
// draft documents report
case 'ByHuevtCFY':
@@ -23,7 +49,7 @@ export default function reportJson(id, userId) {
SignedUrl: { $exists: false },
CreatedBy: { __type: 'Pointer', className: '_User', objectId: currentUserId },
},
keys: [...commanKeys, 'Note', 'Folder.Name', 'IsSignyourself'],
keys: [...commanKeys, ...filterKeys],
};
// Need your sign report
case '4Hhwbp482K':
@@ -44,16 +70,7 @@ export default function reportJson(id, userId) {
},
},
},
keys: [
...commanKeys,
'Note',
'Folder.Name',
'ExtUserPtr.Email',
'Signers.UserId',
'AuditTrail',
'SignedUrl',
'ExpiryDate',
],
keys: [...needYourSignKeys, ...filterKeys],
};
// In progress report
case '1MwEuxLEkF':
@@ -69,21 +86,7 @@ export default function reportJson(id, userId) {
CreatedBy: { __type: 'Pointer', className: '_User', objectId: currentUserId },
ExpiryDate: { $gt: { __type: 'Date', iso: new Date().toISOString() } },
},
keys: [
...commanKeys,
'Note',
'Folder.Name',
'ExtUserPtr.Email',
'AuditTrail',
'AuditTrail.UserPtr',
'ExpiryDate',
'SendMail',
'SignedUrl',
'RequestBody',
'RequestSubject',
'ExtUserPtr.TenantId.RequestBody',
'ExtUserPtr.TenantId.RequestSubject',
],
keys: [...inProgressKeys, ...filterKeys],
};
// completed documents report
case 'kQUoW4hUXz':
@@ -115,17 +118,7 @@ export default function reportJson(id, userId) {
},
],
},
keys: [
...commanKeys,
'Note',
'Folder.Name',
'SignedUrl',
'TimeToCompleteDays',
'IsSignyourself',
'IsCompleted',
'ExpiryDate',
'IsSignyourself',
],
keys: [...commanKeys, ...filterKeys, 'IsCompleted'],
};
// declined documents report
case 'UPr2Fm5WY3':
@@ -137,7 +130,7 @@ export default function reportJson(id, userId) {
IsDeclined: true,
CreatedBy: { __type: 'Pointer', className: '_User', objectId: currentUserId },
},
keys: [...commanKeys, 'Note', 'Folder.Name', 'DeclineReason', 'SignedUrl'],
keys: [...commanKeys, 'DeclineReason'],
};
// Expired Documents report
case 'zNqBHXHsYH':
@@ -152,7 +145,7 @@ export default function reportJson(id, userId) {
ExpiryDate: { $lt: { __type: 'Date', iso: new Date().toISOString() } },
CreatedBy: { __type: 'Pointer', className: '_User', objectId: currentUserId },
},
keys: [...commanKeys, 'Note', 'Folder.Name', 'SignedUrl', 'ExpiryDate'],
keys: [...commanKeys, ...filterKeys],
};
// Recently sent for signatures report show on dashboard
case 'd9k3UfYHBc':
@@ -168,19 +161,7 @@ export default function reportJson(id, userId) {
CreatedBy: { __type: 'Pointer', className: '_User', objectId: currentUserId },
ExpiryDate: { $gt: { __type: 'Date', iso: new Date().toISOString() } },
},
keys: [
...commanKeys,
'Folder.Name',
'ExtUserPtr.Email',
'AuditTrail',
'AuditTrail.UserPtr',
'ExpiryDate',
'SignedUrl',
'RequestBody',
'RequestSubject',
'ExtUserPtr.TenantId.RequestBody',
'ExtUserPtr.TenantId.RequestSubject',
],
keys: inProgressKeys,
};
// Recent signature requests report show on dashboard
case '5Go51Q7T8r':
@@ -201,14 +182,7 @@ export default function reportJson(id, userId) {
},
},
},
keys: [
...commanKeys,
'ExtUserPtr.Email',
'Signers.UserId',
'AuditTrail',
'SignedUrl',
'ExpiryDate',
],
keys: needYourSignKeys,
};
// Drafts report show on dashboard
case 'kC5mfynCi4':
@@ -222,7 +196,7 @@ export default function reportJson(id, userId) {
SignedUrl: { $exists: false },
CreatedBy: { __type: 'Pointer', className: '_User', objectId: currentUserId },
},
keys: [...commanKeys, 'Note', 'Folder.Name'],
keys: commanKeys,
};
// contact book report
case 'contacts':
@@ -233,7 +207,7 @@ export default function reportJson(id, userId) {
CreatedBy: { __type: 'Pointer', className: '_User', objectId: currentUserId },
IsDeleted: { $ne: true },
},
keys: ['Name', 'Email', 'Phone'],
keys: ['Name', 'Email', 'Phone', 'JobTitle', 'Company'],
};
// Templates report
case '6TeaPr321t':
@@ -243,8 +217,7 @@ export default function reportJson(id, userId) {
params: { Type: { $ne: 'Folder' }, IsArchive: { $ne: true } },
keys: [
...commanKeys,
'Note',
'Folder.Name',
...filterKeys,
'IsPublic',
'SharedWith.Name',
'SendinOrder',
@@ -32,7 +32,6 @@ export default async function saveAsTemplate(request) {
templateCls.set('EmailSenderName', _docRes?.EmailSenderName);
templateCls.set('SenderName', _docRes?.SenderName);
templateCls.set('SenderMail', _docRes?.SenderMail);
templateCls.set('FileAdapterId', _docRes?.FileAdapterId);
templateCls.set('RequestBody', _docRes?.RequestBody);
templateCls.set('RequestSubject', _docRes?.RequestSubject);
templateCls.set('NextReminderDate', _docRes?.NextReminderDate);
@@ -56,6 +55,7 @@ export default async function saveAsTemplate(request) {
...pageItem,
pos: pageItem.pos.map(p => ({
...p,
type: p.type === 'text' ? 'text input' : p.type,
options: {
...p.options,
status: 'required',
@@ -74,14 +74,14 @@ export default async function saveAsTemplate(request) {
};
templateCls.set('Placeholders', [placeHolders]);
} else {
const placeHolders = _docRes?.Placeholders.map((signer, signerIndex) => ({
const removePrefill = _docRes?.Placeholders?.filter(x => x.Role !== 'prefill');
const placeHolders = removePrefill.map((signer, signerIndex) => ({
// copy everything else, then overwrite these fields:
...signer,
signerObjId: '',
signerPtr: {},
Role: `Role ${signerIndex + 1}`,
Role: signer?.Role ? signer.Role : `Role ${signerIndex + 1}`,
email: '',
// rebuild placeHolder/pages
placeHolder: (signer.placeHolder || []).map(page => ({
...page,
@@ -1,7 +1,4 @@
import {
flattenPdf,
getSecureUrl,
} from '../../Utils.js';
import { flattenPdf, getSecureUrl } from '../../Utils.js';
export default async function saveFile(request) {
if (!request.params.fileBase64) {
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide file.');
@@ -16,29 +13,28 @@ export default async function saveFile(request) {
const resExt = await extCls.first({ useMasterKey: true });
if (resExt) {
const _resExt = JSON.parse(JSON.stringify(resExt));
const fileName = request.params.fileName;
const ext = request.params.fileName?.split('.')?.pop();
let mimeType;
let file;
if (ext === 'pdf') {
mimeType = 'application/pdf';
const flatPdf = await flattenPdf(fileBase64);
file = [...flatPdf];
} else if (ext === 'png' || ext === 'jpeg' || ext === 'jpg') {
mimeType = `image/${ext}`;
file = { base64: fileBase64 };
}
const pdfFile = new Parse.File(fileName, file, mimeType);
// Save the Parse File if needed
const pdfData = await pdfFile.save({ useMasterKey: true });
const presignedUrl = pdfData.url();
const fileRes = getSecureUrl(presignedUrl);
return { url: fileRes.url };
const fileName = request.params.fileName;
const ext = request.params.fileName?.split('.')?.pop();
let mimeType;
let file;
if (ext === 'pdf') {
mimeType = 'application/pdf';
const flatPdf = await flattenPdf(fileBase64);
file = [...flatPdf];
} else if (ext === 'png' || ext === 'jpeg' || ext === 'jpg') {
mimeType = `image/${ext}`;
file = { base64: fileBase64 };
}
const pdfFile = new Parse.File(fileName, file, mimeType);
// Save the Parse File if needed
const pdfData = await pdfFile.save({ useMasterKey: true });
const presignedUrl = pdfData.url();
const fileRes = getSecureUrl(presignedUrl);
return { url: fileRes.url };
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
}
}
else {
} else {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
} catch (err) {
@@ -4,6 +4,8 @@ export default async function savecontact(request) {
const requestemail = request.params?.email;
const email = requestemail?.toLowerCase()?.replace(/\s/g, '');
const tenantId = request.params.tenantId;
const company = request.params?.company;
const jobTitle = request.params?.jobTitle;
if (request.user) {
const currentUser = request?.user;
@@ -16,12 +18,18 @@ export default async function savecontact(request) {
if (!res) {
const contactQuery = new Parse.Object('contracts_Contactbook');
contactQuery.set('Name', name);
if (phone) {
contactQuery.set('Phone', phone);
}
contactQuery.set('Email', email);
contactQuery.set('UserRole', 'contracts_Guest');
contactQuery.set('IsDeleted', false);
if (phone) {
contactQuery.set('Phone', phone);
}
if (company) {
contactQuery.set('Company', company);
}
if (jobTitle) {
contactQuery.set('JobTitle', jobTitle);
}
if (tenantId) {
contactQuery.set('TenantId', {
__type: 'Pointer',
@@ -29,7 +37,6 @@ export default async function savecontact(request) {
objectId: tenantId,
});
}
try {
const _users = Parse.Object.extend('User');
const _user = new _users();
@@ -40,7 +47,6 @@ export default async function savecontact(request) {
if (phone) {
_user.set('phone', phone);
}
const user = await _user.save();
if (user) {
contactQuery.set('CreatedBy', currentUserPtr);
@@ -52,7 +58,7 @@ export default async function savecontact(request) {
acl.setWriteAccess(currentUser.id, true);
contactQuery.setACL(acl);
const res = await contactQuery.save();
const res = await contactQuery.save(null, { useMasterKey: true });
const parseData = JSON.parse(JSON.stringify(res));
return parseData;
}
@@ -73,7 +79,7 @@ export default async function savecontact(request) {
acl.setReadAccess(currentUser.id, true);
acl.setWriteAccess(currentUser.id, true);
contactQuery.setACL(acl);
const res = await contactQuery.save();
const res = await contactQuery.save(null, { useMasterKey: true });
const parseData = JSON.parse(JSON.stringify(res));
return parseData;
}
@@ -0,0 +1,103 @@
import { appName, smtpenable } from '../../Utils.js';
export const errHtml = err => {
return `<html><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8" /><title>Reset Password</title></head>
<body><h1 style="color:#1a5fa0; margin-bottom:16px;">${err}</h1></body></html>`;
};
const sendDeleteUserMail = async req => {
const app = req.params.app || appName;
if (!req.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
try {
const { userId } = req.params;
if (!userId) {
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Missing userId parameter.');
}
const userPointer = { __type: 'Pointer', className: '_User', objectId: userId };
const createdByPointer = { __type: 'Pointer', className: '_User', objectId: req.user.id };
const userCondition = new Parse.Query('contracts_Users');
userCondition.equalTo('UserId', userPointer);
const userAndCreatorCondition = new Parse.Query('contracts_Users');
userAndCreatorCondition.equalTo('UserId', userPointer);
userAndCreatorCondition.equalTo('CreatedBy', createdByPointer);
const mainQuery = Parse.Query.or(userCondition, userAndCreatorCondition);
const result = await mainQuery.first({ useMasterKey: true });
const username = result.get('Email')?.toLowerCase()?.replace(/\s/g, '');
const name = result?.get('Name') ? `<b>${result?.get('Name')}</b>` : '';
const isAdmin = result?.get('UserRole') === 'contracts_Admin';
if (!isAdmin) {
throw new Parse.Error(
Parse.Error.SCRIPT_FAILED,
'This action is not permitted. Kindly contact your administrator to request account deletion.'
);
}
const serverUrl = process.env?.SERVER_URL?.replace(/\/app\/?$/, '/');
const deleteUrl = `${serverUrl}delete-account/${userId}`;
const mailsender = smtpenable ? process.env.SMTP_USER_EMAIL : process.env.MAILGUN_SENDER;
// Render a simple HTML form. In production, consider using a templating engine.
await Parse.Cloud.sendEmail({
sender: app + ' <' + mailsender + '>',
recipient: username,
subject: `Account Deletion Request for ${username} ${app}`,
text: `Account Deletion Request for ${username} ${app}`,
html: `<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Account Deletion Request - ${app}</title>
</head>
<body style="margin:0; padding:0; font-family:Arial, sans-serif; background-color:#f4f4f4; color:#333;">
<div
style="max-width:600px; margin:50px auto; padding:30px; background-color:#ffffff; border:1px solid #e0e0e0; border-radius:8px;">
<h2 style="color:#d9534f;">Request to Delete Your Account</h2>
<p style="font-size:16px; line-height:1.5;">
Hello ${name},
</p>
<p style="font-size:16px; line-height:1.5;">
We have received a request to permanently delete your <b>${app}</b> account associated with <b>${username}</b>.
</p>
<p style="font-size:16px; line-height:1.5;">
If you did not make this request, please ignore this email. Otherwise, click the button below to proceed
with the deletion.
</p>
<p style="text-align:center; margin:30px 0;">
<a href="${deleteUrl}"
style="background-color:#d9534f; color:#ffffff; padding:12px 24px; border-radius:5px; text-decoration:none; font-size:16px;">
Confirm Account Deletion
</a>
</p>
<p style="font-size:16px; line-height:1.5;">
If the button above doesn't work, please copy and open the following link with your browser.
</p>
<p style="font-size:16px; text-align:center; margin:0px 0px 30px 0px;">
<a href="${deleteUrl}">${deleteUrl}</a>
</p>
<p style="font-size:14px; color:#777;">
Note: This action is irreversible and all your data will be permanently removed from our systems.
</p>
<hr style="margin:30px 0; border:none; border-top:1px solid #eee;">
<p style="font-size:12px; color:#999;">
If you have any questions or need assistance, please contact our support team.
</p>
<p style="font-size:12px; color:#999;">
&copy; ${new Date().getFullYear()} ${app}. All rights reserved.
</p>
</div>
</body>
</html>`,
});
return 'mail sent.';
} catch (err) {
console.log('Err in sending delete user email ', err);
throw new Parse.Error(Parse.Error.SCRIPT_FAILED, err.message);
}
};
export default sendDeleteUserMail;
@@ -57,6 +57,9 @@ export default async function updatePreferences(request) {
if (request.params.IsLTVEnabled !== undefined) {
newOrg.set('IsLTVEnabled', request.params.IsLTVEnabled);
}
if (request.params.DownloadFilenameFormat) {
newOrg.set('DownloadFilenameFormat', request.params.DownloadFilenameFormat);
}
const updateUserRes = await newOrg.save(null, { useMasterKey: true });
if (updateUserRes) {
const _updateUserRes = JSON.parse(JSON.stringify(updateUserRes));
@@ -15,8 +15,7 @@ export default async function updateTourStatus(request) {
const msg = err?.message || 'Something went wrong.';
throw new Parse.Error(code, msg);
}
}
else {
} else {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
}