feat: create duplicate template functionality
feat: add support of csv and xlsx for bulk contacts import
feat: add info text for forms and dashboard button
feat: implement functionality to delete a PDF page while editing the document
feat: show completed documents in which signers included but not owner in completed reports
feat: add delete folder button in opensign drive
feat: introduce Bcc email support for sending completed documents
feat: allow merging multiple pdf while drafting document and template
feat: add 'My Initials' tab for auto-signing in request-sign flow
feat: add support for redirect URL to navigate after document completion
feat: introduce privacy policy and digital signature terms before signing documents.
feat: add feature to allow adding new pages to existing document
feat: add save signature, initials, stamp functionality in sign pad for logged in user
feat: add preferences menu
feat: add save custom email template, notifyonsignature, set timezone, allow signature types in preferences
feat: secure local url
feat: implement Italian language translation
feat: implement German language translation
feat: update menu name from report to documents and shift contactbook in main menu
feat: provide edit contact functionality

fix: unable to delete folder when all its documents are deleted
fix: fields.push is not function
fix: document loading issue in opensign drive
fix: adjust the guest signature flow to display the document in full screen, eliminating any blank space which is displayed below the place holder in mobile view
fix: resolve issue of instance of pdfdict or pdfstream but got undefined

build(deps): update dependencies

refactor: change note text from add contact form
This commit is contained in:
prafull-opensignlabs
2025-02-10 14:41:16 +00:00
parent b55aff20d3
commit e299891174
260 changed files with 22438 additions and 25112 deletions
@@ -1,35 +0,0 @@
The OpenSign Enterprise license (the “Enterprise License”)
Copyright (c) 2020-2024 Qik Innovations Private Limited.
With regard to the OpenSign Software:
This software and associated documentation files (the "Software") may only be
used in production, if you (and any entity that you represent) have agreed to,
and are in compliance with, the OpenSign Subscription Terms of Service(the “Enterprise Terms”), or other
agreement governing the use of the Software, as agreed by you and OpenSign,
and otherwise have a valid OpenSign Enterprise license for the
correct number of user seats. Subject to the foregoing sentence, you are free to
modify this Software and publish patches to the Software. You agree that OpenSign
and/or its licensors (as applicable) retain all right, title and interest in and
to all such modifications and/or patches, and all such modifications and/or
patches may only be used, copied, modified, displayed, distributed, or otherwise
exploited with a valid OpenSign Enterprise license for the correct
number of user seats. Notwithstanding the foregoing, you may copy and modify
the Software for development and testing purposes, without requiring a
subscription. You agree that OpenSign and/or its licensors (as applicable) retain
all right, title and interest in and to all such modifications. You are not
granted any other rights beyond what is expressly stated herein. Subject to the
foregoing, it is forbidden to copy, merge, publish, distribute, sublicense,
and/or sell the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
For all third party components incorporated into the OpenSign Software, those
components are licensed under the original license provided by the owner of the
applicable component.
@@ -1,192 +0,0 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
// `replaceMailVaribles` is used to replace variables from mail with there actual values
function replaceMailVaribles(subject, body, variables) {
let replacedSubject = subject;
let replacedBody = body;
for (const variable in variables) {
const regex = new RegExp(`{{${variable}}}`, 'g');
if (subject) {
replacedSubject = replacedSubject.replace(regex, variables[variable]);
}
if (body) {
replacedBody = replacedBody.replace(regex, variables[variable]);
}
}
const result = {
subject: replacedSubject,
body: replacedBody,
};
return result;
}
// `sendmail` is used to signing reminder mail to signer
async function sendMail(doc, signer) {
const subject = `{{sender_name}} has requested you to sign "{{document_title}}"`;
const body = `<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body><p>Hi {{receiver_name}},</p><br><p>We hope this email finds you well. {{sender_name}}&nbsp;has requested you to review and sign&nbsp;<b>"{{document_title}}"</b>.</p><p>Your signature is crucial to proceed with the next steps as it signifies your agreement and authorization.</p><br><p>{{signing_url}}</p><br><p>If you have any questions or need further clarification regarding the document or the signing process, please contact the sender.</p><br><p>Thanks</p><p> Team OpenSign™</p><br></body> </html>`;
const url = `${cloudServerUrl}/functions/sendmailv3`;
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': process.env.APP_ID,
};
const baseUrl = new URL(process.env.PUBLIC_URL);
const encodeBase64 = btoa(`${doc.objectId}/${signer.Email}/${signer.objectId}`);
const expireDate = doc?.ExpiryDate?.iso;
const newDate = new Date(expireDate);
const localExpireDate = newDate.toLocaleDateString('en-US', {
day: 'numeric',
month: 'long',
year: 'numeric',
});
const signPdf = `${baseUrl.origin}/login/${encodeBase64}`;
const variables = {
document_title: doc.Name,
sender_name: doc.ExtUserPtr.Name,
sender_mail: doc.ExtUserPtr.Email,
sender_phone: doc.ExtUserPtr?.Phone || '',
receiver_name: signer.Name,
receiver_email: signer.Email,
receiver_phone: signer?.Phone || '',
expiry_date: localExpireDate,
company_name: doc?.ExtUserPtr?.Company || '',
signing_url: `<a href=${signPdf} target=_blank>Sign here</a>`,
};
const mail = replaceMailVaribles(subject, body, variables);
let params = {
mailProvider: doc?.ExtUserPtr?.active_mail_adapter,
extUserId: doc?.ExtUserPtr?.objectId,
recipient: signer.Email,
subject: mail.subject,
from: doc?.ExtUserPtr?.Email,
html: mail.body,
};
try {
// The axios request is used to send a signing reminder email.
const res = await axios.post(url, params, { headers: headers });
// console.log('res ', res.data.result);
if (res.data.result.status === 'success') {
return { status: 'success' };
}
} catch (err) {
console.log('err in mail of sendreminder api', err);
return { status: 'error' };
}
}
export default async function autoReminder(request, response) {
// The query below is used to find documents where the reminder date is less than or equal to the current date, and which have existing signers and a signed URL.
try {
const docQuery = new Parse.Query('contracts_Document');
docQuery.limit(2000);
docQuery.lessThanOrEqualTo('NextReminderDate', new Date());
docQuery.equalTo('AutomaticReminders', true);
docQuery.exists('NextReminderDate');
docQuery.exists('Signers');
docQuery.exists('SignedUrl');
docQuery.descending('createdAt');
docQuery.include('Signers,AuditTrail.UserPtr,ExtUserPtr,ExtUserPtr.TenantId');
docQuery.notEqualTo('IsCompleted', true);
docQuery.notEqualTo('IsDeclined', true);
docQuery.notEqualTo('IsArchive', true);
docQuery.greaterThanOrEqualTo('ExpiryDate', new Date());
const docsArr = await docQuery.find({ useMasterKey: true });
if (docsArr && docsArr.length > 0) {
const _docsArr = JSON.parse(JSON.stringify(docsArr));
let mailCount = 0;
let docCount = 0;
for (const doc of _docsArr) {
// The reminderDate variable is used to calculate the next reminder date.
const RemindOnceInEvery = doc?.RemindOnceInEvery || 5;
const ReminderDate = new Date();
ReminderDate.setDate(ReminderDate.getDate() + RemindOnceInEvery);
// The sendInOrder variable is used to determine whether to send emails in order or not.
const SendInOrder = doc?.SendinOrder || false;
if (SendInOrder) {
// The auditTrail variable is used to get the count of how many users have already signed the document.
const auditTrail = doc?.AuditTrail?.filter(x => x.Activity === 'Signed');
const count = auditTrail?.length || 0;
const signer = doc?.Signers?.[count];
if (signer) {
docCount += 1;
const mailRes = await sendMail(doc, signer);
if (mailRes && mailRes.status === 'success') {
mailCount += 1;
}
try {
// The code below is used to update the next reminder date of the document based on the "remind once every X days" setting.
const updateDoc = new Parse.Object('contracts_Document');
updateDoc.id = doc.objectId;
updateDoc.set('NextReminderDate', ReminderDate);
const updateRes = await updateDoc.save(null, { useMasterKey: true });
// console.log('updateRes ', updateRes);
} catch (err) {
console.log('err in update document', err);
}
}
} else {
// The AuditTrail variable is used to check if there is any user who has already signed the document.
const auditTrail = doc?.AuditTrail?.filter(x => x.Activity === 'Signed');
if (auditTrail?.length > 0) {
// The signers variable is used to get the signers who haven't signed the document.
const signers = doc?.Signers.filter(signer => {
const signedUser = auditTrail?.find(y => y.UserPtr.objectId === signer.objectId);
if (!signedUser) {
return signer;
}
});
if (signers?.length > 0) {
docCount += 1;
// The for...of loop below is used to send a signing reminder to every signer who hasn't signed the document yet.
for (const signer of signers) {
const mailRes = await sendMail(doc, signer);
if (mailRes && mailRes.status === 'success') {
mailCount += 1;
}
}
}
} else {
// The for...of loop below is used to send a signing reminder to every signer who hasn't signed the document yet.
const signers = doc?.Signers;
if (signers?.length > 0) {
docCount += 1;
for (const signer of signers) {
const mailRes = await sendMail(doc, signer);
if (mailRes && mailRes.status === 'success') {
mailCount += 1;
}
}
}
}
// The code below is used to update the next reminder date of the document based on the "remind once every X days" setting.
try {
const updateDoc = new Parse.Object('contracts_Document');
updateDoc.id = doc.objectId;
updateDoc.set('NextReminderDate', ReminderDate);
const updateRes = await updateDoc.save(null, { useMasterKey: true });
// console.log('updateRes ', updateRes);
} catch (err) {
console.log('err in sendmail', err);
}
}
}
if (docCount > 0) {
response.json({ status: 'success', document_count: docCount, mail_count: mailCount });
} else {
response.json({ status: 'no record found' });
}
} else {
response.json({ status: 'no record found' });
}
} catch (err) {
console.log('err ', err);
const code = err?.code || 400;
const message = err?.message || 'Somehting went wrong!';
response.status(code).json({ error: message });
}
}
@@ -1,14 +1,8 @@
//--npm modules
import express from 'express';
import cors from 'cors';
import dotenv from 'dotenv';
import uploadFile from './uploadFile.js';
import saveSubscription from './saveSubscription.js';
import saveInvoice from './saveInvoice.js';
import savePayments from './savePayments.js';
import gooogleauth from './googleauth.js';
import autoReminder from './autoReminder.js';
import validateSmtp from './validateSmtp.js';
export const app = express();
dotenv.config();
@@ -18,9 +12,3 @@ app.use(express.urlencoded({ limit: '50mb', extended: true }));
app.post('/file_upload', uploadFile);
app.post('/savesubscription', saveSubscription);
app.post('/saveinvoice', saveInvoice);
app.post('/savepayment', savePayments);
app.post('/googleauth', gooogleauth);
app.post('/sendreminder', autoReminder);
app.post('/validatesmtp', validateSmtp);
@@ -1,60 +0,0 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
const appId = process.env.APP_ID;
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
export default async function gooogleauth(request, response) {
const code = request.body.code;
const baseUrl = new URL(process.env.SERVER_URL);
try {
const userRes = await axios.get(serverUrl + '/users/me', {
headers: {
'X-Parse-Application-Id': appId,
'X-Parse-Session-Token': request.headers['sessiontoken'],
},
});
const userId = userRes.data && userRes.data.objectId;
if (userId) {
const clientId = process.env.GOOGLE_CLIENT_ID;
const clientSecret = process.env.GOOGLE_CLIENT_SECRET;
const redirectUri =
baseUrl?.hostname === 'localhost'
? 'http://localhost:3000'
: 'https://console.opensignlabs.com'; // Should match the redirect URI used in the authorization request
const tokenEndpoint = 'https://oauth2.googleapis.com/token';
const params = new URLSearchParams();
params.append('code', code);
params.append('client_id', clientId);
params.append('client_secret', clientSecret);
params.append('redirect_uri', redirectUri);
params.append('grant_type', 'authorization_code');
const res = await axios.post(tokenEndpoint, params);
// console.log('oauthres ', res.data);
const refresh_token = res.data.refresh_token;
const extUserCls = new Parse.Query('contracts_Users');
extUserCls.equalTo('UserId', { __type: 'Pointer', className: '_User', objectId: userId });
const extUser = await extUserCls.first({ useMasterKey: true });
if (extUser) {
const extUserCls = new Parse.Object('contracts_Users');
extUserCls.id = extUser.id;
extUserCls.set('google_refresh_token', refresh_token);
extUserCls.set('active_mail_adapter', 'google');
const updateExtUser = await extUserCls.save(null, { useMasterKey: true });
// console.log('updateExtUser ', updateExtUser);
}
return response.status(200).json({ status: 'success' });
} else {
return response.status(404).json({ message: 'user not found!' });
}
} catch (err) {
console.log('err in google auth', err);
if (err?.response?.data?.error) {
return response.status(404).json({ message: err.response.data.error });
} else {
return response.status(404).json({ message: err.message });
}
}
}
@@ -1,51 +0,0 @@
export default async function saveInvoice(request, response) {
const InvoiceId = request.body.data.invoice.invoice_id;
const body = request.body;
const Email = request.body.data.invoice.email;
try {
const extUserCls = new Parse.Query('contracts_Users');
extUserCls.equalTo('Email', Email);
const extUser = await extUserCls.first({ useMasterKey: true });
if (extUser) {
const invoiceCls = new Parse.Query('contracts_Invoices');
invoiceCls.equalTo('InvoiceId', InvoiceId);
const invoice = await invoiceCls.first({ useMasterKey: true });
if (invoice) {
const updateInvoice = new Parse.Object('contracts_Invoices');
updateInvoice.id = invoice.id;
updateInvoice.set('InvoiceDetails', body);
await updateInvoice.save(null, { useMasterKey: true });
return response.status(200).json({ status: 'update invoice!' });
} else {
const createInvoice = new Parse.Object('contracts_Invoices');
createInvoice.set('InvoiceId', InvoiceId);
createInvoice.set('InvoiceDetails', body);
createInvoice.set('ExtUserPtr', {
__type: 'Pointer',
className: 'contracts_Users',
objectId: extUser.id,
});
createInvoice.set('CreatedBy', {
__type: 'Pointer',
className: '_User',
objectId: extUser.get('UserId').id,
});
if (extUser?.get('TenantId')?.id) {
createInvoice.set('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: extUser.get('TenantId').id,
});
}
await createInvoice.save(null, { useMasterKey: true });
return response.status(200).json({ status: 'create invoice!' });
}
} else {
return response.status(404).json({ status: 'user not found!' });
}
} catch (err) {
console.log('Err in save invoice', err);
return response.status(400).json({ status: 'error:' + err.message });
}
}
@@ -1,50 +0,0 @@
export default async function savePayments(request, response) {
const PaymentId = request.body.data.payment.payment_id;
const body = request.body;
const Email = request.body.data.payment.email;
try {
const extUserCls = new Parse.Query('contracts_Users');
extUserCls.equalTo('Email', Email);
const extUser = await extUserCls.first({ useMasterKey: true });
if (extUser) {
const paymentCls = new Parse.Query('contracts_Payments');
paymentCls.equalTo('PaymentId', PaymentId);
const payment = await paymentCls.first({ useMasterKey: true });
if (payment) {
const updatePayment = new Parse.Object('contracts_Payments');
updatePayment.id = payment.id;
updatePayment.set('PaymentDetails', body);
await updatePayment.save(null, { useMasterKey: true });
return response.status(200).json({ status: 'update Payment!' });
} else {
const createPayment = new Parse.Object('contracts_Payments');
createPayment.set('PaymentId', PaymentId);
createPayment.set('PaymentDetails', body);
createPayment.set('ExtUserPtr', {
__type: 'Pointer',
className: 'contracts_Users',
objectId: extUser.id,
});
createPayment.set('CreatedBy', {
__type: 'Pointer',
className: '_User',
objectId: extUser.get('UserId').id,
});
if (extUser?.get('TenantId')?.id) {
createPayment.set('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: extUser.get('TenantId').id,
});
}
await createPayment.save(null, { useMasterKey: true });
return response.status(200).json({ status: 'create payments!' });
}
} else {
return response.status(404).json({ status: 'user not found!' });
}
} catch (err) {
console.log('Err in save payment', err);
return response.status(400).json({ status: 'error:' + err.message });
}
}
@@ -1,183 +0,0 @@
import { planCredits } from '../../Utils.js';
export default async function saveSubscription(request, response) {
const SubscriptionId = request.body?.data?.subscription?.subscription_id;
const body = request.body;
const Email = request.body.data?.subscription?.customer?.email;
const Next_billing_date = request.body?.data?.subscription?.next_billing_at;
const planCode = request.body?.data?.subscription?.plan?.plan_code;
const addons = request.body?.data?.subscription?.addons || [];
const event = request.body?.data?.event_type || '';
const credits = planCredits?.[planCode] || 0;
const isTeamPlan = planCode?.includes('team');
let newAddons = 0;
if (addons?.length > 0) {
let allowedUsersMonthly = 0;
let allowedUsersYearly = 0;
addons?.forEach(item => {
if (item.addon_code === 'extra-teams-users-monthly') {
allowedUsersMonthly += item.quantity;
} else if (item.addon_code === 'extra-teams-users-yearly') {
allowedUsersYearly += item.quantity;
} else if (item.addon_code === 'extra-users') {
allowedUsersMonthly += item.quantity;
}
});
if (allowedUsersMonthly > 0 || allowedUsersYearly > 0) {
newAddons = allowedUsersMonthly + allowedUsersYearly + 1;
}
} else {
if (planCode === 'teams-yearly' || planCode === 'teams-monthly' || planCode === 'team-weekly') {
newAddons = 1;
}
}
try {
const extUserCls = new Parse.Query('contracts_Users');
extUserCls.equalTo('Email', Email);
const extUser = await extUserCls.first({ useMasterKey: true });
if (extUser) {
const subcriptionCls = new Parse.Query('contracts_Subscriptions');
subcriptionCls.equalTo('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: extUser.get('TenantId').id,
});
const subscription = await subcriptionCls.first({ useMasterKey: true });
if (subscription) {
const _resSub = JSON.parse(JSON.stringify(subscription));
const updateSubscription = new Parse.Object('contracts_Subscriptions');
updateSubscription.id = subscription.id;
updateSubscription.set('SubscriptionId', SubscriptionId);
updateSubscription.set('SubscriptionDetails', body);
if (Next_billing_date) {
updateSubscription.set('Next_billing_date', new Date(Next_billing_date));
} else {
updateSubscription.unset('Next_billing_date');
}
updateSubscription.set('PlanCode', planCode);
if (newAddons > 0) {
updateSubscription.set('AllowedUsers', parseInt(newAddons));
}
let existAddon = 0;
let allowedUsersMonthly = 0;
let allowedUsersYearly = 0;
_resSub.SubscriptionDetails?.data?.subscription?.addons?.forEach(item => {
if (item.addon_code === 'extra-teams-users-monthly') {
allowedUsersMonthly += item.quantity;
} else if (item.addon_code === 'extra-teams-users-yearly') {
allowedUsersYearly += item.quantity;
} else if (item.addon_code === 'extra-users') {
allowedUsersMonthly += item.quantity;
}
});
if (allowedUsersMonthly > 0 || allowedUsersYearly > 0) {
existAddon = allowedUsersMonthly + allowedUsersYearly + 1; // + 1 is Admin user
} else {
if (
planCode === 'teams-yearly' ||
planCode === 'teams-monthly' ||
planCode === 'team-weekly'
) {
existAddon = 1; // 1 is Admin user
}
}
const isSameAsPrevPlan = subscription?.get('PlanCode') === planCode;
if (isSameAsPrevPlan) {
const planCredits = subscription?.get('PlanCredits');
const existAllowedCredits = subscription?.get('AllowedCredits') || 0;
if (planCredits) {
const oldAddons = existAddon;
const substractedAddon = newAddons - oldAddons;
if (isTeamPlan && substractedAddon > 0) {
const newCredits = existAllowedCredits + substractedAddon * planCredits;
updateSubscription.set('AllowedCredits', newCredits);
if (event === 'subscription_created') {
updateSubscription.set('PlanCredits', credits);
}
} else if (isTeamPlan) {
const existCredits = existAddon * planCredits;
updateSubscription.set('AllowedCredits', existCredits);
if (event === 'subscription_created') {
updateSubscription.set('PlanCredits', credits);
}
} else {
updateSubscription.set('AllowedCredits', planCredits);
if (event === 'subscription_created') {
updateSubscription.set('PlanCredits', credits);
}
}
} else {
if (credits > 0) {
if (isTeamPlan) {
const newCredits = newAddons * credits;
updateSubscription.set('AllowedCredits', newCredits);
updateSubscription.set('PlanCredits', credits);
} else {
updateSubscription.set('AllowedCredits', credits);
updateSubscription.set('PlanCredits', credits);
}
}
}
} else {
if (credits > 0) {
if (isTeamPlan) {
const newCredits = newAddons * credits;
updateSubscription.set('AllowedCredits', newCredits);
updateSubscription.set('PlanCredits', credits);
} else {
updateSubscription.set('AllowedCredits', credits);
updateSubscription.set('PlanCredits', credits);
}
}
}
await updateSubscription.save(null, { useMasterKey: true });
return response.status(200).json({ status: 'update subscription!' });
} else {
const createSubscription = new Parse.Object('contracts_Subscriptions');
createSubscription.set('SubscriptionId', SubscriptionId);
createSubscription.set('SubscriptionDetails', body);
createSubscription.set('ExtUserPtr', {
__type: 'Pointer',
className: 'contracts_Users',
objectId: extUser.id,
});
createSubscription.set('CreatedBy', {
__type: 'Pointer',
className: '_User',
objectId: extUser.get('UserId').id,
});
createSubscription.set('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: extUser.get('TenantId').id,
});
if (Next_billing_date) {
createSubscription.set('Next_billing_date', new Date(Next_billing_date));
} else {
createSubscription.unset('Next_billing_date');
}
createSubscription.set('PlanCode', planCode);
if (newAddons > 0) {
createSubscription.set('AllowedUsers', parseInt(newAddons));
}
if (credits > 0) {
if (isTeamPlan) {
const totalCredits = parseInt(newAddons) * credits;
createSubscription.set('AllowedCredits', totalCredits);
createSubscription.set('PlanCredits', credits);
} else {
createSubscription.set('AllowedCredits', credits);
createSubscription.set('PlanCredits', credits);
}
}
await createSubscription.save(null, { useMasterKey: true });
return response.status(200).json({ status: 'create subscription!' });
}
} else {
return response.status(404).json({ status: 'user not found!' });
}
} catch (err) {
console.log('Err in save subscription', err);
return response.status(400).json({ status: 'error:' + err.message });
}
}
@@ -1,119 +0,0 @@
//--npm modules
import express from 'express';
import cors from 'cors';
export const app = express();
import dotenv from 'dotenv';
import getUser from './routes/getUser.js';
import getDocumentList from './routes/getDocumentList.js';
import getDocument from './routes/getDocument.js';
import getContact from './routes/getContact.js';
import deleteContact from './routes/deleteContact.js';
import getContactList from './routes/getContactList.js';
import getTemplate from './routes/getTemplate.js';
import deletedTemplate from './routes/deleteTemplate.js';
import getTemplatetList from './routes/getTemplateList.js';
import updateTemplate from './routes/updateTemplate.js';
import createContact from './routes/createContact.js';
import multer from 'multer';
import updateDocument from './routes/updateDocument.js';
import deleteDocument from './routes/deleteDocument.js';
import createDocumentWithTemplate from './routes/CreateDocumentWithTemplate.js';
import saveWebhook from './routes/saveWebhook.js';
import deleteWebhook from './routes/deleteWebhook.js';
import getWebhook from './routes/getWebhook.js';
import createDocumentwithCoordinate from './routes/createDocumentwithCoordinate.js';
import createTemplatewithCoordinate from './routes/createTemplatewithCoordinate.js';
import resendMail from './routes/resendMail.js';
import getFolder from './routes/getFolder.js';
import createFolder from './routes/createFolder.js';
import updateFolder from './routes/updateFolder.js';
import getFolderList from './routes/getFolderList.js';
import deleteFolder from './routes/deleteFolder.js';
dotenv.config();
const storage = multer.memoryStorage();
const upload = multer({ storage: storage });
app.use(cors());
app.use(express.json({ limit: '50mb' }));
app.use(express.urlencoded({ limit: '50mb', extended: true }));
// get user details whose api token used
app.get('/getuser', getUser);
// get contact on the basis of id
app.post('/createcontact', createContact);
// get contact on the basis of id
app.get('/contact/:contact_id', getContact);
// soft delete contact
app.delete('/contact/:contact_id', deleteContact);
// get list of contacts
app.get('/contactlist', getContactList);
// create Document
app.post('/createdocumentwithbinary', upload.array('file', 1), createDocumentwithCoordinate);
// create Document with co-ordinate
app.post('/createdocument', createDocumentwithCoordinate);
// create Document with templateId
app.post('/createdocument/:template_id', createDocumentWithTemplate);
// get Document on the basis of id
app.get('/document/:document_id', getDocument);
// get document on the basis of id
app.put('/document/:document_id', updateDocument);
// get document on the basis of id
app.delete('/document/:document_id', deleteDocument);
// get all types of documents on the basis of doctype
app.get('/documentlist/:doctype', getDocumentList);
// create Template with co-ordinate
app.post('/createtemplate', createTemplatewithCoordinate);
// create Template with binary
app.post('/createtemplatewithbinary', upload.array('file', 1), createTemplatewithCoordinate);
// get template on the basis of id
app.get('/template/:template_id', getTemplate);
// get template on the basis of id
app.put('/template/:template_id', updateTemplate);
// get template on the basis of id
app.delete('/template/:template_id', deletedTemplate);
// get all types of documents on the basis of doctype
app.get('/templatelist', getTemplatetList);
// set and update webhook
app.get('/webhook', getWebhook);
// set and update webhook
app.post('/webhook', saveWebhook);
// set and update webhook
app.delete('/webhook', deleteWebhook);
// resend mail
app.post('/resendmail', resendMail);
// create folder
app.post('/createfolder', createFolder);
// update folder
app.put('/folder/:folder_id', updateFolder);
// get folder list
app.delete('/folder/:folder_id', deleteFolder);
// get folder
app.get('/folder/:folder_id', getFolder);
// get folder list
app.get('/folderlist', getFolderList);
@@ -1,435 +0,0 @@
import axios from 'axios';
import { cloudServerUrl, customAPIurl, replaceMailVaribles } from '../../../../Utils.js';
// `sendDoctoWebhook` is used to send res data of document on webhook
async function sendDoctoWebhook(doc, WebhookUrl, userId) {
if (WebhookUrl) {
const params = { event: 'created', ...doc };
await axios
.post(WebhookUrl, params, { headers: { 'Content-Type': 'application/json' } })
.then(res => {
try {
// console.log('res ', res);
const webhook = new Parse.Object('contracts_Webhook');
webhook.set('Log', res?.status);
webhook.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
webhook.save(null, { useMasterKey: true });
} catch (err) {
console.log('err save in contracts_Webhook', err);
}
})
.catch(err => {
console.log('Err send data to webhook', err);
try {
const webhook = new Parse.Object('contracts_Webhook');
webhook.set('Log', err?.status);
webhook.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
webhook.save(null, { useMasterKey: true });
} catch (err) {
console.log('err save in contracts_Webhook', err);
}
});
// console.log('res ', res.data);
}
}
export default async function createDocumentWithTemplate(request, response) {
const signers = request.body.signers;
const folderId = request.body.folderId;
const templateId = request.params.template_id;
const protocol = customAPIurl();
const baseUrl = new URL(process.env.PUBLIC_URL);
const send_email = request.body.send_email;
const email_subject = request.body.email_subject;
const email_body = request.body.email_body;
const sendInOrder = request.body.sendInOrder || true;
const TimeToCompleteDays = request.body.timeToCompleteDays || 15;
try {
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const extUsers = new Parse.Query('contracts_Users');
extUsers.equalTo('UserId', userPtr);
const extUser = await extUsers.first({ useMasterKey: true });
const subscription = new Parse.Query('contracts_Subscriptions');
subscription.equalTo('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: extUser.get('TenantId').id,
});
subscription.include('ExtUserPtr');
subscription.greaterThanOrEqualTo('Next_billing_date', new Date());
const resSub = await subscription.first({ useMasterKey: true });
if (resSub) {
const _resSub = JSON.parse(JSON.stringify(resSub));
const allowedCredits = _resSub?.AllowedCredits || 0;
const addonCredits = _resSub?.AddonCredits || 0;
const totalcredits = allowedCredits + addonCredits;
if (totalcredits > 0) {
const templateQuery = new Parse.Query('contracts_Template');
templateQuery.include('ExtUserPtr');
templateQuery.include('ExtUserPtr.TenantId');
const templateRes = await templateQuery.get(templateId, { useMasterKey: true });
if (templateRes) {
const template = JSON.parse(JSON.stringify(templateRes));
if (template?.Placeholders?.length > 0) {
const emptyplaceholder = template?.Placeholders.filter(x => !x.signerObjId);
const isValid =
signers.length >= emptyplaceholder.length &&
signers.length <= template?.Placeholders?.length;
const placeholder =
signers.length > emptyplaceholder.length ? template.Placeholders : emptyplaceholder;
const updateSigners = placeholder.every(y => signers?.some(x => x.role === y.Role));
// console.log('isValid ', isValid);
if (isValid && updateSigners) {
//Check if every item's placeholders contain at least one placeholder with type 'signature'.
let isSignature = template?.Placeholders?.every(item =>
item?.placeHolder.some(x => x?.pos.some(data => data?.type === 'signature'))
);
if (!isSignature) {
return response
.status(400)
.json({ error: 'Please add at least one signature widget for all signers' });
}
const folderPtr = {
__type: 'Pointer',
className: 'contracts_Document',
objectId: folderId,
};
const object = new Parse.Object('contracts_Document');
object.set('Name', template.Name);
if (template?.Note) {
object.set('Note', template.Note);
}
if (template?.Description) {
object.set('Description', template.Description);
}
object.set('IsSendMail', send_email);
if (sendInOrder) {
object.set('SendinOrder', sendInOrder);
} else if (template?.SendinOrder && template?.SendinOrder) {
object.set('SendinOrder', template?.SendinOrder);
}
let templateSigner = template?.Signers ? template?.Signers : [];
let contact = [];
if (signers && signers.length > 0) {
let parseSigners = [...signers];
let createContactUrl = protocol + '/v1/createcontact';
for (const obj of parseSigners) {
const body = {
name: obj?.name || '',
email: obj?.email || '',
phone: obj?.phone || '',
};
try {
const res = await axios.post(createContactUrl, body, {
headers: { 'Content-Type': 'application/json', 'x-api-token': reqToken },
});
const contactPtr = {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: res.data?.objectId,
};
const newObj = { ...obj, contactPtr: contactPtr };
contact.push(newObj);
} catch (err) {
// console.log('err ', err);
if (err?.response?.data?.objectId) {
const contactPtr = {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: err.response.data?.objectId,
};
const newObj = { ...obj, contactPtr: contactPtr };
contact.push(newObj);
} else {
console.log('err ', err);
}
}
}
const contactPtrs = contact.map(x => x.contactPtr);
object.set('Signers', [...templateSigner, ...contactPtrs]);
let updatedPlaceholder = template?.Placeholders?.map(x => {
let matchingSigner = contact.find(y => x.Role && x.Role === y.role);
if (matchingSigner) {
return {
...x,
signerObjId: matchingSigner?.contactPtr?.objectId,
signerPtr: matchingSigner?.contactPtr,
};
} else {
return { ...x };
}
});
object.set('Placeholders', updatedPlaceholder);
} else {
object.set('Signers', templateSigner);
}
object.set('URL', template.URL);
object.set('SignedUrl', template.URL);
object.set('SentToOthers', true);
if (TimeToCompleteDays) {
object.set('TimeToCompleteDays', TimeToCompleteDays);
}
const enableOTP = request.body?.enableOTP;
const IsEnableOTP =
enableOTP !== undefined ? enableOTP : template?.IsEnableOTP || false;
const enableTour = request.body?.enableTour;
const isTourEnabled =
enableTour !== undefined ? enableTour : template?.IsTourEnabled || false;
object.set('IsTourEnabled', isTourEnabled);
object.set('IsEnableOTP', IsEnableOTP);
object.set('CreatedBy', template.CreatedBy);
object.set('ExtUserPtr', {
__type: 'Pointer',
className: 'contracts_Users',
objectId: template.ExtUserPtr.objectId,
});
if (folderId) {
object.set('Folder', folderPtr);
}
if (template?.FileAdapterId) {
object.set('FileAdapterId', template?.FileAdapterId);
}
const newACL = new Parse.ACL();
newACL.setPublicReadAccess(false);
newACL.setPublicWriteAccess(false);
newACL.setReadAccess(userPtr.objectId, true);
newACL.setWriteAccess(userPtr.objectId, true);
object.setACL(newACL);
const res = await object.save(null, { useMasterKey: true });
const newDate = new Date();
newDate.setDate(newDate.getDate() + 15);
const localExpireDate = newDate.toLocaleDateString('en-US', {
day: 'numeric',
month: 'long',
year: 'numeric',
});
let sender = template.ExtUserPtr.Email;
let sendMail;
if (send_email === false) {
console.log("don't send mail");
} else {
let contactMail = contact;
if (sendInOrder) {
contactMail = contact.slice();
contactMail.splice(1);
}
for (let i = 0; i < contactMail.length; i++) {
try {
const imgPng = 'https://qikinnovation.ams3.digitaloceanspaces.com/logo.png';
let url = `${cloudServerUrl}/functions/sendmailv3/`;
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Master-Key': process.env.MASTER_KEY,
};
const objectId = contactMail[i].contactPtr.objectId;
const hostUrl = baseUrl.origin;
//encode this url value `${res.id}/${contactMail[i].email}/${objectId}` to base64 using `btoa` function
const encodeBase64 = btoa(`${res.id}/${contactMail[i].email}/${objectId}`);
let signPdf = `${hostUrl}/login/${encodeBase64}`;
const openSignUrl = 'https://www.opensignlabs.com/contact-us';
const orgName = template.ExtUserPtr.Company
? template.ExtUserPtr.Company
: '';
const themeBGcolor = '#47a3ad';
const email_html =
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /> </head> <body> <div style='background-color: #f5f5f5; padding: 20px'> <div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background: white;padding-bottom: 20px;'> <div style='padding:10px 10px 0 10px'><img src='" +
imgPng +
"' height='50' style='padding:20px; width:170px; height:40px;' /></div><div style='padding: 2px;font-family: system-ui;background-color:" +
themeBGcolor +
";'><p style='font-size: 20px;font-weight: 400;color: white;padding-left: 20px;' > Digital Signature Request</p></div><div><p style='padding: 20px;font-family: system-ui;font-size: 14px; margin-bottom: 10px;'> " +
template.ExtUserPtr.Name +
' has requested you to review and sign <strong> ' +
template.Name +
"</strong>.</p><div style='padding: 5px 0px 5px 25px;display: flex;flex-direction: row;justify-content: space-around;'><table> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Sender</td> <td> </td> <td style='color:#626363;font-weight:bold'>" +
sender +
"</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Organization</td> <td> </td><td style='color:#626363;font-weight:bold'> " +
orgName +
"</td></tr> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expires on</td><td> </td> <td style='color:#626363;font-weight:bold'>" +
localExpireDate +
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a target=_blank href=" +
signPdf +
"> <button style='padding: 12px 12px 12px 12px;background-color: #d46b0f;color: white; border: 0px;box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;font-weight:bold;margin-top:30px;'>Sign here</button></a> </div> <div style='display: flex; justify-content: center;margin-top: 10px;'> </div></div></div><div><p> This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " +
sender +
' directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href=' +
openSignUrl +
' target=_blank>here</a>.</p> </div></div></body> </html>';
let replaceVar;
const variables = {
document_title: template.Name,
sender_name: template.ExtUserPtr.Name,
sender_mail: template.ExtUserPtr.Email,
sender_phone: template.ExtUserPtr?.Phone || '',
receiver_name: contactMail[i].name,
receiver_email: contactMail[i].email,
receiver_phone: contactMail[i]?.phone || '',
expiry_date: localExpireDate,
company_name: orgName,
signing_url: signPdf,
};
if (email_subject && email_body) {
replaceVar = replaceMailVaribles(email_subject, email_body, variables);
} else if (email_subject) {
replaceVar = replaceMailVaribles(email_subject, '', variables);
replaceVar = { subject: replaceVar.subject, body: email_html };
} else if (email_body) {
replaceVar = replaceMailVaribles(
`${template.ExtUserPtr.Name} has requested you to sign "${template.Name}"`,
email_body,
variables
);
} else {
replaceVar = {
subject: `${template.ExtUserPtr.Name} has requested you to sign "${template.Name}"`,
body: email_html,
};
}
const subject = replaceVar.subject;
const html = replaceVar.body;
let params = {
recipient: contactMail[i].email,
subject: subject,
from: sender,
html: html,
extUserId: template.ExtUserPtr.objectId,
mailProvider: template?.ExtUserPtr?.active_mail_adapter || '',
};
sendMail = await axios.post(url, params, { headers: headers });
} catch (error) {
console.log('error', error);
}
}
}
// if (sendMail.data.result.status === 'success') {
try {
const doc = {
objectId: res.id,
file: template?.URL,
name: template?.Name,
note: template?.Note || '',
description: template?.Description || '',
signers: contact?.map(x => ({
name: x.name,
email: x.email,
phone: x?.phone || '',
})),
createdAt: res.createdAt,
};
if (template.ExtUserPtr && template.ExtUserPtr?.Webhook) {
sendDoctoWebhook(doc, template.ExtUserPtr?.Webhook, userPtr?.objectId);
}
} catch (err) {
console.log('Err', err);
}
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_document_with_templateid',
properties: { response_code: 200 },
});
}
const subscriptionCls = new Parse.Object('contracts_Subscriptions');
subscriptionCls.id = resSub.id;
if (allowedCredits > 0) {
const updateAllowedcredits = allowedCredits - 1 || 0;
subscriptionCls.set('AllowedCredits', updateAllowedcredits);
} else {
const updateAddonCredits = addonCredits > 0 ? addonCredits - 1 : 0;
subscriptionCls.set('AddonCredits', updateAddonCredits);
}
const resSubcription = await subscriptionCls.save(null, { useMasterKey: true });
// console.log("resSubcription ", resSubcription)
return response.json({
objectId: res.id,
signurl: contact.map(x => ({
email: x.email,
url: `${baseUrl.origin}/login/${btoa(
`${res.id}/${x.email}/${x.contactPtr.objectId}`
)}`,
})),
message: 'Document sent successfully!',
});
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_document_with_templateid',
properties: { response_code: 400 },
});
}
return response.status(400).json({ error: 'Please provide signers properly!' });
}
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_document_with_templateid',
properties: { response_code: 400 },
});
}
return response.status(400).json({ error: 'Please setup template properly!' });
}
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_document_with_templateid',
properties: { response_code: 404 },
});
}
return response.status(404).json({ error: 'Invalid template id!' });
}
} else {
return response
.status(429)
.json({ error: 'Quota reached, Please buy credits and try again later.' });
}
} else {
return response.status(400).json({
error: 'Please purchase or renew your subscription.',
});
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
if (err.code === 101) {
return response.status(404).json({ error: 'Invalid template id!' });
}
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -1,193 +0,0 @@
export default async function createContact(request, response) {
const name = request.body.name;
const phone = request.body?.phone;
const email = request.body.email;
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
try {
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
try {
const contactbook = new Parse.Query('contracts_Contactbook');
contactbook.equalTo('Email', email);
contactbook.equalTo('CreatedBy', userPtr);
contactbook.notEqualTo('IsDeleted', true);
const userExists = await contactbook.first({ useMasterKey: true });
if (userExists) {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_contact',
properties: { response_code: 401 },
});
}
return response
.status(401)
.json({ error: 'Contact already exists!', objectId: userExists.id });
} else {
try {
const Tenant = new Parse.Query('partners_Tenant');
Tenant.equalTo('UserId', userPtr);
const tenantRes = await Tenant.first({ useMasterKey: true });
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');
if (tenantRes && tenantRes.id) {
contactQuery.set('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: tenantRes.id,
});
}
try {
const _users = Parse.Object.extend('User');
const _user = new _users();
_user.set('name', name);
_user.set('username', email);
_user.set('email', email);
_user.set('password', email);
if (phone) {
_user.set('phone', phone);
}
const user = await _user.save();
if (user) {
const currentUser = userPtr;
contactQuery.set('CreatedBy', currentUser);
contactQuery.set('UserId', user);
const acl = new Parse.ACL();
acl.setReadAccess(userPtr.objectId, true);
acl.setWriteAccess(userPtr.objectId, true);
acl.setReadAccess(user.id, true);
acl.setWriteAccess(user.id, true);
contactQuery.setACL(acl);
const contactRes = await contactQuery.save();
const parseRes = JSON.parse(JSON.stringify(contactRes));
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_contact',
properties: { response_code: 200 },
});
}
return response.json({
objectId: parseRes.objectId,
name: parseRes.Name,
email: parseRes.Email,
phone: parseRes?.Phone || '',
createdAt: parseRes.createdAt,
updatedAt: parseRes.updatedAt,
});
}
} catch (err) {
console.log('err in', err);
if (err.code === 202) {
const params = { email: email };
const userRes = await Parse.Cloud.run('getUserId', params);
contactQuery.set('CreatedBy', userPtr);
contactQuery.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: userRes.id,
});
const acl = new Parse.ACL();
acl.setReadAccess(userPtr.objectId, true);
acl.setWriteAccess(userPtr.objectId, true);
acl.setReadAccess(userRes.id, true);
acl.setWriteAccess(userRes.id, true);
contactQuery.setACL(acl);
const contactRes = await contactQuery.save();
if (contactRes) {
const parseRes = JSON.parse(JSON.stringify(contactRes));
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_contact',
properties: { response_code: 200 },
});
}
return response.json({
objectId: parseRes.objectId,
name: parseRes.Name,
email: parseRes.Email,
phone: parseRes?.Phone || '',
createdAt: parseRes.createdAt,
updatedAt: parseRes.updatedAt,
});
}
} else {
if (err.code === 137) {
return response.status(401).json({ error: 'Contact already exists!' });
}
return response
.status(400)
.json({ error: 'Something went wrong, please try again later!' });
}
}
} catch (err) {
console.log('err ', err);
if (err.code === 137) {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_contact',
properties: { response_code: 401 },
});
}
return response.status(401).json({ error: 'Contact already exists!' });
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_contact',
properties: { response_code: 400 },
});
}
return response
.status(400)
.json({ error: 'Something went wrong, please try again later!' });
}
}
}
} catch (err) {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_contact',
properties: { response_code: 400 },
});
}
return response
.status(400)
.json({ error: 'Something went wrong, please try again later!' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('Err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -1,454 +0,0 @@
import axios from 'axios';
import {
color,
customAPIurl,
replaceMailVaribles,
saveFileUsage,
formatWidgetOptions,
sanitizeFileName,
cloudServerUrl,
} from '../../../../Utils.js';
import uploadFileToS3 from '../../../parsefunction/uploadFiletoS3.js';
// `sendDoctoWebhook` is used to send res data of document on webhook
async function sendDoctoWebhook(doc, WebhookUrl, userId) {
if (WebhookUrl) {
const params = { event: 'created', ...doc };
await axios
.post(WebhookUrl, params, { headers: { 'Content-Type': 'application/json' } })
.then(res => {
try {
// console.log('res ', res);
const webhook = new Parse.Object('contracts_Webhook');
webhook.set('Log', res?.status);
webhook.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
webhook.save(null, { useMasterKey: true });
} catch (err) {
console.log('err save in contracts_Webhook', err);
}
})
.catch(err => {
console.log('Err send data to webhook', err?.message);
try {
const webhook = new Parse.Object('contracts_Webhook');
webhook.set('Log', err?.status);
webhook.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
webhook.save(null, { useMasterKey: true });
} catch (err) {
console.log('err save in contracts_Webhook', err?.message);
}
});
// console.log('res ', res.data);
}
}
const randomId = () => Math.floor(1000 + Math.random() * 9000);
export default async function createDocumentwithCoordinate(request, response) {
const name = request.body.title;
const note = request.body.note;
const description = request.body.description;
const send_email = request.body.send_email;
const signers = request.body.signers;
const folderId = request.body.folderId;
const base64File = request.body.file;
const fileData = request.files?.[0] ? request.files[0].buffer : null;
const email_subject = request.body.email_subject;
const email_body = request.body.email_body;
const sendInOrder = request.body.sendInOrder || true;
const TimeToCompleteDays = request.body.timeToCompleteDays || 15;
const IsEnableOTP = request.body?.enableOTP === true ? true : false;
const isTourEnabled = request.body?.enableTour || false;
// console.log('fileData ', fileData);
const protocol = customAPIurl();
const baseUrl = new URL(process.env.PUBLIC_URL);
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
try {
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const extUsers = new Parse.Query('contracts_Users');
extUsers.equalTo('UserId', userPtr);
extUsers.include('TenantId');
const extUser = await extUsers.first({ useMasterKey: true });
const subscription = new Parse.Query('contracts_Subscriptions');
subscription.equalTo('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: extUser.get('TenantId').id,
});
subscription.include('ExtUserPtr');
subscription.greaterThanOrEqualTo('Next_billing_date', new Date());
const resSub = await subscription.first({ useMasterKey: true });
if (resSub) {
const _resSub = JSON.parse(JSON.stringify(resSub));
const allowedCredits = _resSub?.AllowedCredits || 0;
const addonCredits = _resSub?.AddonCredits || 0;
const totalcredits = allowedCredits + addonCredits;
if (totalcredits > 0) {
if (signers && signers.length > 0) {
// Check if at least one signature exists among all items in the signers array
let isSignExist = signers.every(item =>
item.widgets.some(data => data?.type === 'signature')
);
if (!isSignExist) {
return response
.status(400)
.json({ error: 'Please add at least one signature widget for all signers' });
}
const parseExtUser = JSON.parse(JSON.stringify(extUser));
let fileUrl;
if (request.files?.[0]) {
const base64 = fileData?.toString('base64');
const file = new Parse.File(request.files?.[0]?.originalname, {
base64: base64,
});
await file.save({ useMasterKey: true });
fileUrl = file.url();
const buffer = Buffer.from(base64, 'base64');
saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId);
} else {
const filename = sanitizeFileName(`${name}.pdf`);
let adapter = {};
const ActiveFileAdapter = parseExtUser?.TenantId?.ActiveFileAdapter || '';
if (ActiveFileAdapter) {
adapter =
parseExtUser?.TenantId?.FileAdapters?.find(x => (x.id = ActiveFileAdapter)) || {};
}
if (adapter?.id) {
const filedata = Buffer.from(base64File, 'base64');
// `uploadFileToS3` is used to save document in user's file storage
fileUrl = await uploadFileToS3(filedata, filename, 'application/pdf', adapter);
} else {
const file = new Parse.File(filename, { base64: base64File }, 'application/pdf');
await file.save({ useMasterKey: true });
fileUrl = file.url();
}
const buffer = Buffer.from(base64File, 'base64');
saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId);
}
const extUserPtr = {
__type: 'Pointer',
className: 'contracts_Users',
objectId: extUser.id,
};
const object = new Parse.Object('contracts_Document');
object.set('Name', name);
if (note) {
object.set('Note', note);
}
if (description) {
object.set('Description', description);
}
if (sendInOrder) {
object.set('SendinOrder', sendInOrder);
}
object.set('URL', fileUrl);
object.set('SignedUrl', fileUrl);
object.set('SentToOthers', true);
object.set('CreatedBy', userPtr);
object.set('ExtUserPtr', extUserPtr);
if (TimeToCompleteDays) {
object.set('TimeToCompleteDays', TimeToCompleteDays);
}
object.set('IsEnableOTP', IsEnableOTP);
object.set('IsTourEnabled', isTourEnabled);
object.set('IsSendMail', send_email);
if (parseExtUser?.TenantId?.ActiveFileAdapter) {
object.set('FileAdapterId', parseExtUser?.TenantId?.ActiveFileAdapter);
}
let contact = [];
if (signers && signers.length > 0) {
let parseSigners;
if (base64File) {
parseSigners = signers;
} else {
parseSigners = JSON.parse(signers);
}
let createContactUrl = protocol + '/v1/createcontact';
for (const [index, element] of parseSigners.entries()) {
const body = {
name: element?.name || '',
email: element?.email || '',
phone: element?.phone || '',
};
try {
const res = await axios.post(createContactUrl, body, {
headers: { 'Content-Type': 'application/json', 'x-api-token': reqToken },
});
// console.log('res ', res.data);
const contactPtr = {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: res.data?.objectId,
};
const newObj = { ...element, contactPtr: contactPtr, index: index };
contact.push(newObj);
} catch (err) {
// console.log('err ', err.response);
if (err?.response?.data?.objectId) {
const contactPtr = {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: err.response.data?.objectId,
};
const newObj = { ...element, contactPtr: contactPtr, index: index };
contact.push(newObj);
}
}
}
object.set(
'Signers',
contact?.map(x => x.contactPtr)
);
let updatePlaceholders = contact.map(signer => {
const placeHolder = [];
for (const widget of signer.widgets) {
const pageNumber = widget.page;
const options = formatWidgetOptions(widget.type, widget.options);
const page = placeHolder.find(page => page.pageNumber === pageNumber);
const widgetData = {
isStamp: widget.type === 'stamp' || widget.type === 'image',
key: randomId(),
isDrag: false,
scale: 1,
isMobile: false,
zIndex: 1,
type: widget.type === 'textbox' ? 'text input' : widget.type,
options: options,
Width: widget.w,
Height: widget.h,
xPosition: widget.x,
yPosition: widget.y,
};
if (page) {
page.pos.push(widgetData);
} else {
placeHolder.push({ pageNumber, pos: [widgetData] });
}
}
return {
signerObjId: signer?.contactPtr?.objectId,
signerPtr: signer?.contactPtr,
Role: signer.role,
Id: randomId(),
blockColor: color[signer?.index],
placeHolder,
};
});
object.set('Placeholders', updatePlaceholders);
}
if (folderId) {
object.set('Folder', {
__type: 'Pointer',
className: 'contracts_Document',
objectId: folderId,
});
}
const newACL = new Parse.ACL();
newACL.setPublicReadAccess(false);
newACL.setPublicWriteAccess(false);
newACL.setReadAccess(userPtr.objectId, true);
newACL.setWriteAccess(userPtr.objectId, true);
object.setACL(newACL);
const res = await object.save(null, { useMasterKey: true });
const doc = {
objectId: res.id,
file: fileUrl,
name: name,
note: note || '',
description: description || '',
signers: contact?.map(x => ({ name: x.name, email: x.email, phone: x?.phone || '' })),
createdAt: res.createdAt,
};
if (parseExtUser && parseExtUser.Webhook) {
sendDoctoWebhook(doc, parseExtUser?.Webhook, userPtr?.objectId);
}
const newDate = new Date();
newDate.setDate(newDate.getDate() + 15);
const localExpireDate = newDate.toLocaleDateString('en-US', {
day: 'numeric',
month: 'long',
year: 'numeric',
});
let sender = parseExtUser.Email;
if (send_email === false) {
console.log("don't send mail");
} else {
let contactMail = contact;
if (sendInOrder) {
contactMail = contact.slice();
contactMail.splice(1);
}
for (let i = 0; i < contactMail.length; i++) {
try {
const imgPng = 'https://qikinnovation.ams3.digitaloceanspaces.com/logo.png';
let url = `${cloudServerUrl}/functions/sendmailv3/`;
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Master-Key': process.env.MASTER_KEY,
};
const objectId = contactMail[i].contactPtr.objectId;
const hostUrl = baseUrl.origin;
//encode this url value `${response.id}/${contactMail[i].email}/${objectId}` to base64 using `btoa` function
const encodeBase64 = btoa(`${res.id}/${contactMail[i].email}/${objectId}`);
let signPdf = `${hostUrl}/login/${encodeBase64}`;
const openSignUrl = 'https://www.opensignlabs.com/contact-us';
const orgName = parseExtUser.Company ? parseExtUser.Company : '';
const themeBGcolor = '#47a3ad';
const email_html =
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /> </head> <body> <div style='background-color: #f5f5f5; padding: 20px'> <div style=' box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background: white;padding-bottom: 20px;'> <div style='padding:10px 10px 0 10px'><img src=" +
imgPng +
" height='50' style='padding:20px; width:170px; height:40px;' /></div> <div style='padding:2px; font-family: system-ui;background-color:" +
themeBGcolor +
";'><p style='font-size: 20px;font-weight: 400;color: white;padding-left: 20px;' > Digital Signature Request</p></div><div><p style='padding: 20px;font-family: system-ui;font-size: 14px; margin-bottom: 10px;'> " +
parseExtUser.Name +
' has requested you to review and sign <strong> ' +
name +
"</strong>.</p><div style='padding: 5px 0px 5px 25px;display: flex;flex-direction: row;justify-content: space-around;'><table> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Sender</td> <td> </td> <td style='color:#626363;font-weight:bold'>" +
sender +
"</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Organization</td> <td> </td><td style='color:#626363;font-weight:bold'> " +
orgName +
"</td></tr> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expires on</td><td> </td> <td style='color:#626363;font-weight:bold'>" +
localExpireDate +
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a target=_blank href=" +
signPdf +
"> <button style='padding: 12px 12px 12px 12px;background-color: #d46b0f;color: white; border: 0px;box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;font-weight:bold;margin-top:30px'>Sign here</button></a> </div> <div style='display: flex; justify-content: center;margin-top: 10px;'> </div></div></div><div><p> This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " +
sender +
' directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href=' +
openSignUrl +
' target=_blank>here</a>.</p> </div></div></body> </html>';
let replaceVar;
const variables = {
document_title: name,
sender_name: parseExtUser.Name,
sender_mail: parseExtUser.Email,
sender_phone: parseExtUser?.Phone || '',
receiver_name: contactMail[i].name,
receiver_email: contactMail[i].email,
receiver_phone: contactMail[i]?.phone || '',
expiry_date: localExpireDate,
company_name: orgName,
signing_url: signPdf,
};
if (email_subject && email_body) {
replaceVar = replaceMailVaribles(email_subject, email_body, variables);
} else if (email_subject) {
replaceVar = replaceMailVaribles(email_subject, '', variables);
replaceVar = { subject: replaceVar.subject, body: email_html };
} else if (email_body) {
replaceVar = replaceMailVaribles(
`${parseExtUser.Name} has requested you to sign "${name}"`,
email_body,
variables
);
} else {
replaceVar = {
subject: `${parseExtUser.Name} has requested you to sign "${name}"`,
body: email_html,
};
}
const subject = replaceVar.subject;
const html = replaceVar.body;
let params = {
recipient: contactMail[i].email,
subject: subject,
from: sender,
html: html,
extUserId: extUser.id,
mailProvider: parseExtUser?.active_mail_adapter || '',
};
await axios.post(url, params, { headers: headers });
} catch (error) {
console.log('error', error);
}
}
}
// if (sendMail.data.result.status === 'success') {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_document',
properties: { response_code: 200 },
});
}
const subscriptionCls = new Parse.Object('contracts_Subscriptions');
subscriptionCls.id = resSub.id;
if (allowedCredits > 0) {
const updateAllowedcredits = allowedCredits - 1 || 0;
subscriptionCls.set('AllowedCredits', updateAllowedcredits);
} else {
const updateAddonCredits = addonCredits > 0 ? addonCredits - 1 : 0;
subscriptionCls.set('AddonCredits', updateAddonCredits);
}
const resSubcription = await subscriptionCls.save(null, { useMasterKey: true });
// console.log('resSubcription ', resSubcription);
return response.json({
objectId: res.id,
signurl: contact.map(x => ({
email: x.email,
url: `${baseUrl.origin}/login/${btoa(
`${res.id}/${x.email}/${x.contactPtr.objectId}`
)}`,
})),
message: 'Document sent successfully!',
});
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_document',
properties: { response_code: 400 },
});
}
return response.status(400).json({ error: 'Please provide signers!' });
}
} else {
return response
.status(429)
.json({ error: 'Quota reached, Please buy credits and try again later.' });
}
} else {
return response.status(400).json({
error: 'Please purchase or renew your subscription.',
});
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -1,73 +0,0 @@
export default async function createFolder(request, response) {
const apiToken = request.headers['x-api-token'];
const folderName = request.body.folderName;
const parentFolderId = request.body.parentFolderId;
if (!apiToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
try {
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', apiToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const userQuery = new Parse.Query('contracts_Users');
userQuery.equalTo('UserId', userPtr);
const extUser = await userQuery.first({ useMasterKey: true });
const folderCls = new Parse.Object('contracts_Document');
folderCls.set('Name', folderName);
folderCls.set('CreatedBy', userPtr);
folderCls.set('Type', 'Folder');
folderCls.set('ExtUserPtr', {
__type: 'Pointer',
className: 'contracts_Users',
objectId: extUser.id,
});
if (parentFolderId) {
folderCls.set('Folder', {
__type: 'Pointer',
className: 'contracts_Document',
objectId: parentFolderId,
});
}
const acl = new Parse.ACL();
acl.setReadAccess(parseUser.userId.objectId, true); // Allow read access to the userPtr
acl.setWriteAccess(parseUser.userId.objectId, true); // Allow write access to the userPtr
// Set the ACL to the document
folderCls.setACL(acl);
const _resFolder = await folderCls.save(null, { useMasterKey: true });
if (_resFolder) {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_folder',
properties: { response_code: 200 },
});
}
const parseRes = JSON.parse(JSON.stringify(_resFolder));
return response.json({
objectId: parseRes.objectId,
folderName: parseRes.Name,
parentFolderId: parseRes?.Folder?.objectId || '',
createdAt: parseRes.createdAt,
updatedAt: parseRes.updatedAt,
});
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -1,242 +0,0 @@
import axios from 'axios';
import {
color,
customAPIurl,
saveFileUsage,
formatWidgetOptions,
sanitizeFileName,
} from '../../../../Utils.js';
import uploadFileToS3 from '../../../parsefunction/uploadFiletoS3.js';
const randomId = () => Math.floor(1000 + Math.random() * 9000);
export default async function createTemplatewithCoordinate(request, response) {
const name = request.body.title;
const note = request.body.note;
const description = request.body.description;
const signers = request.body.signers;
const folderId = request.body.folderId;
const base64File = request.body.file;
const fileData = request.files?.[0] ? request.files[0].buffer : null;
const SendinOrder = request.body.sendInOrder || true;
const isEnableOTP = request.body?.enableOTP === true ? true : false;
const isTourEnabled = request.body?.enableTour || false;
// console.log('fileData ', fileData);
const protocol = customAPIurl();
try {
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
if (signers && signers.length > 0) {
const contractsUser = new Parse.Query('contracts_Users');
contractsUser.equalTo('UserId', userPtr);
contractsUser.include('TenantId');
const extUser = await contractsUser.first({ useMasterKey: true });
const parseExtUser = JSON.parse(JSON.stringify(extUser));
let fileUrl;
if (request.files?.[0]) {
const base64 = fileData?.toString('base64');
const file = new Parse.File(request.files?.[0]?.originalname, {
base64: base64,
});
await file.save({ useMasterKey: true });
fileUrl = file.url();
const buffer = Buffer.from(base64, 'base64');
saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId);
} else {
const filename = sanitizeFileName(`${name}.pdf`);
let adapter = {};
const ActiveFileAdapter = parseExtUser?.TenantId?.ActiveFileAdapter || '';
if (ActiveFileAdapter) {
adapter =
parseExtUser?.TenantId?.FileAdapters?.find(x => (x.id = ActiveFileAdapter)) || {};
}
if (adapter?.id) {
const filedata = Buffer.from(base64File, 'base64');
// `uploadFileToS3` is used to save document in user's file storage
fileUrl = await uploadFileToS3(filedata, filename, 'application/pdf', adapter);
} else {
const file = new Parse.File(filename, { base64: base64File }, 'application/pdf');
await file.save({ useMasterKey: true });
fileUrl = file.url();
}
const buffer = Buffer.from(base64File, 'base64');
saveFileUsage(buffer.length, fileUrl, parseUser.userId.objectId);
}
const extUserPtr = {
__type: 'Pointer',
className: 'contracts_Users',
objectId: extUser.id,
};
const object = new Parse.Object('contracts_Template');
object.set('Name', name);
if (note) {
object.set('Note', note);
}
if (description) {
object.set('Description', description);
}
if (SendinOrder) {
object.set('SendinOrder', SendinOrder);
}
object.set('URL', fileUrl);
object.set('CreatedBy', userPtr);
object.set('ExtUserPtr', extUserPtr);
object.set('IsEnableOTP', isEnableOTP);
object.set('IsTourEnabled', isTourEnabled);
let contact = [];
if (signers && signers.length > 0) {
let parseSigners;
if (base64File) {
parseSigners = signers;
} else {
parseSigners = JSON.parse(signers);
}
let createContactUrl = protocol + '/v1/createcontact';
for (const [index, element] of parseSigners.entries()) {
if (element?.name && element?.email) {
const body = {
name: element?.name || '',
email: element?.email || '',
phone: element?.phone || '',
};
try {
const res = await axios.post(createContactUrl, body, {
headers: { 'Content-Type': 'application/json', 'x-api-token': reqToken },
});
// console.log('res ', res.data);
const contactPtr = {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: res.data?.objectId,
};
const newObj = { ...element, contactPtr: contactPtr, index: index };
contact.push(newObj);
} catch (err) {
// console.log('err ', err.response);
if (err?.response?.data?.objectId) {
const contactPtr = {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: err.response.data?.objectId,
};
const newObj = { ...element, contactPtr: contactPtr, index: index };
contact.push(newObj);
}
}
} else {
const newObj = { ...element, contactPtr: {}, index: index };
contact.push(newObj);
}
}
const updatedSigners = contact?.filter(x => x.contactPtr && x.contactPtr.objectId);
if (updatedSigners && updatedSigners.length > 0) {
object.set(
'Signers',
updatedSigners?.map(x => x.contactPtr)
);
}
let updatePlaceholders = contact.map((signer, index) => {
const placeHolder = [];
for (const widget of signer.widgets) {
const pageNumber = widget.page;
const page = placeHolder.find(page => page.pageNumber === pageNumber);
const options = formatWidgetOptions(widget.type, widget.options);
const widgetData = {
isStamp: widget.type === 'stamp' || widget.type === 'image',
key: randomId(),
isDrag: false,
scale: 1,
isMobile: false,
zIndex: 1,
type: widget.type === 'textbox' ? 'text input' : widget.type,
options: options,
Width: widget.w,
Height: widget.h,
xPosition: widget.x,
yPosition: widget.y,
};
if (page) {
page.pos.push(widgetData);
} else {
placeHolder.push({
pageNumber,
pos: [widgetData],
});
}
}
return {
signerObjId: signer?.contactPtr?.objectId,
signerPtr: signer?.contactPtr,
Role: signer.role,
Id: randomId(),
blockColor: color[signer?.index],
placeHolder,
};
});
object.set('Placeholders', updatePlaceholders);
}
if (folderId) {
object.set('Folder', {
__type: 'Pointer',
className: 'contracts_Template',
objectId: folderId,
});
}
if (parseExtUser?.TenantId?.ActiveFileAdapter) {
object.set('FileAdapterId', parseExtUser?.TenantId?.ActiveFileAdapter);
}
const newACL = new Parse.ACL();
newACL.setPublicReadAccess(false);
newACL.setPublicWriteAccess(false);
newACL.setReadAccess(userPtr.objectId, true);
newACL.setWriteAccess(userPtr.objectId, true);
object.setACL(newACL);
const res = await object.save(null, { useMasterKey: true });
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_template',
properties: { response_code: 200 },
});
}
return response.json({
objectId: res.id,
message: 'Template created successfully!',
});
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_create_template',
properties: { response_code: 400 },
});
}
return response.status(400).json({ error: 'Please provide signers!' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -1,65 +0,0 @@
export default async function deleteContact(request, response) {
try {
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const Contactbook = new Parse.Query('contracts_Contactbook');
Contactbook.equalTo('objectId', request.params.contact_id);
Contactbook.equalTo('CreatedBy', userPtr);
const res = await Contactbook.first({ useMasterKey: true });
if (res) {
const isDeleted = res.get('IsDeleted');
if (isDeleted && isDeleted) {
return response.status(404).json({ error: 'Contact not found!' });
} else {
const Contactbook = Parse.Object.extend('contracts_Contactbook');
const deleteQuery = new Contactbook();
deleteQuery.id = request.params.contact_id;
deleteQuery.set('IsDeleted', true);
const deleteRes = await deleteQuery.save(null, { useMasterKey: true });
if (deleteRes) {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_delete_contact',
properties: { response_code: 200 },
});
}
return response.json({
objectId: request.params.contact_id,
deletedAt: deleteRes.get('updatedAt'),
});
}
}
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_delete_contact',
properties: { response_code: 404 },
});
}
return response.status(404).json({ error: 'Contact not found!' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -1,66 +0,0 @@
export default async function deleteDocument(request, response) {
try {
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const Document = new Parse.Query('contracts_Document');
Document.equalTo('objectId', request.params.document_id);
Document.equalTo('CreatedBy', userPtr);
Document.include('ExtUserPtr.TenantId');
const res = await Document.first({ useMasterKey: true });
if (res) {
const isArchive = res.get('IsArchive');
if (isArchive && isArchive) {
return response.status(404).json({ error: 'Document not found!' });
} else {
const Document = Parse.Object.extend('contracts_Document');
const deleteQuery = new Document();
deleteQuery.id = request.params.document_id;
deleteQuery.set('IsArchive', true);
const deleteRes = await deleteQuery.save(null, { useMasterKey: true });
if (deleteRes) {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_delete_document',
properties: { response_code: 200 },
});
}
return response.json({
objectId: request.params.document_id,
deletedAt: deleteRes.get('updatedAt'),
});
}
}
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_delete_document',
properties: { response_code: 404 },
});
}
return response.status(404).json({ error: 'Document not found!' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -1,76 +0,0 @@
export default async function deleteFolder(request, response) {
const apiToken = request.headers['x-api-token'];
const folderId = request.params.folder_id;
if (!apiToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
try {
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', apiToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const obj = new Parse.Query('contracts_Document');
obj.equalTo('objectId', folderId);
obj.equalTo('IsArchive', true);
obj.equalTo('CreatedBy', userPtr);
obj.include('ExtUserPtr.TenantId');
const isFolderDeleted = await obj.first({ useMasterKey: true });
if (!isFolderDeleted) {
const folder = new Parse.Query('contracts_Document');
folder.equalTo('Folder', {
__type: 'Pointer',
className: 'contracts_Document',
objectId: folderId,
});
folder.notEqualTo('IsArchive', true);
folder.equalTo('CreatedBy', userPtr);
folder.equalTo('Type', 'Folder');
folder.include('ExtUserPtr.TenantId');
const isSubItems = await folder.first({ useMasterKey: true });
// console.log('isSubItems ', isSubItems);
if (isSubItems) {
return response
.status(400)
.json({ error: 'folder is not empty, contains document or folder.' });
} else {
const deleteFolder = new Parse.Object('contracts_Document');
deleteFolder.id = folderId;
deleteFolder.set('IsArchive', true);
const deleteRes = await deleteFolder.save(null, { useMasterKey: true });
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_delete_folder',
properties: { response_code: 200 },
});
}
return response.json({ objectId: deleteRes.id, deletedAt: deleteRes.updatedAt });
}
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_delete_folder',
properties: { response_code: 404 },
});
}
return response.status(404).json({ error: 'folder not found.' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err is delete folder API', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -1,72 +0,0 @@
export default async function deletedTemplate(request, response) {
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
try {
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const template = new Parse.Query('contracts_Template');
template.equalTo('objectId', request.params.template_id);
template.equalTo('CreatedBy', userPtr);
template.include('ExtUserPtr.TenantId');
const res = await template.first({ useMasterKey: true });
if (res) {
const isArchive = res.get('IsArchive');
if (isArchive && isArchive) {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_delete_template',
properties: { response_code: 404 },
});
}
return response.status(404).json({ error: 'Template not found!' });
} else {
const template = Parse.Object.extend('contracts_Template');
const deleteQuery = new template();
deleteQuery.id = request.params.template_id;
deleteQuery.set('IsArchive', true);
const deleteRes = await deleteQuery.save(null, { useMasterKey: true });
if (deleteRes) {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_delete_template',
properties: { response_code: 200 },
});
}
return response.json({
objectId: request.params.template_id,
deletedAt: deleteRes.get('updatedAt'),
});
}
}
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_delete_template',
properties: { response_code: 404 },
});
}
return response.status(404).json({ error: 'Template not found!' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -1,57 +0,0 @@
export default async function deleteWebhook(request, response) {
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
try {
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const query = new Parse.Query('contracts_Users');
query.equalTo('UserId', userPtr);
const user = await query.first({ useMasterKey: true });
if (user) {
const updateQuery = new Parse.Object('contracts_Users');
updateQuery.id = user.id;
updateQuery.unset('Webhook');
const res = await updateQuery.save(null, { useMasterKey: true });
if (res) {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_delete_webhook',
properties: { response_code: 200 },
});
}
return response.json({
result: 'Webhook deleted successfully!',
});
}
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_delete_webhook',
properties: { response_code: 404 },
});
}
return response.status(404).json({ error: 'User not found!' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -1,61 +0,0 @@
export default async function getContact(request, response) {
try {
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const Contactbook = new Parse.Query('contracts_Contactbook');
Contactbook.equalTo('objectId', request.params.contact_id);
Contactbook.equalTo('CreatedBy', userPtr);
Contactbook.notEqualTo('IsDeleted', true);
Contactbook.select('Name,Email,Phone');
const res = await Contactbook.first({ useMasterKey: true });
if (res) {
const parseRes = JSON.parse(JSON.stringify(res));
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_contact',
properties: { response_code: 200 },
});
}
return response.json({
objectId: parseRes.objectId,
name: parseRes.Name,
email: parseRes.Email,
phone: parseRes?.Phone || '',
createdAt: parseRes.createdAt,
updatedAt: parseRes.updatedAt,
});
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_contact',
properties: { response_code: 404 },
});
}
return response.status(404).json({ error: 'Contact not found!' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -1,63 +0,0 @@
export default async function getContactList(request, response) {
try {
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const limit = request?.query?.limit ? parseInt(request.query.limit) : 100;
const skip = request?.query?.skip ? parseInt(request.query.skip) : 0;
const Contactbook = new Parse.Query('contracts_Contactbook');
Contactbook.equalTo('CreatedBy', userPtr);
Contactbook.notEqualTo('IsDeleted', true);
Contactbook.limit(limit);
Contactbook.skip(skip);
Contactbook.descending('createdAt');
const res = await Contactbook.find({ useMasterKey: true });
if (res && res.length > 0) {
const parseRes = JSON.parse(JSON.stringify(res));
const contactlist = parseRes.map(x => ({
objectId: x.objectId,
name: x.Name,
email: x.Email,
phone: x?.Phone || '',
createdAt: x.createdAt,
updatedAt: x.updatedAt,
}));
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_contact_list',
properties: { response_code: 200 },
});
}
return response.json({ result: contactlist });
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_contact_list',
properties: { response_code: 200 },
});
}
return response.json({ result: [] });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -1,88 +0,0 @@
export default async function getDocument(request, response) {
try {
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const Document = new Parse.Query('contracts_Document');
Document.equalTo('objectId', request.params.document_id);
Document.equalTo('CreatedBy', userPtr);
Document.notEqualTo('IsArchive', true);
Document.include('Signers');
Document.include('Folder');
Document.include('ExtUserPtr');
Document.include('Placeholders.signerPtr');
Document.include('ExtUserPtr.TenantId');
const res = await Document.first({ useMasterKey: true });
if (res) {
const document = JSON.parse(JSON.stringify(res));
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_document',
properties: { response_code: 200 },
});
}
return response.json({
objectId: document.objectId,
title: document.Name,
note: document.Note || '',
folder: { objectId: document?.Folder?.objectId, name: document?.Folder?.Name } || '',
file: document?.SignedUrl || document.URL,
certificate: document?.CertificateUrl || '',
owner: document?.ExtUserPtr?.Name,
signers:
document?.Placeholders?.map(y => ({
role: y.Role,
name: y?.signerPtr?.Name || '',
email: y?.signerPtr?.Email || '',
phone: y?.signerPtr?.Phone || '',
widgets: y.placeHolder?.flatMap(x =>
x?.pos.map(w => ({
type: w?.type ? w.type : w.isStamp ? 'stamp' : 'signature',
x: w.xPosition,
y: w.yPosition,
w: w?.Width || 150,
h: w?.Height || 60,
page: x?.pageNumber,
}))
),
})) ||
document?.Signers?.map(y => ({ name: y?.Name, email: y?.Email, phone: y?.Phone })) ||
[],
sendInOrder: document?.SendinOrder || false,
enableOTP: document?.IsEnableOTP || false,
createdAt: document.createdAt,
updatedAt: document.updatedAt,
enableTour: document?.IsTourEnabled || false,
});
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_document',
properties: { response_code: 404 },
});
}
return response.status(404).json({ error: 'Document not found!' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -1,141 +0,0 @@
import axios from 'axios';
import reportJson from '../../../parsefunction/reportsJson.js';
import dotenv from 'dotenv';
import { cloudServerUrl } from '../../../../Utils.js';
dotenv.config();
export default async function getDocumentList(request, response) {
const reqToken = request.headers['x-api-token'];
const appId = process.env.APP_ID;
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
try {
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const docType = request.params.doctype;
const limit = request?.query?.limit ? request.query.limit : 100;
const skip = request?.query?.skip ? request.query.skip : 0;
let reportId;
switch (docType) {
case 'draft':
reportId = 'ByHuevtCFY';
break;
case 'signaturerequest':
reportId = '4Hhwbp482K';
break;
case 'inprogress':
reportId = '1MwEuxLEkF';
break;
case 'completed':
reportId = 'kQUoW4hUXz';
break;
case 'expired':
reportId = 'zNqBHXHsYH';
break;
case 'declined':
reportId = 'UPr2Fm5WY3';
break;
default:
reportId = '';
}
const json = reportId && reportJson(reportId, userPtr.objectId);
const clsName = 'contracts_Document';
if (reportId && json) {
const { params, keys } = json;
const orderBy = '-updatedAt';
const strParams = JSON.stringify(params);
const strKeys = keys.join();
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': appId,
'X-Parse-Master-Key': process.env.MASTER_KEY,
};
const url = `${serverUrl}/classes/${clsName}?where=${strParams}&keys=${strKeys},Placeholders&order=${orderBy}&skip=${skip}&limit=${limit}&include=AuditTrail.UserPtr,Placeholders.signerPtr,ExtUserPtr.TenantId`;
try {
const res = await axios.get(url, { headers: headers });
if (res.data && res.data.results.length > 0) {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: `api_get_document_list_${docType}`,
properties: { response_code: 200, doc_type: docType },
});
}
const updateRes = res.data.results.map(x => ({
objectId: x.objectId,
title: x.Name,
note: x.Note || '',
folder: { objectId: x?.Folder?.objectId, name: x?.Folder?.Name } || '',
// file: x?.SignedUrl || x.URL,
owner: x?.ExtUserPtr?.Name,
signers:
x?.Placeholders?.map(y => ({
role: y.Role,
name: y?.signerPtr?.Name || '',
email: y?.signerPtr?.Email || '',
phone: y?.signerPtr?.Phone || '',
widgets: y.placeHolder?.flatMap(x =>
x?.pos.map(w => ({
type: w?.type ? w.type : w.isStamp ? 'stamp' : 'signature',
x: w.xPosition,
y: w.yPosition,
w: w?.Width || 150,
h: w?.Height || 60,
page: x?.pageNumber,
}))
),
})) ||
x?.Signers?.map(y => ({ name: y?.Name, email: y?.Email, phone: y?.Phone })) ||
[],
sendInOrder: x?.SendinOrder || false,
enableOTP: x?.IsEnableOTP || false,
createdAt: x.createdAt,
updatedAt: x.updatedAt,
enableTour: x?.IsTourEnabled || false,
}));
return response.json({ result: updateRes });
} else {
return response.json({ result: [] });
}
} catch (err) {
console.log('err in getdocument list', err);
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: `api_get_document_list_${docType}`,
properties: { response_code: 400, doc_type: docType },
});
}
return response
.status(400)
.json({ error: 'Something went wrong, please try again later!' });
}
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: `api_get_document_list_${docType}`,
properties: { response_code: 404, doc_type: docType },
});
}
return response.status(404).json({ error: 'Report not available!' });
}
}
return response.status(405).json({ error: 'Invalid API Token!' });
} catch (err) {
console.log('Err', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -1,64 +0,0 @@
export default async function getFolder(request, response) {
const apiToken = request.headers['x-api-token'];
const folder_id = request.params.folder_id;
if (!apiToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
try {
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', apiToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const folderCls = new Parse.Query('contracts_Document');
folderCls.equalTo('CreatedBy', userPtr);
folderCls.equalTo('objectId', folder_id);
folderCls.equalTo('Type', 'Folder');
folderCls.notEqualTo('IsArchive', true);
folderCls.descending('createdAt');
folderCls.include('Folder');
folderCls.include('ExtUserPtr.TenantId');
const res = await folderCls.first({ useMasterKey: true });
if (res) {
const parseRes = JSON.parse(JSON.stringify(res));
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_folder',
properties: { response_code: 200 },
});
}
return response.json({
objectId: parseRes.objectId,
folderName: parseRes.Name,
parentFolderId: parseRes?.Folder?.objectId || '',
parentFolderName: parseRes?.Folder?.Name || '',
createdAt: parseRes.createdAt,
updatedAt: parseRes.updatedAt,
});
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_folder',
properties: { response_code: 404 },
});
}
return response.status(404).json({ error: 'folder not found.' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -1,77 +0,0 @@
export default async function getFolderList(request, response) {
const apiToken = request.headers['x-api-token'];
const parentFolderId = request.query?.parentFolderId || '';
if (!apiToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
try {
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', apiToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const limit = request?.query?.limit ? parseInt(request.query.limit) : 100;
const skip = request?.query?.skip ? parseInt(request.query.skip) : 0;
const folderCls = new Parse.Query('contracts_Document');
folderCls.equalTo('CreatedBy', userPtr);
folderCls.equalTo('Type', 'Folder');
folderCls.notEqualTo('IsArchive', true);
folderCls.descending('createdAt');
folderCls.include('Folder');
if (parentFolderId) {
folderCls.equalTo('Folder', {
__type: 'Pointer',
className: 'contracts_Document',
objectId: parentFolderId,
});
} else {
folderCls.doesNotExist('Folder');
}
folderCls.descending('createdAt');
folderCls.include('ExtUserPtr.TenantId');
folderCls.limit(limit);
folderCls.skip(skip);
const res = await folderCls.find({ useMasterKey: true });
if (res && res.length > 0) {
const parseRes = JSON.parse(JSON.stringify(res));
const folderlist = parseRes.map(x => ({
objectId: x.objectId,
folderName: x.Name,
parentFolderId: x?.Folder?.objectId || '',
parentFolderName: x?.Folder?.Name || '',
createdAt: x.createdAt,
updatedAt: x.updatedAt,
}));
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_Folder_list',
properties: { response_code: 200 },
});
}
return response.json({ result: folderlist });
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_Folder_list',
properties: { response_code: 200 },
});
}
return response.json({ result: [] });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -1,87 +0,0 @@
export default async function getTemplate(request, response) {
try {
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const Template = new Parse.Query('contracts_Template');
Template.equalTo('objectId', request.params.template_id);
Template.equalTo('CreatedBy', userPtr);
Template.notEqualTo('IsArchive', true);
Template.include('Signers');
Template.include('Folder');
Template.include('ExtUserPtr');
Template.include('Placeholders.signerPtr');
Template.include('ExtUserPtr.TenantId');
const res = await Template.first({ useMasterKey: true });
if (res) {
const template = JSON.parse(JSON.stringify(res));
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_template',
properties: { response_code: 200 },
});
}
return response.json({
objectId: template.objectId,
title: template.Name,
note: template.Note || '',
folder: { objectId: template?.Folder?.objectId, name: template?.Folder?.Name } || '',
file: template?.SignedUrl || template?.URL,
owner: template?.ExtUserPtr?.Name,
signers:
template?.Placeholders?.map(y => ({
role: y.Role,
name: y?.signerPtr?.Name || '',
email: y?.signerPtr?.Email || '',
phone: y?.signerPtr?.Phone || '',
widgets: y.placeHolder?.flatMap(x =>
x?.pos.map(w => ({
type: w?.type ? w.type : w.isStamp ? 'stamp' : 'signature',
x: w.xPosition,
y: w.yPosition,
w: w?.Width || 150,
h: w?.Height || 60,
page: x?.pageNumber,
}))
),
})) || [],
sendInOrder: template?.SendinOrder || false,
enableOTP: template?.IsEnableOTP || false,
createdAt: template.createdAt,
updatedAt: template.updatedAt,
enableTour: template?.IsTourEnabled || false,
});
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_template',
properties: { response_code: 404 },
});
}
return response.status(404).json({ error: 'Template not found!' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -1,114 +0,0 @@
import axios from 'axios';
import dotenv from 'dotenv';
import { cloudServerUrl } from '../../../../Utils.js';
dotenv.config();
export default async function getTemplatetList(request, response) {
const reqToken = request.headers['x-api-token'];
const appId = process.env.APP_ID;
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
try {
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const limit = request?.query?.limit ? request.query.limit : 100;
const skip = request?.query?.skip ? request.query.skip : 0;
const clsName = 'contracts_Template';
const params = {
Type: { $ne: 'Folder' },
CreatedBy: userPtr,
IsArchive: { $ne: true },
};
const keys = [
'Name',
'Note',
'Description',
'Folder.Name',
'URL',
'SignedUrl',
'ExtUserPtr.Name',
'Signers.Name',
'Signers.Email',
'Signers.Phone',
'Placeholders',
];
const orderBy = '-updatedAt';
const strParams = JSON.stringify(params);
const strKeys = keys.join();
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': appId,
'X-Parse-Master-Key': process.env.MASTER_KEY,
};
const url = `${serverUrl}/classes/${clsName}?where=${strParams}&keys=${strKeys}&order=${orderBy}&skip=${skip}&limit=${limit}&include=AuditTrail.UserPtr,Placeholders.signerPtr,ExtUserPtr.TenantId`;
const res = await axios.get(url, { headers: headers });
if (res.data && res.data.results.length > 0) {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_template_list',
properties: { response_code: 200 },
});
}
const updateRes = res.data.results.map(template => ({
objectId: template.objectId,
title: template.Name,
note: template.Note || '',
folder: { objectId: template?.Folder?.objectId, name: template?.Folder?.Name } || '',
file: template?.SignedUrl || template.URL,
owner: template?.ExtUserPtr?.Name,
signers:
template?.Placeholders?.map(y => ({
role: y.Role,
name: y?.signerPtr?.Name || '',
email: y?.signerPtr?.Email || '',
phone: y?.signerPtr?.Phone || '',
widgets: y.placeHolder?.flatMap(x =>
x?.pos.map(w => ({
type: w?.type ? w.type : w.isStamp ? 'stamp' : 'signature',
x: w.xPosition,
y: w.yPosition,
w: w?.Width || 150,
h: w?.Height || 60,
page: x?.pageNumber,
}))
),
})) || [],
sendInOrder: template?.SendinOrder || false,
enableOTP: template?.IsEnableOTP || false,
createdAt: template.createdAt,
updatedAt: template.updatedAt,
enableTour: template?.IsTourEnabled || false,
}));
return response.json({ result: updateRes });
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_template_list',
properties: { response_code: 200 },
});
}
return response.json({ result: [] });
}
}
return response.status(405).json({ error: 'Invalid API Token!' });
} catch (err) {
console.log('err', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -1,62 +0,0 @@
import dotenv from 'dotenv';
dotenv.config();
export default async function getUser(request, response) {
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
try {
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const query = new Parse.Query('contracts_Users');
query.equalTo('UserId', userPtr);
query.exclude('IsContactEntry,TourStatus,UserRole,TenantId,UserId,CreatedBy,Plan');
const user = await query.first({ useMasterKey: true });
if (user) {
const parseRes = JSON.parse(JSON.stringify(user));
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_your_account_details',
properties: { response_code: 200 },
});
}
return response.json({
objectId: parseRes.objectId,
name: parseRes.Name,
email: parseRes.Email,
phone: parseRes?.Phone || '',
jobTitle: parseRes.JobTitle,
company: parseRes.Company,
createdAt: parseRes.createdAt,
updatedAt: parseRes.updatedAt,
});
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_your_account_details',
properties: { response_code: 404 },
});
}
return response.status(404).json({ error: 'User not found!' });
}
}
return response.status(405).json({ error: 'Invalid API Token!' });
} catch (err) {
console.log('Err', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -1,55 +0,0 @@
export default async function getWebhook(request, response) {
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
try {
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const query = new Parse.Query('contracts_Users');
query.equalTo('UserId', userPtr);
const user = await query.first({ useMasterKey: true });
if (user) {
const extUser = JSON.parse(JSON.stringify(user));
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_webhook',
properties: { response_code: 200 },
});
}
if (extUser && extUser.Webhook) {
return response.json({
webhook: extUser.Webhook,
});
} else {
return response.json({});
}
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_get_webhook',
properties: { response_code: 404 },
});
}
return response.status(404).json({ error: 'User not found!' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('Err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -1,159 +0,0 @@
import axios from 'axios';
import { cloudServerUrl, replaceMailVaribles } from '../../../../Utils.js';
export default async function resendMail(request, response) {
try {
const reqToken = request.headers['x-api-token'];
const docId = request.body.document_id;
const userMail = request.body.email;
const email_subject = request.body.email_subject;
const email_body = request.body.email_body;
const baseUrl = new URL(process.env.SERVER_URL);
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const docQuery = new Parse.Query('contracts_Document');
docQuery.equalTo('objectId', docId);
docQuery.equalTo('CreatedBy', userPtr);
docQuery.include('Signers,ExtUserPtr');
docQuery.notEqualTo('IsCompleted', true);
docQuery.notEqualTo('IsDeclined', true);
docQuery.notEqualTo('IsArchive', true);
docQuery.greaterThanOrEqualTo('ExpiryDate', new Date());
docQuery.exists('SignedUrl');
docQuery.include('ExtUserPtr.TenantId');
const resDoc = await docQuery.first({ useMasterKey: true });
// console.log("resDoc ",resDoc)
if (resDoc) {
const _resDoc = resDoc.toJSON();
const contact = _resDoc.Signers.find(x => x.Email === userMail);
const activeMailAdapter = _resDoc?.ExtUserPtr?.active_mail_adapter || '';
if (contact) {
try {
const imgPng = 'https://qikinnovation.ams3.digitaloceanspaces.com/logo.png';
let url = `${cloudServerUrl}/functions/sendmailv3/`;
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Master-Key': process.env.MASTER_KEY,
};
const objectId = contact.objectId;
const hostUrl = baseUrl.origin;
const title = _resDoc.Name;
const receiverMail = contact.Email;
//encode this url value `${response.id}/${receiverMail}/${objectId}` to base64 using `btoa` function
const encodeBase64 = btoa(`${_resDoc.objectId}/${receiverMail}/${objectId}`);
let signPdf = `${hostUrl}/login/${encodeBase64}`;
const openSignUrl = 'https://www.opensignlabs.com/contact-us';
const orgName = _resDoc.ExtUserPtr.Company ? _resDoc.ExtUserPtr.Company : '';
const newDate = new Date(_resDoc.ExpiryDate.iso);
newDate.setDate(newDate.getDate() + 15);
const sender = _resDoc.ExtUserPtr.Email;
const localExpireDate = newDate.toLocaleDateString('en-US', {
day: 'numeric',
month: 'long',
year: 'numeric',
});
const themeBGcolor = '#47a3ad';
const email_html =
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /> </head> <body> <div style='background-color: #f5f5f5; padding: 20px'> <div style=' box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background: white;padding-bottom: 20px;'> <div style='padding:10px 10px 0 10px'><img src=" +
imgPng +
" height='50' style='padding:20px; width:170px; height:40px;' /></div> <div style='padding:2px; font-family: system-ui;background-color:" +
themeBGcolor +
";'><p style='font-size: 20px;font-weight: 400;color: white;padding-left: 20px;' > Digital Signature Request</p></div><div><p style='padding: 20px;font-family: system-ui;font-size: 14px; margin-bottom: 10px;'> " +
_resDoc.ExtUserPtr.Name +
' has requested you to review and sign <strong> ' +
title +
"</strong>.</p><div style='padding: 5px 0px 5px 25px;display: flex;flex-direction: row;justify-content: space-around;'><table> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Sender</td> <td> </td> <td style='color:#626363;font-weight:bold'>" +
sender +
"</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Organization</td> <td> </td><td style='color:#626363;font-weight:bold'> " +
orgName +
"</td></tr> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expires on</td><td> </td> <td style='color:#626363;font-weight:bold'>" +
localExpireDate +
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a target=_blank href=" +
signPdf +
"> <button style='padding: 12px 12px 12px 12px;background-color: #d46b0f;color: white; border: 0px;box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;font-weight:bold;margin-top:30px'>Sign here</button></a> </div> <div style='display: flex; justify-content: center;margin-top: 10px;'> </div></div></div><div><p> This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " +
sender +
' directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href=' +
openSignUrl +
' target=_blank>here</a>.</p> </div></div></body> </html>';
let replaceVar;
const variables = {
document_title: title,
sender_name: _resDoc.ExtUserPtr.Name,
sender_mail: _resDoc.ExtUserPtr.Email,
sender_phone: _resDoc.ExtUserPtr?.Phone || '',
receiver_name: contact.Name,
receiver_email: contact.Email,
receiver_phone: contact?.Phone || '',
expiry_date: localExpireDate,
company_name: orgName,
signing_url: signPdf,
};
if (email_subject && email_body) {
replaceVar = replaceMailVaribles(email_subject, email_body, variables);
} else if (email_subject) {
replaceVar = replaceMailVaribles(email_subject, '', variables);
replaceVar = { subject: replaceVar.subject, body: email_html };
} else if (email_body) {
replaceVar = replaceMailVaribles(
`${_resDoc.ExtUserPtr.Name} has requested you to sign "${title}"`,
email_body,
variables
);
} else {
replaceVar = {
subject: `${_resDoc.ExtUserPtr.Name} has requested you to sign "${title}"`,
body: email_html,
};
}
const subject = replaceVar.subject;
const html = replaceVar.body;
let params = {
recipient: contact.Email,
subject: subject,
from: sender,
html: html,
extUserId: _resDoc.ExtUserPtr.objectId,
mailProvider: activeMailAdapter,
};
const res = await axios.post(url, params, { headers: headers });
if (res.data.result && res.data.result.status === 'success') {
return response.json({ result: 'mail sent successfully.' });
}
} catch (error) {
console.log('error', error);
return response.status(400).json({ error: error.message });
}
} else {
return response.status(404).json({ error: 'user not found.' });
}
} else {
return response.status(404).json({ error: 'document not found.' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token.' });
}
} catch (err) {
console.log('err in resendmail', err);
return response.status(400).json({ error: err.message || 'Something went wrong.' });
}
}
@@ -1,85 +0,0 @@
export default async function saveWebhook(request, response) {
const Url = request.body.url;
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
try {
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const query = new Parse.Query('contracts_Users');
query.equalTo('UserId', userPtr);
const user = await query.first({ useMasterKey: true });
if (user) {
const extUser = JSON.parse(JSON.stringify(user));
const isUrlExist = extUser?.Webhook && extUser?.Webhook === Url;
if (!isUrlExist) {
try {
const updateQuery = new Parse.Object('contracts_Users');
updateQuery.id = user.id;
updateQuery.set('Webhook', Url);
const res = await updateQuery.save(null, { useMasterKey: true });
if (res) {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_save_webhook',
properties: { response_code: 200 },
});
}
return response.json({
result: 'Webhook updated successfully!',
});
}
} catch (err) {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_save_webhook',
properties: { response_code: 400 },
});
}
console.log('Err ', err);
return response
.status(400)
.json({ error: 'Something went wrong, please try again later!' });
}
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_save_webhook',
properties: { response_code: 401 },
});
}
return response.status(401).json({ error: 'Webhook url already exists!' });
}
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_save_webhook',
properties: { response_code: 404 },
});
}
return response.status(404).json({ error: 'User not found!' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('Err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -1,100 +0,0 @@
export default async function updateDocument(request, response) {
try {
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const allowedKeys = ['name', 'note', 'description', 'folderId', 'enableOTP', 'enableTour'];
const objectKeys = Object.keys(request.body);
const isValid = objectKeys.every(key => allowedKeys.includes(key)) && objectKeys.length > 0;
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
if (isValid) {
const document = new Parse.Query('contracts_Document');
document.equalTo('objectId', request.params.document_id);
document.equalTo('CreatedBy', userPtr);
document.include('ExtUserPtr.TenantId');
const res = await document.first({ useMasterKey: true });
if (res) {
const isArchive = res.get('IsArchive');
if (isArchive && isArchive) {
return response.status(404).json({ message: 'Document not found!' });
} else {
const document = Parse.Object.extend('contracts_Document');
const updateQuery = new document();
updateQuery.id = request.params.document_id;
if (request?.body?.name) {
updateQuery.set('Name', request?.body?.name);
}
if (request?.body?.note) {
updateQuery.set('Note', request?.body?.note);
}
if (request?.body?.description) {
updateQuery.set('Description', request?.body?.description);
}
if (request?.body?.folderId) {
updateQuery.set('Folder', {
__type: 'Pointer',
className: 'contracts_Document',
objectId: request?.body?.folderId,
});
}
if (request.body?.enableOTP !== undefined) {
updateQuery.set('IsEnableOTP', request.body?.enableOTP);
}
if (request.body?.enableTour !== undefined) {
updateQuery.set('IsTourEnabled', request.body?.enableTour);
}
const updatedRes = await updateQuery.save(null, { useMasterKey: true });
if (updatedRes) {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_update_document',
properties: { response_code: 200 },
});
}
return response.json({
objectId: updatedRes.id,
updatedAt: updatedRes.get('updatedAt'),
});
}
}
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_update_document',
properties: { response_code: 404 },
});
}
return response.status(404).json({ error: 'Document not found!' });
}
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_update_document',
properties: { response_code: 400 },
});
}
return response.status(400).json({ error: 'Please provide valid field names!' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -1,67 +0,0 @@
export default async function updateFolder(request, response) {
const apiToken = request.headers['x-api-token'];
const folderId = request.params.folder_id;
const name = request.body.folderName;
const parentFolderId = request.body.parentFolderId;
if (!apiToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
try {
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', apiToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
const folderCls = new Parse.Query('contracts_Document');
folderCls.equalTo('CreatedBy', userPtr);
folderCls.equalTo('objectId', folderId);
folderCls.equalTo('Type', 'Folder');
folderCls.notEqualTo('IsArchive', true);
folderCls.include('ExtUserPtr.TenantId');
const res = await folderCls.first({ useMasterKey: true });
if (res) {
const updateFolder = new Parse.Object('contracts_Document');
updateFolder.id = res.id;
updateFolder.set('Name', name);
if (parentFolderId) {
updateFolder.set('Folder', {
__type: 'Pointer',
className: 'contracts_Document',
objectId: parentFolderId,
});
}
const updateRes = await updateFolder.save(null, { useMasterKey: true });
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_update_folder',
properties: { response_code: 200 },
});
}
return response.json({ objectId: updateRes.id, updatedAt: updateRes.updatedAt });
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_update_folder',
properties: { response_code: 404 },
});
}
return response.status(404).json({ error: 'folder not found.' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -1,100 +0,0 @@
export default async function updateTemplate(request, response) {
try {
const reqToken = request.headers['x-api-token'];
if (!reqToken) {
return response.status(400).json({ error: 'Please Provide API Token' });
}
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('token', reqToken);
tokenQuery.include('userId');
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// Valid Token then proceed request
const allowedKeys = ['name', 'note', 'description', 'folderId', 'enableOTP', 'enableTour'];
const objectKeys = Object.keys(request.body);
const isValid = objectKeys.every(key => allowedKeys.includes(key)) && objectKeys.length > 0;
const parseUser = JSON.parse(JSON.stringify(token));
const userPtr = {
__type: 'Pointer',
className: '_User',
objectId: parseUser.userId.objectId,
};
if (isValid) {
const template = new Parse.Query('contracts_Template');
template.equalTo('objectId', request.params.template_id);
template.equalTo('CreatedBy', userPtr);
template.include('ExtUserPtr.TenantId');
const res = await template.first({ useMasterKey: true });
if (res) {
const isArchive = res.get('IsArchive');
if (isArchive && isArchive) {
return response.status(404).json({ message: 'Template not found!' });
} else {
const template = Parse.Object.extend('contracts_Template');
const updateQuery = new template();
updateQuery.id = request.params.template_id;
if (request?.body?.name) {
updateQuery.set('Name', request?.body?.name);
}
if (request?.body?.note) {
updateQuery.set('Note', request?.body?.note);
}
if (request?.body?.description) {
updateQuery.set('Description', request?.body?.description);
}
if (request?.body?.folderId) {
updateQuery.set('Folder', {
__type: 'Pointer',
className: 'contracts_Template',
objectId: request?.body?.folderId,
});
}
if (request.body?.enableOTP !== undefined) {
updateQuery.set('IsEnableOTP', request.body?.enableOTP);
}
if (request.body?.enableTour !== undefined) {
updateQuery.set('IsTourEnabled', request.body?.enableTour);
}
const updatedRes = await updateQuery.save(null, { useMasterKey: true });
if (updatedRes) {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_update_template',
properties: { response_code: 200 },
});
}
return response.json({
objectId: updatedRes.id,
updatedAt: updatedRes.get('updatedAt'),
});
}
}
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_update_template',
properties: { response_code: 404 },
});
}
return response.status(404).json({ error: 'Template not found!' });
}
} else {
if (request.posthog) {
request.posthog?.capture({
distinctId: parseUser.userId.email,
event: 'api_update_template',
properties: { response_code: 400 },
});
}
return response.status(400).json({ error: 'Please provide valid field names!' });
}
} else {
return response.status(405).json({ error: 'Invalid API Token!' });
}
} catch (err) {
console.log('err ', err);
return response.status(400).json({ error: 'Something went wrong, please try again later!' });
}
}
@@ -1,72 +0,0 @@
import { createTransport } from 'nodemailer';
import { appName } from '../../Utils.js';
// Online Javascript Editor for free
// Write, Edit and Run your Javascript code using JS Online Compiler
function generateOtpWithSum10() {
let otp = [];
let sum = 0;
// First, generate 3 random digits (0-9) and add them to the sum
for (let i = 0; i < 3; i++) {
let digit = Math.floor(Math.random() * 10);
otp.push(digit);
sum += digit;
}
// Calculate the last digit to ensure the sum equals 10
let lastDigit = 10 - sum;
// If the last digit is not valid (e.g., greater than 9), regenerate
if (lastDigit >= 0 && lastDigit <= 9) {
otp.push(lastDigit);
} else {
return generateOtpWithSum10(); // Recursively generate again
}
return otp.join('');
}
export default async function validateSmtp(request, response) {
const host = request.body.host;
const port = request.body?.port?.toString();
const username = request.body.username;
const password = request.body.password;
const secure = request.body?.secure;
const email = request.body?.email;
const otp = generateOtpWithSum10();
// console.log('Generated OTP:', otp);
try {
const smtpsecure = secure || port !== '465' ? false : true;
const transporterSMTP = createTransport({
host: host,
port: port,
secure: smtpsecure,
auth: { user: username, pass: password },
});
const from = appName;
const mailsender = username;
const messageParams = {
from: from + ' <' + mailsender + '>',
to: email,
subject: `Your ${appName} SMTP credentials verification code`,
text: 'mail',
html:
`<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body><div style='background-color:#f5f5f5;padding:20px'><div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background-color:white;'><div style='background-color:red;padding:2px;font-family:system-ui; background-color:#47a3ad;'> <p style='font-size:20px;font-weight:400;color:white;padding-left:20px',>SMTP Verification Code</p></div><div style='padding:20px'><p style='font-family:system-ui;font-size:14px'>Your verification code is:</p><p style=' text-decoration: none; font-weight: bolder; color:blue;font-size:45px;margin:20px'>` +
otp +
'</p></div> </div> </div></body></html>',
};
const res = await transporterSMTP.sendMail(messageParams);
console.log('custom smtp transporter res: ', res?.response);
if (!res.err) {
response.status(200).json({ message: 'success' });
}
} catch (err) {
console.log('err ', err);
const code = err?.responseCode || 400;
const message = err?.response || 'failed!';
response.status(code).json({ error: message });
}
}
+15 -85
View File
@@ -1,9 +1,7 @@
import PDF from './parsefunction/pdf/PDF.js';
import sendmailv3 from './parsefunction/sendMailv3.js';
import GoogleSign from './parsefunction/GoogleSign.js';
import ZohoDetails from './parsefunction/ZohoDetails.js';
import usersignup from './parsefunction/usersignup.js';
import FacebookSign from './parsefunction/FacebookSign.js';
import DocumentAftersave from './parsefunction/DocumentAftersave.js';
import ContactbookAftersave from './parsefunction/ContactBookAftersave.js';
import sendMailOTPv1 from './parsefunction/SendMailOTPv1.js';
@@ -13,86 +11,53 @@ import getUserDetails from './parsefunction/getUserDetails.js';
import getDocument from './parsefunction/getDocument.js';
import getDrive from './parsefunction/getDrive.js';
import getReport from './parsefunction/getReport.js';
import generateApiToken from './parsefunction/generateApiToken.js';
import getapitoken from './parsefunction/getapitoken.js';
import TemplateAfterSave from './parsefunction/TemplateAfterSave.js';
import GetTemplate from './parsefunction/GetTemplate.js';
import savewebhook from './parsefunction/saveWebhook.js';
import callWebhook from './parsefunction/callWebhook.js';
import SubscribeFree from './parsefunction/SubscribeFree.js';
import DocumentBeforesave from './parsefunction/DocumentBeforesave.js';
import TemplateBeforeSave from './parsefunction/TemplateBeforesave.js';
import DocumentBeforeFind from './parsefunction/DocumentAfterFind.js';
import TemplateAfterFind from './parsefunction/TemplateAfterFind.js';
import UserAfterFind from './parsefunction/UserAfterFInd.js';
import SignatureAfterFind from './parsefunction/SignatureAfterFind.js';
import getInvoices from './parsefunction/getInvoices.js';
import getPayments from './parsefunction/getPayments.js';
import getSubscriptions from './parsefunction/getSubscriptions.js';
import TenantAterFind from './parsefunction/TenantAfterFind.js';
import saveSubscription from './parsefunction/saveSubscription.js';
import VerifyEmail from './parsefunction/VerifyEmail.js';
import encryptedpdf from './parsefunction/encryptedPdf.js';
import { getSignedUrl } from './parsefunction/getSignedUrl.js';
import createBatchDocs from './parsefunction/createBatchDocs.js';
import linkContactToDoc from './parsefunction/linkContactToDoc.js';
import CreatePublicTemplate from './parsefunction/CreatePublicTemplate.js';
import GetPublicUserName from './parsefunction/GetPublicUserName.js';
import GetPublicTemplate from './parsefunction/GetPublicTemplate.js';
import ssoSignin from './parsefunction/ssoSignin.js';
import isextenduser from './parsefunction/isextenduser.js';
import getUserByOrg from './parsefunction/getUserByOrg.js';
import getUserListByOrg from './parsefunction/getUserListByOrg.js';
import TeamsAftersave from './parsefunction/TeamsAftersave.js';
import SubscriptionAftersave from './parsefunction/SubscriptionAftersave.js';
import PublicUserLinkContactToDoc from './parsefunction/PublicUserLinkContactToDoc.js';
import GetLogoByDomain from './parsefunction/GetLogobyDomain.js';
import GetLogoByDomain from './parsefunction/GetLogoByDomain.js';
import AddAdmin from './parsefunction/AddAdmin.js';
import CheckAdminExist from './parsefunction/CheckAdminExist.js';
import UpdateExistUserAsAdmin from './parsefunction/UpdateExistUserAsAdmin.js';
import Newsletter from './parsefunction/Newsletter.js';
import getOrganizations from './parsefunction/getOrganizations.js';
import addOrganization from './parsefunction/addOrganization.js';
import updateOrganization from './parsefunction/updateOrganization.js';
import getTeams from './parsefunction/getTeams.js';
import addTeam from './parsefunction/addTeam.js';
import updateTeam from './parsefunction/updateTeam.js';
import getOrgAdmins from './parsefunction/getOrgAdmins.js';
import getAllUserTeamByOrg from './parsefunction/getAllUserTeamByOrg.js';
import AllowedUsers from './parsefunction/AlllowedUsers.js';
import BuyAddonUsers from './parsefunction/BuyAddonUsers.js';
import ExtUserAftersave from './parsefunction/ExtUserAftersave.js';
import ExtUserAfterdelete from './parsefunction/ExtUserAfterdelete.js';
import AllowedCredits from './parsefunction/AllowedCredits.js';
import BuyCredits from './parsefunction/BuyCredits.js';
import getContact from './parsefunction/getContact.js';
import updateContactTour from './parsefunction/updateContactTour.js';
import declinedocument from './parsefunction/declinedocument.js';
import addcustomsmtp from './parsefunction/addcustomsmtp.js';
import deactivateMailAdapter from './parsefunction/deactivateMailAdapter.js';
import addFileAdapter from './parsefunction/addFileAdapter.js';
import saveToFileAdapter from './parsefunction/saveToFileAdapter.js';
import getFileAdapter from './parsefunction/getFileAdapter.js';
import addPfxFile from './parsefunction/addPfxFile.js';
import getPfxFile from './parsefunction/getPfxFile.js';
import getTenant from './parsefunction/getTenant.js';
import getSigners from './parsefunction/getSigners.js';
import saveFile from './parsefunction/saveFile.js';
import savecontact from './parsefunction/savecontact.js';
import isUserInContactBook from './parsefunction/isUserInContactBook.js';
import updateTourStatus from './parsefunction/updateTourStatus.js';
import saveTemplate from './parsefunction/saveTemplate.js';
import updateToPublicTemplate from './parsefunction/updateToPublicTemplate.js';
import updateSignatureType from './parsefunction/updatesignaturetype.js';
import updatePreferences from './parsefunction/updatePreferences.js';
import createDuplicate from './parsefunction/createDuplicate.js';
import createBatchContact from './parsefunction/createBatchContact.js';
import generateCertificatebydocId from './parsefunction/generateCertificatebydocId.js';
import fileUpload from './parsefunction/fileUpload.js';
import getUserListByOrg from './parsefunction/getUserListByOrg.js';
import editContact from './parsefunction/editContact.js';
// This afterSave function triggers after an object is added or updated in the specified class, allowing for post-processing logic.
Parse.Cloud.afterSave('contracts_Document', DocumentAftersave);
Parse.Cloud.afterSave('contracts_Contactbook', ContactbookAftersave);
Parse.Cloud.afterSave('contracts_Template', TemplateAfterSave);
Parse.Cloud.afterSave('contracts_Teams', TeamsAftersave);
Parse.Cloud.afterSave('contracts_Subscriptions', SubscriptionAftersave);
Parse.Cloud.afterSave('contracts_Users', ExtUserAftersave);
// This beforeSave function triggers before an object is added or updated in the specified class, allowing for validation or modification.
Parse.Cloud.beforeSave('contracts_Document', DocumentBeforesave);
@@ -105,16 +70,10 @@ Parse.Cloud.afterFind('contracts_Template', TemplateAfterFind);
Parse.Cloud.afterFind('contracts_Signature', SignatureAfterFind);
Parse.Cloud.afterFind('partners_Tenant', TenantAterFind);
// This afterDelete function triggers after an object get deleted.
Parse.Cloud.afterDelete('contracts_Users', ExtUserAfterdelete);
// This define function creates a custom Cloud Function that can be called from the client-side, enabling custom business logic on the server.
Parse.Cloud.define('signPdf', PDF);
Parse.Cloud.define('sendmailv3', sendmailv3);
Parse.Cloud.define('googlesign', GoogleSign);
Parse.Cloud.define('zohodetails', ZohoDetails);
Parse.Cloud.define('usersignup', usersignup);
Parse.Cloud.define('facebooksign', FacebookSign);
Parse.Cloud.define('SendOTPMailV1', sendMailOTPv1);
Parse.Cloud.define('AuthLoginAsMail', AuthLoginAsMail);
Parse.Cloud.define('getUserId', getUserId);
@@ -122,63 +81,34 @@ Parse.Cloud.define('getUserDetails', getUserDetails);
Parse.Cloud.define('getDocument', getDocument);
Parse.Cloud.define('getDrive', getDrive);
Parse.Cloud.define('getReport', getReport);
Parse.Cloud.define('generateapitoken', generateApiToken);
Parse.Cloud.define('getapitoken', getapitoken);
Parse.Cloud.define('getTemplate', GetTemplate);
Parse.Cloud.define('savewebhook', savewebhook);
Parse.Cloud.define('callwebhook', callWebhook);
Parse.Cloud.define('freesubscription', SubscribeFree);
Parse.Cloud.define('getinvoices', getInvoices);
Parse.Cloud.define('getpayments', getPayments);
Parse.Cloud.define('getsubscriptions', getSubscriptions);
Parse.Cloud.define('savesubscription', saveSubscription);
Parse.Cloud.define('verifyemail', VerifyEmail);
Parse.Cloud.define('encryptedpdf', encryptedpdf);
Parse.Cloud.define('getsignedurl', getSignedUrl);
Parse.Cloud.define('batchdocuments', createBatchDocs);
Parse.Cloud.define('linkcontacttodoc', linkContactToDoc);
Parse.Cloud.define('createpublictemplate', CreatePublicTemplate);
Parse.Cloud.define('getpublicusername', GetPublicUserName);
Parse.Cloud.define('getpublictemplate', GetPublicTemplate);
Parse.Cloud.define('ssosign', ssoSignin);
Parse.Cloud.define('isextenduser', isextenduser);
Parse.Cloud.define('getuserbyorg', getUserByOrg);
Parse.Cloud.define('getuserlistbyorg', getUserListByOrg);
Parse.Cloud.define('publicuserlinkcontacttodoc', PublicUserLinkContactToDoc);
Parse.Cloud.define('getlogobydomain', GetLogoByDomain);
Parse.Cloud.define('addadmin', AddAdmin);
Parse.Cloud.define('checkadminexist', CheckAdminExist);
Parse.Cloud.define('updateuserasadmin', UpdateExistUserAsAdmin);
Parse.Cloud.define('newsletter', Newsletter);
Parse.Cloud.define('getorganizations', getOrganizations);
Parse.Cloud.define('addorganization', addOrganization);
Parse.Cloud.define('updateorganization', updateOrganization);
Parse.Cloud.define('getteams', getTeams);
Parse.Cloud.define('addteam', addTeam);
Parse.Cloud.define('updateteam', updateTeam);
Parse.Cloud.define('getorgadmins', getOrgAdmins);
Parse.Cloud.define('getalluserteambyorg', getAllUserTeamByOrg);
Parse.Cloud.define('allowedusers', AllowedUsers);
Parse.Cloud.define('buyaddonusers', BuyAddonUsers);
Parse.Cloud.define('allowedcredits', AllowedCredits);
Parse.Cloud.define('buycredits', BuyCredits);
Parse.Cloud.define('getcontact', getContact);
Parse.Cloud.define('updatecontacttour', updateContactTour);
Parse.Cloud.define('declinedoc', declinedocument);
Parse.Cloud.define('addsmtp', addcustomsmtp);
Parse.Cloud.define('deactivatemailadapter', deactivateMailAdapter);
Parse.Cloud.define('savetofileadapter', saveToFileAdapter);
Parse.Cloud.define('addfileadapter', addFileAdapter);
Parse.Cloud.define('getfileadapter', getFileAdapter);
Parse.Cloud.define('addpfx', addPfxFile);
Parse.Cloud.define('getpfx', getPfxFile);
Parse.Cloud.define('gettenant', getTenant);
Parse.Cloud.define('getsigners', getSigners);
Parse.Cloud.define('savetemplate', saveTemplate);
Parse.Cloud.define('savefile', saveFile);
Parse.Cloud.define('savecontact', savecontact);
Parse.Cloud.define('isuserincontactbook', isUserInContactBook);
Parse.Cloud.define('updatetourstatus', updateTourStatus);
Parse.Cloud.define('updatetopublictemplate', updateToPublicTemplate);
Parse.Cloud.define('updatesignaturetype', updateSignatureType);
Parse.Cloud.define('updatepreferences', updatePreferences);
Parse.Cloud.define('createduplicate', createDuplicate);
Parse.Cloud.define('createbatchcontact', createBatchContact);
Parse.Cloud.define('generatecertificate', generateCertificatebydocId);
Parse.Cloud.define('fileupload', fileUpload);
Parse.Cloud.define('getuserlistbyorg', getUserListByOrg);
Parse.Cloud.define('editcontact', editContact);
@@ -5,51 +5,55 @@ const APPID = process.env.APP_ID;
const masterKEY = process.env.MASTER_KEY;
async function addTeamAndOrg(extUser) {
try {
const orgCls = new Parse.Object('contracts_Organizations');
orgCls.set('Name', extUser.Company);
orgCls.set('IsActive', true);
orgCls.set('ExtUserId', {
__type: 'Pointer',
className: 'contracts_Users',
objectId: extUser?.objectId,
});
orgCls.set('CreatedBy', {
__type: 'Pointer',
className: '_User',
objectId: extUser?.UserId?.objectId,
});
orgCls.set('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: extUser?.TenantId?.objectId,
});
const orgRes = await orgCls.save(null, { useMasterKey: true });
const teamCls = new Parse.Object('contracts_Teams');
teamCls.set('Name', 'All Users');
teamCls.set('OrganizationId', {
__type: 'Pointer',
className: 'contracts_Organizations',
objectId: orgRes.id,
});
teamCls.set('IsActive', true);
const teamRes = await teamCls.save(null, { useMasterKey: true });
const updateUser = new Parse.Object('contracts_Users');
updateUser.id = extUser.objectId;
updateUser.set('UserRole', 'contracts_Admin');
updateUser.set('OrganizationId', {
__type: 'Pointer',
className: 'contracts_Organizations',
objectId: orgRes.id,
});
updateUser.set('TeamIds', [
{
const extUserCls = new Parse.Query('contracts_Users');
const updateUser = await extUserCls.get(extUser.objectId, { useMasterKey: true });
if (updateUser && !updateUser?.get('OrganizationId')) {
const orgCls = new Parse.Object('contracts_Organizations');
orgCls.set('Name', extUser.Company);
orgCls.set('IsActive', true);
orgCls.set('ExtUserId', {
__type: 'Pointer',
className: 'contracts_Teams',
objectId: teamRes.id,
},
]);
const extUserRes = await updateUser.save(null, { useMasterKey: true });
className: 'contracts_Users',
objectId: extUser?.objectId,
});
orgCls.set('CreatedBy', {
__type: 'Pointer',
className: '_User',
objectId: extUser?.UserId?.objectId,
});
orgCls.set('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: extUser?.TenantId?.objectId,
});
const orgRes = await orgCls.save(null, { useMasterKey: true });
const teamCls = new Parse.Object('contracts_Teams');
teamCls.set('Name', 'All Users');
teamCls.set('OrganizationId', {
__type: 'Pointer',
className: 'contracts_Organizations',
objectId: orgRes.id,
});
teamCls.set('IsActive', true);
const teamRes = await teamCls.save(null, { useMasterKey: true });
// const updateUser = new Parse.Object('contracts_Users');
// updateUser.id = extUser.objectId;
updateUser.set('UserRole', 'contracts_Admin');
updateUser.set('OrganizationId', {
__type: 'Pointer',
className: 'contracts_Organizations',
objectId: orgRes.id,
});
updateUser.set('TeamIds', [
{
__type: 'Pointer',
className: 'contracts_Teams',
objectId: teamRes.id,
},
]);
const extUserRes = await updateUser.save(null, { useMasterKey: true });
}
} catch (err) {
console.log('err in add team, role, org', err);
}
@@ -94,7 +98,6 @@ async function saveUser(userDetails) {
}
export default async function AddAdmin(request) {
const userDetails = request.params.userDetails;
// const subscription = request.params.subscription;
const user = await saveUser(userDetails);
try {
@@ -167,8 +170,10 @@ export default async function AddAdmin(request) {
if (userDetails && userDetails.jobTitle) {
newObj.set('JobTitle', userDetails.jobTitle);
}
if (userDetails?.timezone) {
newObj.set('Timezone', userDetails?.timezone);
}
const extRes = await newObj.save(null, { useMasterKey: true });
// if (subscription) {
const extUser = {
objectId: extRes.id,
Name: userDetails.name,
@@ -181,8 +186,6 @@ export default async function AddAdmin(request) {
JobTitle: userDetails.jobTitle,
};
await addTeamAndOrg(extUser);
// await saveSubscription(extRes.id, user.id, tenantRes.id, subscription);
// }
return { message: 'User sign up', sessionToken: user.sessionToken };
}
} catch (err) {
@@ -1,48 +0,0 @@
export default async function AllowedUsers(request) {
if (!request?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
try {
const extUser = new Parse.Query('contracts_Users');
extUser.equalTo('UserId', {
__type: 'Pointer',
className: '_User',
objectId: request.user.id,
});
const resExtUser = await extUser.first({ useMasterKey: true });
if (resExtUser) {
const _resExtUser = JSON.parse(JSON.stringify(resExtUser));
const subscription = new Parse.Query('contracts_Subscriptions');
subscription.equalTo('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: _resExtUser.TenantId.objectId,
});
subscription.include('ExtUserPtr');
const resSub = await subscription.first({ useMasterKey: true });
if (resSub) {
const _resSub = JSON.parse(JSON.stringify(resSub));
const count = _resSub?.UsersCount || 0;
if (count > 0) {
const allowedUser = resSub.get('AllowedUsers') || 0;
const remainUsers = allowedUser - count;
if (remainUsers > 0) {
return remainUsers;
} else {
return 0;
}
} else {
const alloweduser = resSub.get('AllowedUsers') || 0;
return alloweduser;
}
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
}
} catch (err) {
console.log('err in allowedusers', err);
const code = err?.code || 400;
const msg = err?.message || 'Something went wrong.';
throw new Parse.Error(code, msg);
}
}
@@ -1,69 +0,0 @@
import { parseJwt } from '../../Utils.js';
import jwt from 'jsonwebtoken';
async function checkCredits(userId) {
const extUser = new Parse.Query('contracts_Users');
extUser.equalTo('UserId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
const resExtUser = await extUser.first({ useMasterKey: true });
if (resExtUser) {
const _resExtUser = JSON.parse(JSON.stringify(resExtUser));
const subscription = new Parse.Query('contracts_Subscriptions');
subscription.equalTo('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: _resExtUser.TenantId.objectId,
});
subscription.include('ExtUserPtr');
const resSub = await subscription.first({ useMasterKey: true });
if (resSub) {
const _resSub = JSON.parse(JSON.stringify(resSub));
const AllowedCredits = _resSub?.AllowedCredits || 0;
const AddonCredits = _resSub?.AddonCredits || 0;
return { allowedcredits: AllowedCredits, addoncredits: AddonCredits };
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
}
}
export default async function AllowedCredits(request) {
const jwttoken = request?.headers?.jwttoken || '';
try {
if (request?.user) {
return await checkCredits(request.user.id);
} else if (jwttoken) {
const jwtDecode = parseJwt(jwttoken);
if (jwtDecode?.user_email) {
const userCls = new Parse.Query(Parse.User);
userCls.equalTo('email', jwtDecode?.user_email);
const userRes = await userCls.first({ useMasterKey: true });
const userId = userRes?.id;
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('userId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
const appRes = await tokenQuery.first({ useMasterKey: true });
const decoded = jwt.verify(jwttoken, appRes?.get('token'));
if (decoded?.user_email) {
return await checkCredits(userId);
} else {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid token.');
}
} else {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid token.');
}
} else {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
} catch (err) {
console.log('err in allowedCredits', err);
const code = err?.code || 400;
const msg = err?.message || 'Something went wrong.';
throw new Parse.Error(code, msg);
}
}
@@ -1,121 +0,0 @@
import axios from 'axios';
import { planCredits } from '../../Utils.js';
export default async function BuyAddonUsers(request) {
const users = request.params.users;
if (!request?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
if (users) {
try {
const extUser = new Parse.Query('contracts_Users');
extUser.equalTo('UserId', {
__type: 'Pointer',
className: '_User',
objectId: request.user.id,
});
const resExtUser = await extUser.first({ useMasterKey: true });
if (resExtUser) {
const _resExtUser = JSON.parse(JSON.stringify(resExtUser));
const subscription = new Parse.Query('contracts_Subscriptions');
subscription.equalTo('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: _resExtUser.TenantId.objectId,
});
subscription.include('ExtUserPtr');
const resSub = await subscription.first({ useMasterKey: true });
if (resSub) {
const _resSub = JSON.parse(JSON.stringify(resSub));
// Define the URL
const url = 'https://accounts.zoho.in/oauth/v2/token';
// Convert the data to x-www-form-urlencoded format
const formData = new URLSearchParams();
formData.append('refresh_token', process.env.ZOHO_REFRESH_TOKEN);
formData.append('client_id', process.env.ZOHO_CLIENT_ID);
formData.append('client_secret', process.env.ZOHO_CLIENT_SECRET);
formData.append('redirect_uri', process.env.ZOHO_REDIRECT_URI);
formData.append('grant_type', 'refresh_token');
const headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
// Make the POST request using Axios
const res = await axios.post(url, formData, { headers });
if (res.data.access_token) {
const subscriptionId = _resSub.SubscriptionId;
const price = _resSub?.SubscriptionDetails?.data?.subscription?.plan?.price;
const plan_code = _resSub?.SubscriptionDetails?.data?.subscription?.plan?.plan_code;
const addonsArr = _resSub?.SubscriptionDetails?.data?.subscription?.addons || [];
let addon = 0;
if (addonsArr?.length > 0) {
let allowedUsersMonthly = 0;
let allowedUsersYearly = 0;
addonsArr?.forEach(item => {
if (item.addon_code === 'extra-teams-users-monthly') {
allowedUsersMonthly += item.quantity;
} else if (item.addon_code === 'extra-teams-users-yearly') {
allowedUsersYearly += item.quantity;
} else if (item.addon_code === 'extra-users') {
allowedUsersMonthly += item.quantity;
}
});
if (allowedUsersMonthly > 0 || allowedUsersYearly > 0) {
addon = allowedUsersMonthly + allowedUsersYearly;
}
}
const quantity = parseInt(users) + parseInt(addon);
const addoncode = plan_code.includes('yearly')
? 'extra-teams-users-yearly'
: 'extra-teams-users-monthly';
const data = JSON.stringify({
plan: { plan_code: plan_code },
addons: [
{
addon_code: addoncode,
addon_description: 'Extra users',
price: price,
quantity: quantity,
},
],
});
const updatedSubscription = await axios.put(
'https://www.zohoapis.in/billing/v1/subscriptions/' + subscriptionId,
data,
{
headers: {
Authorization: 'Zoho-oauthtoken ' + res.data.access_token,
'X-com-zoho-subscriptions-organizationid': process.env.ZOHO_BILLING_ORG_ID,
},
}
);
const subscriptionInfo = { data: updatedSubscription.data };
const allowedUsers = quantity + 1;
const existAllowedCredits = _resSub?.AllowedCredits || 0;
const credits = resSub?.PlanCredits || planCredits[plan_code];
const newAllowedCredits = users * credits;
const totalCredits = existAllowedCredits + newAllowedCredits;
const updateSub = new Parse.Object('contracts_Subscriptions');
updateSub.id = resSub.id;
updateSub.set('SubscriptionDetails', subscriptionInfo);
updateSub.set('AllowedUsers', allowedUsers);
updateSub.set('AllowedCredits', totalCredits);
const resupdateSub = await updateSub.save(null, { useMasterKey: true });
return { status: 'success', addon: allowedUsers };
} else {
throw new Parse.Error('400', 'Invalid access token.');
}
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
}
} catch (err) {
const code = err?.response?.data?.code || err?.response?.status || err?.code || 400;
const msg =
err?.response?.data?.error ||
err?.response?.data ||
err?.message ||
'Something went wrong.';
console.log('err in buyaddon', code, msg);
throw new Parse.Error(code, msg);
}
}
}
@@ -1,92 +0,0 @@
import axios from 'axios';
export default async function BuyCredits(request) {
const credits = request.params.credits;
if (!request?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
if (credits) {
try {
const extUser = new Parse.Query('contracts_Users');
extUser.equalTo('UserId', {
__type: 'Pointer',
className: '_User',
objectId: request.user.id,
});
const resExtUser = await extUser.first({ useMasterKey: true });
if (resExtUser) {
const _resExtUser = JSON.parse(JSON.stringify(resExtUser));
const subscription = new Parse.Query('contracts_Subscriptions');
subscription.equalTo('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: _resExtUser.TenantId.objectId,
});
subscription.include('ExtUserPtr');
const resSub = await subscription.first({ useMasterKey: true });
if (resSub) {
const _resSub = JSON.parse(JSON.stringify(resSub));
// Define the URL
const url = 'https://accounts.zoho.in/oauth/v2/token';
// Convert the data to x-www-form-urlencoded format
const formData = new URLSearchParams();
formData.append('refresh_token', process.env.ZOHO_REFRESH_TOKEN);
formData.append('client_id', process.env.ZOHO_CLIENT_ID);
formData.append('client_secret', process.env.ZOHO_CLIENT_SECRET);
formData.append('redirect_uri', process.env.ZOHO_REDIRECT_URI);
formData.append('grant_type', 'refresh_token');
const headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
// Make the POST request using Axios
const res = await axios.post(url, formData, { headers });
if (res.data.access_token) {
const subscriptionId = _resSub.SubscriptionId;
const plan_code = _resSub?.SubscriptionDetails?.data?.subscription?.plan?.plan_code;
const quantity = parseInt(credits);
const addoncode = 'addon-credits';
const data = JSON.stringify({
plan: { plan_code: plan_code },
addons: [
{ addon_code: addoncode, addon_description: 'addon credits', quantity: quantity },
],
});
const creditsUrl = `https://www.zohoapis.in/billing/v1/subscriptions/${subscriptionId}/buyonetimeaddon`;
const resCredits = await axios.post(creditsUrl, data, {
headers: {
Authorization: 'Zoho-oauthtoken ' + res.data.access_token,
'X-com-zoho-subscriptions-organizationid': process.env.ZOHO_BILLING_ORG_ID,
},
});
// console.log('resCredits ', resCredits.data);
if (resCredits.data) {
const existAddonCredits = _resSub?.AddonCredits ? _resSub.AddonCredits : 0;
const addonCredits = existAddonCredits + quantity;
const updateSub = new Parse.Object('contracts_Subscriptions');
updateSub.id = resSub.id;
updateSub.set('AddonCredits', addonCredits);
const resupdateSub = await updateSub.save(null, { useMasterKey: true });
// console.log('resupdateSub ', resupdateSub);
return { status: 'success', addon: quantity };
}
} else {
throw new Parse.Error('400', 'Invalid access token.');
}
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
}
} catch (err) {
const code = err?.response?.data?.code || err?.response?.status || err?.code || 400;
const msg =
err?.response?.data?.error ||
err?.response?.data ||
err?.message ||
'Something went wrong.';
console.log('err in Buyaddonusers', code, msg);
throw new Parse.Error(code, msg);
}
} else {
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide parameters.');
}
}
@@ -3,22 +3,66 @@ async function ContactbookAftersave(request) {
you can check as follows */
if (!request.original) {
const user = request.user;
// Retrieve the current ACL
const acl = new Parse.ACL();
// Ensure the current user has read access
if (acl && request?.user) {
const object = request.object;
acl.setReadAccess(user, true);
acl.setWriteAccess(user, true);
acl.setReadAccess(object.get('UserId'), true);
acl.setWriteAccess(object.get('UserId'), true);
const object = request.object;
if (object.get('UserId')) {
// Retrieve the current ACL
const acl = new Parse.ACL();
// Ensure the current user has read access
if (acl && request?.user) {
const object = request.object;
acl.setReadAccess(user, true);
acl.setWriteAccess(user, true);
acl.setReadAccess(object.get('UserId'), true);
acl.setWriteAccess(object.get('UserId'), true);
object.setACL(acl);
object.set('IsDeleted', false)
// Continue saving the object
return object.save(null, { useMasterKey: true });
object.setACL(acl);
object.set('IsDeleted', false);
// Continue saving the object
return object.save(null, { useMasterKey: true });
}
} else {
const Name = object.get('Name');
const Email = object.get('Email');
const Phone = object.get('Phone');
try {
const _users = Parse.Object.extend('User');
const _user = new _users();
_user.set('name', Name);
_user.set('username', Email);
_user.set('email', Email);
_user.set('password', Email);
if (Email) {
_user.set('phone', Phone);
}
const user = await _user.save();
if (user) {
object.set('UserId', user);
const acl = object.getACL() || new Parse.ACL();
acl.setReadAccess(user.id, true);
acl.setWriteAccess(user.id, true);
object.setACL(acl);
await object.save(null, { useMasterKey: true });
// console.log('res update new user with contac', res);
}
} catch (err) {
// console.log('err ', err);
if (err.code === 202) {
const userQuery = new Parse.Query(Parse.User);
userQuery.equalTo('email', Email);
const userRes = await userQuery.first({ useMasterKey: true });
object.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: userRes.id,
});
const acl = object.getACL() || new Parse.ACL();
acl.setReadAccess(userRes.id, true);
acl.setWriteAccess(userRes.id, true);
object.setACL(acl);
await object.save(null, { useMasterKey: true });
// console.log('res update existing user with contact', res);
}
}
}
} else {
console.log('Object being update');
@@ -1,41 +0,0 @@
export default async function CreatePublicTemplate(request) {
const templateid = request.params.templateid;
const ispublic = request.params.ispublic;
const publicrole = request.params.publicrole;
try {
if (!request?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
} else {
const userId = request?.user && request?.user?.id;
if (templateid) {
const updateTemplate = new Parse.Object('contracts_Template');
updateTemplate.id = templateid;
if (ispublic) {
updateTemplate.set('PublicRole', publicrole);
}
updateTemplate.set('IsPublic', ispublic);
const Acl = new Parse.ACL();
if (ispublic) {
Acl.setPublicReadAccess(true);
}
Acl.setReadAccess(userId, true);
Acl.setWriteAccess(userId, true);
updateTemplate.setACL(Acl);
const savedObject = await updateTemplate.save(null, { useMasterKey: true });
const res = savedObject.toJSON();
if (res) {
return {
status: 'success',
};
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Please provide required parameters!');
}
}
} catch (err) {
const code = err.code || 400;
const msg = err.message;
const error = new Parse.Error(code, msg);
throw error;
}
}
@@ -1,31 +1,53 @@
import { useLocal } from '../../Utils.js';
import getPresignedUrl from './getSignedUrl.js';
import getPresignedUrl, { presignedlocalUrl } from './getSignedUrl.js';
async function DocumentAfterFind(request) {
if (request.objects.length === 1) {
if (request.objects) {
const obj = request.objects[0];
const FileAdapterId = obj?.get('FileAdapterId') || '';
if (FileAdapterId || useLocal !== 'true') {
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');
let fileAdapter = {};
if (FileAdapterId) {
const tenantId = obj?.get('ExtUserPtr')?.get('TenantId');
if (tenantId) {
const _tenantId = JSON.parse(JSON.stringify(obj?.get('ExtUserPtr')?.get('TenantId')));
fileAdapter = _tenantId?.FileAdapters?.find(x => x.id === FileAdapterId) || {};
}
}
if (SignedUrl) {
obj.set('SignedUrl', getPresignedUrl(SignedUrl, fileAdapter));
obj.set(
'SignedUrl',
getPresignedUrl(
SignedUrl,
)
);
}
if (Url) {
obj.set('URL', getPresignedUrl(Url, fileAdapter));
obj.set(
'URL',
getPresignedUrl(
Url,
)
);
}
if (certificateUrl) {
obj.set('CertificateUrl', getPresignedUrl(certificateUrl, fileAdapter));
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 (SignedUrl) {
obj.set('SignedUrl', presignedlocalUrl(SignedUrl));
}
if (Url) {
obj.set('URL', presignedlocalUrl(Url));
}
if (certificateUrl) {
obj.set('CertificateUrl', presignedlocalUrl(certificateUrl));
}
return [obj];
}
@@ -1,31 +0,0 @@
async function ExtUserAfterdelete(request) {
try {
const extUser = request.object;
const tenantId = extUser.get('TenantId')?.id;
const subscription = new Parse.Query('contracts_Subscriptions');
subscription.equalTo('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: tenantId,
});
const resSub = await subscription.first({ useMasterKey: true });
if (resSub) {
const updateSub = new Parse.Object('contracts_Subscriptions');
updateSub.id = resSub.id;
if (resSub?.get('UsersCount') > 0) {
updateSub.decrement('UsersCount', 1);
} else {
updateSub.decrement('UsersCount', 0);
}
updateSub.save(null, { useMasterKey: true });
} else {
const addSub = new Parse.Object('contracts_Subscriptions');
addSub.set('UsersCount', 0);
addSub.save(null, { useMasterKey: true });
}
} catch (err) {
console.log('err in extuser afterdelete', err);
}
}
export default ExtUserAfterdelete;
@@ -1,44 +0,0 @@
async function ExtUserAftersave(request) {
try {
if (!request.original) {
const extUser = request.object;
const tenantId = extUser.get('TenantId')?.id;
const subscription = new Parse.Query('contracts_Subscriptions');
subscription.equalTo('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: tenantId,
});
const resSub = await subscription.first({ useMasterKey: true });
if (resSub) {
const updateSub = new Parse.Object('contracts_Subscriptions');
updateSub.id = resSub.id;
updateSub.increment('UsersCount', 1);
await updateSub.save(null, { useMasterKey: true });
} else {
const addSub = new Parse.Object('contracts_Subscriptions');
addSub.set('UsersCount', 1);
addSub.set('ExtUserPtr', {
__type: 'Pointer',
className: 'contracts_Users',
objectId: extUser.id,
});
addSub.set('CreatedBy', {
__type: 'Pointer',
className: '_User',
objectId: extUser.get('UserId').id,
});
addSub.set('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: extUser.get('TenantId').id,
});
await addSub.save(null, { useMasterKey: true });
}
}
} catch (err) {
console.log('err in extuser aftersave', err);
}
}
export default ExtUserAftersave;
@@ -1,93 +0,0 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const APPID = process.env.APP_ID;
const masterKEY = process.env.MASTER_KEY;
/**
* FacebookSign is function which is used to sign up/sign in with google
* @param Id It is google Id
* @param AccessToken It is facebook Access Token
* @param Email It is user's email with user sign in/sign up
* @param Phone It is user's Phone number
* @param Name It is user's Name
* @returns if success {email, message, sessiontoken} else on reject {message}
*/
export default async function FacebookSign(request) {
const userGoogleId = request.params.Id;
const userAccessToken = request.params.AccessToken;
const userEmail = request.params.Email;
const phone = request.params?.Phone || '';
const name = request.params.Name;
const authData = {
facebook: { id: userGoogleId, access_token: userAccessToken },
};
const userQuery = new Parse.Query(Parse.User);
userQuery.equalTo('email', userEmail);
const res = await userQuery.first({ useMasterKey: true });
if (res) {
try {
const SignIn = await axios.put(
serverUrl + '/users/' + res.id,
{ authData: authData },
{
headers: {
'X-Parse-Application-Id': APPID,
'X-Parse-Master-key': masterKEY,
},
}
);
if (SignIn.data) {
// console.log("google Sign in", SignIn);
const sessiontoken = SignIn.data.sessionToken;
console.log('Google sessiontoken', sessiontoken);
return {
email: userEmail,
message: 'User Sign In',
sessiontoken: sessiontoken,
};
}
} catch (err) {
console.log('err in user google sign in', err);
return { message: 'Internal server error' };
}
} else {
// console.log("in sign up condition");
try {
const SignUp = await axios.post(
serverUrl + '/users',
{
authData: authData,
username: userEmail,
email: userEmail,
phone: phone,
name: name,
},
{
headers: {
'X-Parse-Application-Id': APPID,
'X-Parse-Revocable-Session': '1',
},
}
);
// console.log("SignUp", SignUp);
if (SignUp.data) {
const sessiontoken = SignUp.data.sessionToken;
const payload = {
email: userEmail,
message: 'User Sign Up',
sessiontoken: sessiontoken,
};
return payload;
}
} catch (err) {
console.log('err in user google sign up', err);
return { message: 'Internal server err' };
}
}
}
@@ -1,51 +0,0 @@
async function GetPublicTemplate(request) {
try {
const username = request.params.username;
if (username) {
const extUserQuery = new Parse.Query('contracts_Users');
extUserQuery.equalTo('UserName', username);
const userRes = await extUserQuery.first({ useMasterKey: true });
const userId = userRes.get('UserId').id;
//get _user details
const userQuery = new Parse.Query(Parse.User);
const user = await userQuery.get(userId, { useMasterKey: true });
if (userId) {
const templatQuery = new Parse.Query('contracts_Template');
templatQuery.equalTo('CreatedBy', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
templatQuery.descending('updatedAt');
templatQuery.equalTo('IsPublic', true);
templatQuery.notEqualTo('IsArchive', true);
templatQuery.include('ExtUserPtr.TenantId');
const getTemplate = await templatQuery.find({ useMasterKey: true });
const extcls = new Parse.Query('contracts_Users');
extcls.equalTo('Email', user.get('email'));
const res = await extcls.first({ useMasterKey: true });
if (res) {
const _res = JSON.parse(JSON.stringify(res));
return {
template: getTemplate,
user: user,
extend_User: { Tagline: _res?.Tagline || '', SearchIndex: _res?.SearchIndex || '' },
};
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Template not found');
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User does not exist');
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Please provide required parameters!');
}
} catch (err) {
const code = err.code || 400;
const msg = err.message;
const error = new Parse.Error(code, msg);
throw error;
}
}
export default GetPublicTemplate;
@@ -1,23 +0,0 @@
async function GetPublicUserName(request) {
try {
if (!request?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
} else {
const username = request.params.username;
if (username) {
const userQuery = new Parse.Query('contracts_Users');
userQuery.equalTo('UserName', username);
const res = await userQuery.first({ useMasterKey: true });
return res;
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Please provide required parameters!');
}
}
} catch (err) {
const code = err.code || 400;
const msg = err.message;
const error = new Parse.Error(code, msg);
throw error;
}
}
export default GetPublicUserName;
@@ -1,45 +1,17 @@
import axios from 'axios';
import { cloudServerUrl, parseJwt } from '../../Utils.js';
import jwt from 'jsonwebtoken';
import {
cloudServerUrl,
} from '../../Utils.js';
export default async function GetTemplate(request) {
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const templateId = request.params.templateId;
const ispublic = request.params.ispublic;
const jwttoken = request.headers?.jwttoken;
const sessiontoken = request.headers?.sessiontoken;
try {
if (!ispublic) {
let userEmail;
if (jwttoken) {
try {
const jwtDecode = parseJwt(jwttoken);
if (jwtDecode?.user_email) {
const userCls = new Parse.Query(Parse.User);
userCls.equalTo('email', jwtDecode?.user_email);
const userRes = await userCls.first({ useMasterKey: true });
const userId = userRes?.id;
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('userId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
const appRes = await tokenQuery.first({ useMasterKey: true });
const decoded = jwt.verify(jwttoken, appRes?.get('token'));
if (decoded?.user_email) {
userEmail = decoded?.user_email;
} else {
return { error: 'Invalid token!' };
}
} else {
return { error: 'Invalid token!' };
}
} catch (err) {
console.log('err in jwt', err);
return { error: "You don't have access of this document!" };
}
} else if (sessiontoken) {
if (sessiontoken) {
const userRes = await axios.get(serverUrl + '/users/me', {
headers: {
'X-Parse-Application-Id': process.env.APP_ID,
@@ -56,6 +28,7 @@ export default async function GetTemplate(request) {
template.include('Signers');
template.include('CreatedBy');
template.include('ExtUserPtr.TenantId');
template.include('Bcc');
const extUserQuery = new Parse.Query('contracts_Users');
extUserQuery.equalTo('Email', userEmail);
extUserQuery.include('TeamIds');
@@ -83,6 +56,7 @@ export default async function GetTemplate(request) {
template.include('CreatedBy');
template.include('ExtUserPtr.TenantId');
template.include('Placeholders.signerPtr');
template.include('Bcc');
}
}
const res = await template.first({ useMasterKey: true });
@@ -109,6 +83,7 @@ export default async function GetTemplate(request) {
template.include('Signers');
template.include('CreatedBy');
template.include('ExtUserPtr.TenantId');
template.include('Bcc');
const res = await template.first({ useMasterKey: true });
// console.log("res ", res)
if (res) {
@@ -1,100 +0,0 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const APPID = process.env.APP_ID;
const masterKEY = process.env.MASTER_KEY;
/**
* GoogleSign is function which is used to sign up/sign in with google
* @param Id It is google Id
* @param TokenId It is google token Id
* @param Gmail It is user's gmail with user sign in/sign up
* @param Phone It is user's Phone number
* @param Name It is user's Name
* @returns if success {email, message, sessiontoken} else on reject {message}
*/
export default async function GoogleSign(request) {
const userGoogleId = request.params.Id;
const userTokenId = request.params.TokenId;
const userEmail = request.params.Gmail;
const phone = request.params?.Phone || '';
const name = request.params.Name;
const extUserId = request.params?.extUserId || '';
const authData = { google: { id: userGoogleId, id_token: userTokenId } };
const userQuery = new Parse.Query(Parse.User);
userQuery.equalTo('email', userEmail);
const res = await userQuery.first({ useMasterKey: true });
if (res) {
if (extUserId) {
const userQuery = new Parse.Query('contracts_Users');
const resExtUser = await userQuery.get(extUserId, { useMasterKey: true });
const _resExtUser = JSON.parse(JSON.stringify(resExtUser));
try {
const SignIn = await axios.put(
serverUrl + '/users/' + res.id,
{ authData: authData },
{
headers: {
'X-Parse-Application-Id': APPID,
'X-Parse-Master-key': masterKEY,
},
}
);
if (SignIn.data) {
// console.log("google Sign in", SignIn);
const sessiontoken = SignIn.data.sessionToken;
return {
email: userEmail,
phone: _resExtUser?.Phone || '',
company: _resExtUser?.Company,
message: 'User Sign In',
sessiontoken: sessiontoken,
};
}
} catch (err) {
console.log('err in user google sign in', err);
return { message: 'Internal server error' };
}
} else {
return { message: 'Internal server err' };
}
} else {
// console.log("in sign up condition");
try {
const SignUp = await axios.post(
serverUrl + '/users',
{
authData: authData,
username: userEmail,
email: userEmail,
phone: phone,
name: name,
},
{
headers: {
'X-Parse-Application-Id': APPID,
'X-Parse-Revocable-Session': '1',
},
}
);
// console.log("SignUp", SignUp);
if (SignUp.data) {
const sessiontoken = SignUp.data.sessionToken;
const payload = {
email: userEmail,
message: 'User Sign Up',
sessiontoken: sessiontoken,
};
return payload;
}
} catch (err) {
console.log('err in user google sign up', err);
return { message: 'Internal server err' };
}
}
}
@@ -4,12 +4,11 @@ export default async function Newsletter(request) {
const email = request.params.email;
const domain = request.params.domain;
try {
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': 'legadranaxn',
};
const envAppId = process.env.REACT_APP_APPID || 'legadranaxn';
const headers = { 'Content-Type': 'application/json', 'X-Parse-Application-Id': envAppId };
const envProdServer = process.env.REACT_APP_SERVERURL || 'https://app.opensignlabs.com/api/app';
const newsletter = await axios.post(
'https://app.opensignlabs.com/api/app/classes/Newsletter',
`${envProdServer}/classes/Newsletter`,
{ Name: name, Email: email, Domain: domain },
{ headers: headers }
);
@@ -1,456 +0,0 @@
import { replaceMailVaribles } from '../../Utils.js';
// `saveRoleContact` is used to save user in contracts_Guest role and create contact
const saveRoleContact = async contact => {
try {
const Role = new Parse.Query(Parse.Role);
const guestRole = await Role.equalTo('name', 'contracts_Guest').first();
if (guestRole) {
// Check if the user is already in the role
const relation = guestRole.relation('users');
const usersInRoleQuery = relation.query();
usersInRoleQuery.equalTo('objectId', contact.UserId.objectId);
const usersInRole = await usersInRoleQuery.find();
if (usersInRole.length > 0) {
console.log('User already added to Guest role.');
} else {
relation.add({ __type: 'Pointer', className: '_User', id: contact.UserId.objectId });
await guestRole.save(null, { useMasterKey: true });
// console.log('User added to Guest role successfully.');
}
}
} catch (err) {
console.log('err in role save', err);
}
const contactQuery = new Parse.Object('contracts_Contactbook');
contactQuery.set('Name', contact.Name);
contactQuery.set('Email', contact.Email);
if (contact?.Phone) {
contactQuery.set('Phone', contact.Phone);
}
contactQuery.set('CreatedBy', contact.CreatedBy);
contactQuery.set('UserId', contact.UserId);
contactQuery.set('UserRole', 'contracts_Guest');
if (contact?.TenantId) {
contactQuery.set('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: contact.TenantId,
});
}
contactQuery.set('IsDeleted', false);
const acl = new Parse.ACL();
acl.setReadAccess(contact.CreatedBy.objectId, true);
acl.setWriteAccess(contact.CreatedBy.objectId, true);
acl.setReadAccess(contact.UserId.objectId, true);
acl.setWriteAccess(contact.UserId.objectId, true);
contactQuery.setACL(acl);
const contactRes = await contactQuery.save();
if (contactRes) {
return contactRes;
}
};
// `createDocumentFromTemplate` is used to create document from template
const createDocumentFromTemplate = async (template, existContact, index) => {
try {
if (template) {
//update contact in placeholder, signers and update ACl in provide document
const object = new Parse.Object('contracts_Document');
object.set('Name', template?.Name);
object.set('Description', template?.Description);
object.set('Note', template?.Note);
object.set('TimeToCompleteDays', template?.TimeToCompleteDays || 15);
object.set('SendinOrder', template?.SendinOrder || false);
object.set('AutomaticReminders', template?.AutomaticReminders || false);
object.set('RemindOnceInEvery', template?.RemindOnceInEvery || 5);
object.set('URL', template?.URL);
object.set('CreatedBy', template?.CreatedBy);
object.set('ExtUserPtr', template?.ExtUserPtr);
object.set('OriginIp', template?.OriginIp || '');
object.set('IsEnableOTP', template?.IsEnableOTP || false);
object.set('IsTourEnabled', template?.IsTourEnabled || false);
object.set('FileAdapterId', template?.FileAdapterId || '');
if (template?.SignatureType?.length > 0) {
object.set('SignatureType', template?.SignatureType);
}
if (template?.NotifyOnSignatures) {
object.set('NotifyOnSignatures', template?.NotifyOnSignatures);
}
let signers = template?.Signers || [];
const signerobj = {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: existContact.id,
};
signers = [...signers.slice(0, index), signerobj, ...signers.slice(index)];
object.set('Signers', signers);
object.set('SignedUrl', template.URL || template.SignedUrl);
const Placeholders = template?.Placeholders || [];
Placeholders[index] = {
...Placeholders[index],
signerObjId: existContact.id,
signerPtr: {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: existContact.id,
},
};
object.set('Placeholders', Placeholders);
object.set('SendMail', true);
const resDoc = await object.save(null, { useMasterKey: true });
return resDoc;
}
} catch (err) {
console.log('err in create document from template', err);
}
};
//`sendMailToAllSigners` is used to send email to all signers at a time if send-in-order false
const sendMailToAllSigners = async docId => {
try {
//get document details that recenlty created from public template
const docQuery = new Parse.Query('contracts_Document');
docQuery.include('ExtUserPtr,ExtUserPtr.TenantId');
docQuery.include('Signers');
const docRes = await docQuery.get(docId, { useMasterKey: true });
const Doc = JSON.parse(JSON.stringify(docRes));
const templateOwnerUserId = Doc?.CreatedBy?.objectId;
const tenantCreditsQuery = new Parse.Query('partners_Tenant');
tenantCreditsQuery.equalTo('UserId', {
__type: 'Pointer',
className: '_User',
objectId: templateOwnerUserId,
});
const res = await tenantCreditsQuery.first();
if (res) {
const existUserId = Doc?.ExtUserPtr?.objectId;
try {
const getSubscriptionDetails = await Parse.Cloud.run('getsubscriptions', {
extUserId: existUserId,
ispublic: true,
});
if (getSubscriptionDetails) {
const tenantRes = JSON.parse(JSON.stringify(res));
const extUserDetails = Doc?.ExtUserPtr;
const signerMail = Doc?.Signers;
const requestBody = tenantRes?.RequestBody;
const requestSubject = tenantRes?.RequestSubject;
const subscription_json = JSON.parse(JSON.stringify(getSubscriptionDetails));
const billingDate =
subscription_json?.result?.Next_billing_date &&
subscription_json?.result?.Next_billing_date?.iso;
const isSubscribed = billingDate ? new Date(billingDate) > new Date() : false;
for (let i = 0; i < signerMail.length; i++) {
try {
const senderEmail = Doc?.ExtUserPtr?.Email;
const senderPhone = Doc?.ExtUserPtr?.Phone;
const expireDate = Doc?.ExpiryDate?.iso || 15;
const newDate = new Date(expireDate);
const localExpireDate = newDate.toLocaleDateString('en-US', {
day: 'numeric',
month: 'long',
year: 'numeric',
});
const imgPng = 'https://qikinnovation.ams3.digitaloceanspaces.com/logo.png';
const objectId = signerMail[i].objectId;
const hostPublicUrl = 'https://app.opensignlabs.com';
//encode this url value `${Doc.objectId}/${signerMail[i].Email}/${objectId}` to base64 using `btoa` function
const encodeBase64 = btoa(`${Doc?.objectId}/${signerMail[i].Email}/${objectId}`);
let signPdf = `${hostPublicUrl}/login/${encodeBase64}`;
const openSignUrl = 'https://www.opensignlabs.com/';
const orgName = Doc?.ExtUserPtr?.Company || '';
const themeBGcolor = '#47a3ad';
const senderName = `${Doc?.ExtUserPtr.Name}`;
const documentName = `${Doc?.Name}`;
let replaceVar;
if (isSubscribed && requestBody && requestSubject) {
const replacedRequestBody = requestBody.replace(/"/g, "'");
htmlReqBody =
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>" +
replacedRequestBody +
'</body> </html>';
const variables = {
document_title: documentName,
sender_name: senderName,
sender_mail: senderEmail,
sender_phone: senderPhone || '',
receiver_name: signerMail[i].Name,
receiver_email: signerMail[i].Email,
receiver_phone: signerMail[i]?.Phone || '',
expiry_date: localExpireDate,
company_name: orgName,
signing_url: `<a href=${signPdf} target=_blank>Sign here</a>`,
};
replaceVar = replaceMailVaribles(requestSubject, htmlReqBody, variables);
}
let params = {
mailProvider: extUserDetails?.activeMailAdapter,
extUserId: existUserId,
recipient: signerMail[i].Email,
subject:
replaceVar?.subject ||
`${senderName} has requested you to sign "${documentName}"`,
from: senderEmail,
html:
replaceVar?.body ||
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /> </head> <body> <div style='background-color: #f5f5f5; padding: 20px'=> <div style=' box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background: white;padding-bottom: 20px;'> <div style='padding:10px 10px 0 10px'><img src=" +
imgPng +
" height='50' style='padding: 20px,width:170px,height:40px' /></div> <div style=' padding: 2px;font-family: system-ui;background-color:" +
themeBGcolor +
";'><p style='font-size: 20px;font-weight: 400;color: white;padding-left: 20px;' > Digital Signature Request</p></div><div><p style='padding: 20px;font-family: system-ui;font-size: 14px; margin-bottom: 10px;'> " +
Doc?.ExtUserPtr.Name +
' has requested you to review and sign <strong> ' +
Doc?.Name +
"</strong>.</p><div style='padding: 5px 0px 5px 25px;display: flex;flex-direction: row;justify-content: space-around;'><table> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Sender</td> <td> </td> <td style='color:#626363;font-weight:bold'>" +
senderEmail +
"</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Organization</td> <td> </td><td style='color:#626363;font-weight:bold'> " +
orgName +
"</td></tr> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expire on</td><td> </td> <td style='color:#626363;font-weight:bold'>" +
localExpireDate +
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a href=" +
signPdf +
"> <button style='padding: 12px 12px 12px 12px;background-color: #d46b0f;color: white; border: 0px;box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;font-weight:bold;margin-top:30px'>Sign here</button></a> </div> <div style='display: flex; justify-content: center;margin-top: 10px;'> </div></div></div><div><p> This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " +
senderEmail +
' directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href= ' +
openSignUrl +
' target=_blank>here</a>.</p> </div></div></body> </html>',
};
await Parse.Cloud.run('sendmailv3', params);
} catch (error) {
console.log('error', error);
}
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
}
} catch (e) {
console.log('error in get partners_Tenant class details', err);
}
}
} catch (e) {
console.log('error in sendMailToAllSigners function', err);
}
};
const deductcount = async _resSub => {
try {
const subscriptionCls = new Parse.Object('contracts_Subscriptions');
subscriptionCls.id = _resSub.objectId;
const allowedCredits = _resSub?.AllowedCredits || 0;
const addonCredits = _resSub?.AddonCredits || 0;
if (allowedCredits > 0) {
const updateAllowedcredits = allowedCredits - 1 || 0;
subscriptionCls.set('AllowedCredits', updateAllowedcredits);
} else {
const updateAddonCredits = addonCredits > 0 ? addonCredits - 1 : 0;
subscriptionCls.set('AddonCredits', updateAddonCredits);
}
await subscriptionCls.save(null, { useMasterKey: true });
} catch (err) {
console.log('Err in deductcount in PublicUserLinkContacttoDoc cloud function', err);
}
};
// `PublicUserLinkContactToDoc` cloud function is used to create contact, add this contact in contracts_Guest role and
// create new document from template and save contact pointer in placeholder, signers and ACL of Document
export default async function PublicUserLinkContactToDoc(req) {
const email = req.params.email;
const templateid = req.params.templateid;
const signatureType = req.params.signatureType;
const name = req.params.name;
const phone = req.params.phone;
const role = req.params.role;
try {
if (templateid) {
// Execute the query to get the template with the specified 'templateid'
const docQuery = new Parse.Query('contracts_Template');
docQuery.include('ExtUserPtr');
docQuery.include('ExtUserPtr.TenantId');
const tempRes = await docQuery.get(templateid, { useMasterKey: true });
// Check if the template was found; if not, throw an error indicating the template was not found
if (!tempRes) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Template not found.');
}
const _tempRes = JSON.parse(JSON.stringify(tempRes));
const subscription = new Parse.Query('contracts_Subscriptions');
subscription.equalTo('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: _tempRes?.ExtUserPtr?.TenantId?.objectId,
});
subscription.include('ExtUserPtr');
const date = new Date();
subscription.greaterThanOrEqualTo('Next_billing_date', date);
const resSub = await subscription.first({ useMasterKey: true });
if (resSub) {
const _resSub = JSON.parse(JSON.stringify(resSub));
const allowedCredits = _resSub?.AllowedCredits || 0;
const addonCredits = _resSub?.AddonCredits || 0;
const totalcredits = allowedCredits + addonCredits;
if (totalcredits > 0) {
const Placeholders = _tempRes?.Placeholders || [];
let index;
if (role) {
index = Placeholders?.findIndex(x => x.Role && x.Role === role);
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, '');
}
if (index !== -1) {
// Execute the query to check if a contact already exists in the 'contracts_Contactbook' class
const contactCls = new Parse.Query('contracts_Contactbook');
contactCls.equalTo('Email', email);
contactCls.equalTo('CreatedBy', _tempRes.CreatedBy);
contactCls.notEqualTo('IsDeleted', true);
const existContact = await contactCls.first({ useMasterKey: true });
if (existContact) {
const template_json = JSON.parse(JSON.stringify(tempRes));
const _template_json = template_json;
_template_json.SignatureType =
signatureType?.length > 0 ? signatureType : _template_json?.SignatureType;
//update contact in placeholder, signers and update ACl in provide document
const docRes = await createDocumentFromTemplate(_template_json, existContact, index);
if (docRes) {
await deductcount(_resSub);
//condition will execute only if sendInOrder will be false for send email to all signers at a time.
if (!template_json?.SendinOrder) {
await sendMailToAllSigners(docRes.id);
}
return { contactId: existContact.id, docId: docRes.id };
}
} else {
// Execute the query to check if a user already exists in the 'contracts_Users' class
const extUserQuery = new Parse.Query('contracts_Users');
extUserQuery.equalTo('Email', email);
const extUser = await extUserQuery.first({ useMasterKey: true });
if (extUser) {
const _extUser = JSON.parse(JSON.stringify(extUser));
const contact = {
UserId: _extUser.UserId,
Name: _extUser.Name,
Email: email,
Phone: _extUser?.Phone ? _extUser.Phone : '',
CreatedBy: _tempRes.CreatedBy,
TenantId: _tempRes.ExtUserPtr?.TenantId?.objectId,
};
const template_json = JSON.parse(JSON.stringify(tempRes));
const _template_json = template_json;
_template_json.SignatureType =
signatureType?.length > 0 ? signatureType : _template_json?.SignatureType;
// if user present on platform create contact on the basis of extended user details
const contactRes = await saveRoleContact(contact);
const docRes = await createDocumentFromTemplate(_template_json, contactRes, index);
if (docRes) {
await deductcount(_resSub);
//condition will execute only if sendInOrder will be false for send email to all signers at a time.
if (!template_json?.SendinOrder) {
await sendMailToAllSigners(docRes.id);
}
return { contactId: contactRes.id, docId: docRes.id };
}
} else if (name) {
try {
// Execute the query to check if a user already exists in the '_User' class
const userQuery = new Parse.Query(Parse.User);
userQuery.equalTo('email', email);
const userRes = await userQuery.first({ useMasterKey: true });
if (userRes) {
const contact = {
UserId: { __type: 'Pointer', className: '_User', objectId: userRes.id },
Name: name,
Email: email,
Phone: phone,
CreatedBy: _tempRes.CreatedBy,
TenantId: _tempRes.ExtUserPtr?.TenantId?.objectId,
};
const template_json = JSON.parse(JSON.stringify(tempRes));
const _template_json = template_json;
_template_json.SignatureType =
signatureType?.length > 0 ? signatureType : _template_json?.SignatureType;
// Create new contract on the basis provided contact details by user and userId from _User class
const contactRes = await saveRoleContact(contact);
//update contact in placeholder, signers and update ACl in provide document
const docRes = await createDocumentFromTemplate(
_template_json,
contactRes,
index
);
if (docRes) {
await deductcount(_resSub);
//condition will execute only if sendInOrder will be false for send email to all signers at a time.
if (!template_json?.SendinOrder) {
await sendMailToAllSigners(docRes.id);
}
return { contactId: contactRes.id, docId: docRes.id };
}
} else {
// create new user in _User class on the basis of details provide by user
const _users = Parse.Object.extend('User');
const _user = new _users();
_user.set('name', name);
_user.set('username', email);
_user.set('email', email);
_user.set('password', email);
if (phone) {
_user.set('phone', phone);
}
const newUserRes = await _user.save();
const contact = {
UserId: { __type: 'Pointer', className: '_User', objectId: newUserRes.id },
Name: name,
Email: email,
Phone: phone,
CreatedBy: _tempRes.CreatedBy,
TenantId: _tempRes.ExtUserPtr?.TenantId?.objectId,
};
const template_json = JSON.parse(JSON.stringify(tempRes));
const _template_json = template_json;
_template_json.SignatureType =
signatureType?.length > 0 ? signatureType : _template_json?.SignatureType;
// Create new contract on the basis provided contact details by user and userId from _User class
const contactRes = await saveRoleContact(contact);
//update contact in placeholder, signers and update ACl in provide document
const docRes = await createDocumentFromTemplate(
_template_json,
contactRes,
index
);
if (docRes) {
await deductcount(_resSub);
//condition will execute only if sendInOrder will be false for send email to all signers at a time.
if (!template_json?.SendinOrder) {
await sendMailToAllSigners(docRes.id);
}
return { contactId: contactRes.id, docId: docRes.id };
}
}
} catch (err) {
console.log('Err', err);
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
}
}
} else {
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
'Please provide required parameters!'
);
}
} else {
throw new Parse.Error(Parse.Error.REQUEST_LIMIT_EXCEEDED, 'Insufficient Credit');
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Plan expired');
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Template not found.');
}
} catch (err) {
console.log('err in publicuserlinkcontacttodoc', err);
throw err;
}
}
@@ -1,5 +1,5 @@
import { useLocal } from '../../Utils.js';
import getPresignedUrl from './getSignedUrl.js';
import getPresignedUrl, { presignedlocalUrl } from './getSignedUrl.js';
async function SignatureAfterFind(request) {
if (useLocal !== 'true') {
@@ -17,6 +17,21 @@ async function SignatureAfterFind(request) {
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');
if (ImageURL) {
obj.set('ImageURL', presignedlocalUrl(ImageURL));
}
if (Initials) {
obj.set('Initials', presignedlocalUrl(Initials));
}
return [obj];
}
}
}
}
export default SignatureAfterFind;
@@ -1,86 +0,0 @@
export default async function SubscribeFree(request) {
const userId = request.params.userId;
const userPtr = { __type: 'Pointer', className: '_User', objectId: userId };
try {
const extQuery = new Parse.Query('contracts_Users');
extQuery.equalTo('UserId', userPtr);
const extUser = await extQuery.first({ useMasterKey: true });
if (extUser) {
const subscriptionCls = new Parse.Query('contracts_Subscriptions');
subscriptionCls.equalTo('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: extUser.get('TenantId').id,
});
subscriptionCls.descending('createdAt');
const subcripitions = await subscriptionCls.first({ useMasterKey: true });
if (subcripitions) {
if (subcripitions?.get('PlanCode') === 'freeplan') {
return { status: 'success', result: 'already subscribed!' };
} else if (subcripitions?.get('Next_billing_date') < new Date()) {
try {
const updateSubscription = new Parse.Object('contracts_Subscriptions');
updateSubscription.id = subcripitions.id;
updateSubscription.set('PlanCode', 'freeplan');
updateSubscription.set('AllowedCredits', 0);
updateSubscription.set('PlanCredits', 0);
await updateSubscription.save(null, { useMasterKey: true });
return { status: 'success', result: 'subscribed!' };
} catch (err) {
console.log('err ', err);
return { status: 'error', result: err.message };
}
} else if (subcripitions?.get('Next_billing_date') > new Date()) {
return { status: 'success', result: 'already subscribed!' };
} else {
try {
const updateSubscription = new Parse.Object('contracts_Subscriptions');
updateSubscription.id = subcripitions.id;
updateSubscription.set('PlanCode', 'freeplan');
updateSubscription.set('AllowedCredits', 0);
updateSubscription.set('PlanCredits', 0);
await updateSubscription.save(null, { useMasterKey: true });
return { status: 'success', result: 'subscribed!' };
} catch (err) {
console.log('err ', err);
return { status: 'error', result: err.message };
}
}
} else {
try {
const createSubscription = new Parse.Object('contracts_Subscriptions');
createSubscription.set('PlanCode', 'freeplan');
createSubscription.set('ExtUserPtr', {
__type: 'Pointer',
className: 'contracts_Users',
objectId: extUser.id,
});
createSubscription.set('CreatedBy', {
__type: 'Pointer',
className: '_User',
objectId: extUser.get('UserId').id,
});
if (extUser?.get('TenantId')) {
createSubscription.set('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: extUser.get('TenantId').id,
});
}
createSubscription.set('AllowedCredits', 0);
createSubscription.set('PlanCredits', 0);
await createSubscription.save(null, { useMasterKey: true });
return { status: 'success', result: 'subscribed!' };
} catch (err) {
console.log('err ', err);
return { status: 'error', result: err.message };
}
}
} else {
return { status: 'error', result: 'User not found!' };
}
} catch (err) {
console.log('err ', err);
return { status: 'error', result: err.message };
}
}
@@ -1,98 +0,0 @@
async function addTeamAndOrg(extUser) {
try {
const orgCls = new Parse.Object('contracts_Organizations');
orgCls.set('Name', extUser.Company);
orgCls.set('IsActive', true);
orgCls.set('ExtUserId', {
__type: 'Pointer',
className: 'contracts_Users',
objectId: extUser?.objectId,
});
orgCls.set('CreatedBy', {
__type: 'Pointer',
className: '_User',
objectId: extUser?.UserId?.objectId,
});
orgCls.set('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: extUser?.TenantId?.objectId,
});
const orgRes = await orgCls.save(null, { useMasterKey: true });
const teamCls = new Parse.Object('contracts_Teams');
teamCls.set('Name', 'All Users');
teamCls.set('OrganizationId', {
__type: 'Pointer',
className: 'contracts_Organizations',
objectId: orgRes.id,
});
teamCls.set('IsActive', true);
const teamRes = await teamCls.save(null, { useMasterKey: true });
const updateUser = new Parse.Object('contracts_Users');
updateUser.id = extUser.objectId;
updateUser.set('UserRole', 'contracts_Admin');
updateUser.set('OrganizationId', {
__type: 'Pointer',
className: 'contracts_Organizations',
objectId: orgRes.id,
});
updateUser.set('TeamIds', [
{
__type: 'Pointer',
className: 'contracts_Teams',
objectId: teamRes.id,
},
]);
const extUserRes = await updateUser.save(null, { useMasterKey: true });
} catch (err) {
console.log('err in add team, role, org', err);
}
}
export default async function SubscriptionAftersave(request) {
const oldObj = request.original;
if (!oldObj) {
try {
const subscription = new Parse.Query('contracts_Subscriptions');
subscription.include('CreatedBy');
const res = await subscription.get(request.object.id, { useMasterKey: true });
const _res = JSON.parse(JSON.stringify(res));
const user = _res.CreatedBy?.email;
if (user) {
const extUserQuery = new Parse.Query('contracts_Users');
extUserQuery.equalTo('Email', user);
const extUserRes = await extUserQuery.first({ useMasterKey: true });
if (extUserRes) {
const extUser = JSON.parse(JSON.stringify(extUserRes));
if (!extUser?.OrganizationId) {
await addTeamAndOrg(extUser);
}
}
}
} catch (err) {
console.log('Err in subscriptionaftersave', err);
}
} else {
try {
const subscription = new Parse.Query('contracts_Subscriptions');
subscription.include('CreatedBy');
const res = await subscription.get(request.object.id, { useMasterKey: true });
const _res = JSON.parse(JSON.stringify(res));
const user = _res.CreatedBy?.email;
if (user) {
const extUserQuery = new Parse.Query('contracts_Users');
extUserQuery.equalTo('Email', user);
const extUserRes = await extUserQuery.first({ useMasterKey: true });
if (extUserRes) {
const extUser = JSON.parse(JSON.stringify(extUserRes));
if (!extUser?.OrganizationId) {
await addTeamAndOrg(extUser);
}
}
}
} catch (err) {
console.log('Err in subscriptionaftersave', err);
}
}
}
@@ -1,31 +1,53 @@
import { useLocal } from '../../Utils.js';
import getPresignedUrl from './getSignedUrl.js';
import getPresignedUrl, { presignedlocalUrl } from './getSignedUrl.js';
async function TemplateAfterFind(request) {
if (request.objects.length === 1) {
if (request.objects) {
const obj = request.objects[0];
const FileAdapterId = obj?.get('FileAdapterId') || '';
if (FileAdapterId || useLocal !== 'true') {
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');
let fileAdapter = {};
if (FileAdapterId) {
const tenantId = obj?.get('ExtUserPtr')?.get('TenantId');
if (tenantId) {
const _tenantId = JSON.parse(JSON.stringify(obj?.get('ExtUserPtr')?.get('TenantId')));
fileAdapter = _tenantId?.FileAdapters?.find(x => x.id === FileAdapterId) || {};
}
}
if (SignedUrl) {
obj.set('SignedUrl', getPresignedUrl(SignedUrl, fileAdapter));
obj.set(
'SignedUrl',
getPresignedUrl(
SignedUrl,
)
);
}
if (Url) {
obj.set('URL', getPresignedUrl(Url, fileAdapter));
obj.set(
'URL',
getPresignedUrl(
Url,
)
);
}
if (certificateUrl) {
obj.set('CertificateUrl', getPresignedUrl(certificateUrl, fileAdapter));
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 (SignedUrl) {
obj.set('SignedUrl', presignedlocalUrl(SignedUrl));
}
if (Url) {
obj.set('URL', presignedlocalUrl(Url));
}
if (certificateUrl) {
obj.set('CertificateUrl', presignedlocalUrl(certificateUrl));
}
return [obj];
}
@@ -6,13 +6,16 @@ export default async function TemplateAfterSave(request) {
const signers = request.object.get('Signers');
const AutoReminder = request?.object?.get('AutomaticReminders') || false;
const ip = request?.headers?.['x-real-ip'] || '';
const originIp = request?.object?.get('OriginIp') || '';
if (AutoReminder) {
const RemindOnceInEvery = request?.object?.get('RemindOnceInEvery') || 5;
const ReminderDate = new Date(request?.object?.get('createdAt'));
ReminderDate.setDate(ReminderDate.getDate() + RemindOnceInEvery);
request.object.set('NextReminderDate', ReminderDate);
}
request.object.set('OriginIp', ip);
if (!originIp) {
request.object.set('OriginIp', ip);
}
await request.object.save(null, { useMasterKey: true });
if (signers && signers.length > 0) {
await updateAclDoc(request.object.id);
@@ -1,5 +1,5 @@
import { useLocal } from '../../Utils.js';
import getPresignedUrl from './getSignedUrl.js';
import getPresignedUrl, { presignedlocalUrl } from './getSignedUrl.js';
async function TenantAterFind(request) {
if (useLocal !== 'true') {
@@ -13,6 +13,17 @@ async function TenantAterFind(request) {
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];
}
}
}
}
export default TenantAterFind;
@@ -1,5 +1,5 @@
import { useLocal } from '../../Utils.js';
import getPresignedUrl from './getSignedUrl.js';
import getPresignedUrl, { presignedlocalUrl } from './getSignedUrl.js';
async function UserAfterFind(request) {
if (useLocal !== 'true') {
@@ -13,6 +13,17 @@ async function UserAfterFind(request) {
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];
}
}
}
}
export default UserAfterFind;
@@ -1,61 +0,0 @@
import axios from 'axios';
/**
* ZohoDetails function
* @param hostedpagesId Id must be in String
* @returns response {phone, name, email, nextBillingDate, company, plan, customer_id, subscription_id}
*/
export default async function ZohoDetails(request) {
// Define the URL
const url = 'https://accounts.zoho.in/oauth/v2/token';
// Convert the data to x-www-form-urlencoded format
const formData = new URLSearchParams();
formData.append('refresh_token', process.env.ZOHO_REFRESH_TOKEN);
formData.append('client_id', process.env.ZOHO_CLIENT_ID);
formData.append('client_secret', process.env.ZOHO_CLIENT_SECRET);
formData.append('redirect_uri', process.env.ZOHO_REDIRECT_URI);
formData.append('grant_type', 'refresh_token');
const headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
// Make the POST request using Axios
const res = await axios.post(url, formData, { headers });
// console.log("Access Token:", res.data);
if (res.data.access_token) {
const hostedpages = request.params.hostedpagesId;
const userData = await axios.get(
'https://www.zohoapis.in/billing/v1/hostedpages/' + hostedpages,
{
headers: {
Authorization: 'Zoho-oauthtoken ' + res.data.access_token,
'X-com-zoho-subscriptions-organizationid': process.env.ZOHO_BILLING_ORG_ID,
},
}
);
const first_name = userData.data.data.subscription.contactpersons[0].first_name || '';
const last_name = userData.data.data.subscription.contactpersons[0].last_name || '';
const company_name =
(userData.data.data.subscription.customer &&
userData.data.data.subscription.customer.company_name) ||
'';
const jobTitle =
(userData.data.data.subscription.customer &&
userData.data.data.subscription.customer.cd_job_title) ||
'';
const resData = {
phone: userData.data.data.subscription.contactpersons[0]?.mobile || '',
name: first_name + ' ' + last_name,
email: userData.data.data.subscription.contactpersons[0].email,
nextBillingDate: userData.data.data.subscription.next_billing_at,
company: company_name,
plan: userData.data.data.subscription.plan,
customer_id: userData.data.data.subscription.customer_id,
subscription_id: userData.data.data.subscription.subscription_id,
jobTitle: jobTitle,
subscription: userData.data,
};
return resData;
}
}
@@ -1,142 +0,0 @@
// `generateId` is used to unique Id for fileAdapter
function generateId(length) {
const characters = 'abcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
}
// `getExtUser` get ext user details
async function getExtUser(request) {
const extUserCls = new Parse.Query('contracts_Users');
extUserCls.equalTo('UserId', request.user);
extUserCls.include('TenantId');
return await extUserCls.first({ useMasterKey: true });
}
// `saveTenant` save file adapter details in tenant class
async function saveTenant(tenantId, fileAdapters, activeAdapter) {
const tenantCls = new Parse.Object('partners_Tenant');
tenantCls.id = tenantId;
if (fileAdapters?.length > 0) {
tenantCls.set('FileAdapters', fileAdapters);
}
if (activeAdapter) {
tenantCls.set('ActiveFileAdapter', activeAdapter);
} else {
tenantCls.unset('ActiveFileAdapter');
}
return await tenantCls.save(null, { useMasterKey: true });
}
// `updateTenantSchema` is used add FileAdapter in protected fields
async function updateTenantSchema() {
const tenantSchema = new Parse.Schema('partners_Tenant');
const currentSchema = await tenantSchema.get();
let clp = currentSchema.classLevelPermissions;
// Public permission ("*")
const role = '*';
if (!clp.protectedFields || Object.keys(clp.protectedFields).length === 0) {
// Initialize protectedFields if it doesn't exist
clp.protectedFields = { [role]: [] };
}
// save FileAdapters field is in protectedFields if not exists
if (!clp.protectedFields[role]?.includes('FileAdapters')) {
clp.protectedFields[role].push('FileAdapters');
// Update the class schema with the modified CLP
tenantSchema.setCLP(clp);
await tenantSchema.update();
}
}
export default async function addFileAdapter(request) {
if (!request?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
const fileAdapterName = request.params.fileAdapterName;
const bucketName = request.params.bucketName;
const region = request.params.region;
const endpoint = request.params.endpoint;
const baseUrl = request.params.baseUrl;
const accessKeyId = request.params.accessKeyId;
const secretAccessKey = request.params.secretAccessKey;
const adapter = request.params.fileAdapter;
if (fileAdapterName && accessKeyId && secretAccessKey && adapter) {
try {
const extUser = await getExtUser(request);
if (extUser) {
const _extUser = JSON.parse(JSON.stringify(extUser));
const tenantId = extUser?.get('TenantId')?.id;
// assign existing file adapters or empty array
let fileAdapters = _extUser?.TenantId?.FileAdapters || [];
const uniqueId = generateId(10);
let id = extUser?.get('TenantId')?.id + '_' + uniqueId;
const index = fileAdapters?.findIndex(x => x.fileAdapterName === fileAdapterName);
if (index !== -1) {
// If an object with the same fileAdapterName exists, update it
if (bucketName || region || endpoint || baseUrl) {
throw new Parse.Error(
Parse.Error.INVALID_QUERY,
'Cannot update bucketName, region, endpoint, baseUrl.'
);
} else {
const adapterConfig = { accessKeyId: accessKeyId, secretAccessKey: secretAccessKey };
fileAdapters[index] = { ...fileAdapters[index], ...adapterConfig };
id = fileAdapters[index].id;
}
} else {
if (bucketName && region && endpoint && baseUrl) {
// If the object with the given fileAdapterName doesn't exist, add a new one
fileAdapters.push({
id: id,
fileAdapterName: fileAdapterName,
fileAdapter: adapter,
bucketName: bucketName,
region: region,
endpoint: endpoint,
baseUrl: baseUrl,
accessKeyId: accessKeyId,
secretAccessKey: secretAccessKey,
});
} else {
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide all parameters.');
}
}
const updateTenant = await saveTenant(tenantId, fileAdapters, id);
await updateTenantSchema();
const ActiveFileAdapter = adapter === 'opensign' ? 'opensign' : id;
return { ActiveFileAdapter: ActiveFileAdapter, updateAt: updateTenant.updatedAt };
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
}
} catch (err) {
console.log('Err in add custom file adapter', err);
const code = err.code || 400;
const msg = err.message || 'Something went wrong.';
throw new Parse.Error(code, msg);
}
} else if (adapter === 'opensign') {
try {
const extUser = await getExtUser(request);
if (extUser) {
const tenantId = extUser?.get('TenantId')?.id;
const updateTenant = await saveTenant(tenantId);
await updateTenantSchema();
return { ActiveFileAdapter: 'opensign', updateAt: updateTenant.updatedAt };
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
}
} catch (err) {
console.log('Err in add custom file adapter', err);
const code = err.code || 400;
const msg = err.message || 'Something went wrong.';
throw new Parse.Error(code, msg);
}
} else {
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide all parameters.');
}
}
@@ -1,70 +0,0 @@
export default async function addOrganization(request) {
const name = request.params.name;
if (!request?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
try {
const extUserQuery = new Parse.Query('contracts_Users');
extUserQuery.equalTo('UserId', {
__type: 'Pointer',
className: '_User',
objectId: request.user.id,
});
extUserQuery.notEqualTo('IsDisabled', true);
const resExt = await extUserQuery.first({ useMasterKey: true });
if (!resExt) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
}
const _resExt = JSON.parse(JSON.stringify(resExt));
const orgQuery = new Parse.Query('contracts_Organizations');
orgQuery.equalTo('Name', name);
orgQuery.equalTo('CreatedBy', {
__type: 'Pointer',
className: '_User',
objectId: request.user.id,
});
const resOrg = await orgQuery.first({ useMasterKey: true });
if (resOrg) {
throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, 'Organization already exists.');
} else {
const newOrg = new Parse.Object('contracts_Organizations');
newOrg.set('Name', name);
newOrg.set('IsActive', true);
newOrg.set('CreatedBy', {
__type: 'Pointer',
className: '_User',
objectId: request.user.id,
});
newOrg.set('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: _resExt.TenantId.objectId,
});
newOrg.set('ExtUserId', {
__type: 'Pointer',
className: 'contracts_Users',
objectId: resExt.id,
});
const newResOrg = await newOrg.save(null, { useMasterKey: true });
const teamCls = new Parse.Object('contracts_Teams');
teamCls.set('Name', 'All Users');
teamCls.set('OrganizationId', {
__type: 'Pointer',
className: 'contracts_Organizations',
objectId: newResOrg.id,
});
teamCls.set('IsActive', true);
await teamCls.save(null, { useMasterKey: true });
if (newResOrg) {
return newResOrg;
}
}
} catch (err) {
console.log('err in addorganization', err);
const code = err?.code || 400;
const msg = err?.message || 'Something went wrong.';
throw new Parse.Error(code, msg);
}
}
@@ -1,58 +0,0 @@
// `updateTenantSchema` is used add FileAdapter in protected fields
async function updateTenantSchema() {
const tenantSchema = new Parse.Schema('partners_Tenant');
const currentSchema = await tenantSchema.get();
let clp = currentSchema.classLevelPermissions;
// Public permission ("*")
const role = '*';
if (!clp.protectedFields || Object.keys(clp.protectedFields).length === 0) {
// Initialize protectedFields if it doesn't exist
clp.protectedFields = { [role]: [] };
}
// save PfxFile field is in protectedFields if not exists
if (!clp.protectedFields[role]?.includes('PfxFile')) {
clp.protectedFields[role].push('PfxFile');
// Update the class schema with the modified CLP
tenantSchema.setCLP(clp);
await tenantSchema.update();
}
}
export default async function addPfxFile(request) {
if (!request?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
const pfxBase64 = request.params.pfxBase64;
const title = request.params.title;
const password = request.params.password;
const provider = request.params.provider;
if (provider === 'opensign' || (pfxBase64 && password)) {
try {
const extUserCls = new Parse.Query('contracts_Users');
extUserCls.equalTo('UserId', request.user);
extUserCls.include('TenantId');
const extUser = await extUserCls.first({ useMasterKey: true });
if (extUser) {
const tenantCls = new Parse.Object('partners_Tenant');
tenantCls.id = extUser?.get('TenantId')?.id;
if (provider === 'opensign') {
tenantCls.unset('PfxFile');
} else {
tenantCls.set('PfxFile', { title: title, password: password, base64: pfxBase64 });
}
const updateTenant = await tenantCls.save(null, { useMasterKey: true });
await updateTenantSchema();
return { updateAt: updateTenant.updatedAt };
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
}
} catch (err) {
console.log('Err in add custom file adapter', err);
const code = err.code || 400;
const msg = err.message || 'Something went wrong.';
throw new Parse.Error(code, msg);
}
} else {
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide all parameters.');
}
}
@@ -1,63 +0,0 @@
export default async function addTeam(request) {
const Name = request.params.Name;
const ParentId = request.params.ParentId;
const Ancestors = request.params.Ancestors;
const ParentPtr = { __type: 'Pointer', className: 'contracts_Teams', objectId: ParentId };
if (Name) {
if (!request?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
try {
const extUserQuery = new Parse.Query('contracts_Users');
extUserQuery.equalTo('UserId', {
__type: 'Pointer',
className: '_User',
objectId: request.user.id,
});
extUserQuery.notEqualTo('IsDisabled', true);
const resExt = await extUserQuery.first({ useMasterKey: true });
const extUser = JSON.parse(JSON.stringify(resExt));
if (!extUser) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
}
const teamCls = new Parse.Query('contracts_Teams');
teamCls.equalTo('Name', Name);
teamCls.equalTo('OrganizationId', {
__type: 'Pointer',
className: 'contracts_Organizations',
objectId: extUser.OrganizationId.objectId,
});
if (ParentId) {
teamCls.equalTo('ParentId', ParentPtr);
}
const teamRes = await teamCls.first({ useMasterKey: true });
if (teamRes) {
throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, 'Team already exists.');
} else {
const newteam = new Parse.Object('contracts_Teams');
newteam.set('Name', Name);
newteam.set('OrganizationId', {
__type: 'Pointer',
className: 'contracts_Organizations',
objectId: extUser.OrganizationId.objectId,
});
if (ParentId) {
newteam.set('ParentId', ParentPtr);
}
if (Ancestors && Ancestors.length > 0) {
newteam.set('Ancestors', Ancestors);
}
newteam.set('IsActive', true);
const newTeamRes = await newteam.save(null, { useMasterKey: true });
return newTeamRes;
}
} catch (err) {
console.log('err in getOrganizations', err);
const code = err?.code || 400;
const msg = err?.message || 'Something went wrong.';
throw new Parse.Error(code, msg);
}
} else {
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide parameters');
}
}
@@ -1,33 +0,0 @@
export default async function addcustomsmtp(request) {
if (!request?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
const host = request.params.host;
const port = request.params.port;
const username = request.params.username;
const password = request.params.password;
if (host && port && username && password) {
try {
const extUserCls = new Parse.Query('contracts_Users');
extUserCls.equalTo('UserId', request.user);
const extUser = await extUserCls.first({ useMasterKey: true });
if (extUser) {
const extUserCls = new Parse.Object('contracts_Users');
extUserCls.id = extUser.id;
extUserCls.set('SmtpConfig', { host, port, username, password });
extUserCls.set('active_mail_adapter', 'smtp');
const updateExtUser = await extUserCls.save(null, { useMasterKey: true });
// console.log('updateExtUser ', updateExtUser);
return updateExtUser.updatedAt;
}
return extUser;
} catch (err) {
console.log('Err in add custom smtp', err);
const code = err.code || 400;
const msg = err.message || 'Something went wrong.';
throw new Parse.Error(code, msg);
}
} else {
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide all parameters.');
}
}
@@ -0,0 +1,56 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
const appId = process.env.APP_ID;
const masterkey = process.env.MASTER_KEY;
export default async function createBatchContact(req) {
if (!req?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
if (!req.params?.contacts) {
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide parameter.');
}
const contactData = JSON.parse(req.params.contacts);
if (contactData?.length > 0) {
try {
const requests = contactData.map(x => {
return {
method: 'POST',
path: '/app/classes/contracts_Contactbook',
body: {
UserRole: 'contracts_Guest',
TenantId: { __type: 'Pointer', className: 'partners_Tenant', objectId: x.TenantId },
CreatedBy: { __type: 'Pointer', className: '_User', objectId: req.user.id },
Name: x.Name,
Email: x.Email,
IsDeleted: false,
IsImported: true,
...(x?.Phone ? { Phone: `${x?.Phone}` } : {}),
ACL: { [req.user.id]: { read: true, write: true } },
},
};
});
const parseConfig = {
baseURL: cloudServerUrl,
headers: {
'X-Parse-Application-Id': appId,
'X-Parse-Master-Key': masterkey,
'Content-Type': 'application/json',
},
};
const response = await axios.post('batch', { requests: requests }, parseConfig);
// Handle the batch query response
// console.info('createbatchcontact ', response.data);
const successCount = response?.data?.filter(item => item.success).length;
const failedCount = requests.length - successCount;
console.log(
`createbatchcontact query response: success: ${successCount}, failed: ${failedCount}`
);
return { success: successCount, failed: failedCount };
} catch (err) {
console.log('err while create batch contact', err);
throw new Parse.Error(400, 'Something went wrong, please try again later');
}
} else {
throw new Parse.Error(Parse.Error.INVALID_CONTENT_LENGTH, 'Please provide parameter');
}
}
@@ -1,27 +1,15 @@
import axios from 'axios';
import { cloudServerUrl, parseJwt } from '../../Utils.js';
import jwt from 'jsonwebtoken';
import {
cloudServerUrl,
replaceMailVaribles,
} from '../../Utils.js';
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const appId = process.env.APP_ID;
const licenseKey = process.env.LICENSE_KEY;
async function deductcount(docsCount, extUserId, subscription) {
async function deductcount(
docsCount,
extUserId,
) {
try {
if (licenseKey) {
const allowedCredits = subscription?.AllowedCredits || 0;
const addonCredits = subscription?.AddonCredits || 0;
const subscriptionCls = new Parse.Object('contracts_Subscriptions');
subscriptionCls.id = subscription.objectId;
if (docsCount <= allowedCredits) {
const updateAllowedcredits = allowedCredits - docsCount;
subscriptionCls.set('AllowedCredits', updateAllowedcredits);
} else {
const remaingCount = docsCount - allowedCredits;
const updateAddonCredits = addonCredits - remaingCount;
subscriptionCls.set('AllowedCredits', 0);
subscriptionCls.set('AddonCredits', updateAddonCredits);
}
await subscriptionCls.save(null, { useMasterKey: true });
}
const extCls = new Parse.Object('contracts_Users');
extCls.id = extUserId;
extCls.increment('DocumentCount', docsCount);
@@ -55,11 +43,7 @@ async function sendMail(document) {
try {
const imgPng = 'https://qikinnovation.ams3.digitaloceanspaces.com/logo.png';
let url = `${serverUrl}/functions/sendmailv3`;
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': appId,
// sessionToken: sessionToken,
};
const headers = { 'Content-Type': 'application/json', 'X-Parse-Application-Id': appId };
const objectId = signerMail[i]?.signerObjId;
const hostUrl = baseUrl.origin;
let encodeBase64;
@@ -74,35 +58,67 @@ async function sendMail(document) {
const openSignUrl = 'https://www.opensignlabs.com/';
const orgName = document.ExtUserPtr.Company ? document.ExtUserPtr.Company : '';
const themeBGcolor = '#47a3ad';
const senderObj = document?.ExtUserPtr;
const mailBody = document?.ExtUserPtr?.TenantId?.RequestBody || '';
const mailSubject = document?.ExtUserPtr?.TenantId?.RequestSubject || '';
let replaceVar;
if (mailBody && mailSubject) {
const replacedRequestBody = mailBody.replace(/"/g, "'");
const htmlReqBody =
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>" +
replacedRequestBody +
'</body></html>';
const variables = {
document_title: document?.Name,
sender_name:
senderObj?.Name,
sender_mail:
senderObj?.Email,
sender_phone: senderObj?.Phone || '',
receiver_name: existSigner?.Name || '',
receiver_email: existSigner?.Email || signerMail[i].email,
receiver_phone: existSigner?.Phone || '',
expiry_date: localExpireDate,
company_name: orgName,
signing_url: `<a href=${signPdf} target=_blank>Sign here</a>`,
};
replaceVar = replaceMailVaribles(mailSubject, htmlReqBody, variables);
}
let params = {
mailProvider: document?.ExtUserPtr?.active_mail_adapter || '',
extUserId: document.ExtUserPtr.objectId,
recipient: objectId ? existSigner?.Email : signerMail[i].email,
subject: `${document.ExtUserPtr.Name} has requested you to sign "${document.Name}"`,
mailProvider: document?.ExtUserPtr?.active_mail_adapter || '',
from: sender,
html:
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /> </head> <body> <div style='background-color: #f5f5f5; padding: 20px'> <div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background: white;padding-bottom: 20px;'> <div style='padding:10px 10px 0 10px'><img src='" +
imgPng +
"' height='50' style='padding:20px; width:170px; height:40px;' /></div><div style='padding: 2px;font-family: system-ui;background-color:" +
themeBGcolor +
";'><p style='font-size: 20px;font-weight: 400;color: white;padding-left: 20px;' > Digital Signature Request</p></div><div><p style='padding: 20px;font-family: system-ui;font-size: 14px; margin-bottom: 10px;'> " +
document.ExtUserPtr.Name +
' has requested you to review and sign <strong> ' +
document.Name +
"</strong>.</p><div style='padding: 5px 0px 5px 25px;display: flex;flex-direction: row;justify-content: space-around;'><table> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Sender</td> <td> </td> <td style='color:#626363;font-weight:bold'>" +
sender +
"</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Organization</td> <td> </td><td style='color:#626363;font-weight:bold'> " +
orgName +
"</td></tr> <tr> <td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expires on</td><td> </td> <td style='color:#626363;font-weight:bold'>" +
localExpireDate +
"</td></tr><tr> <td></td> <td> </td></tr></table> </div> <div style='margin-left:70px'><a target=_blank href=" +
signPdf +
"> <button style='padding: 12px 12px 12px 12px;background-color: #d46b0f;color: white; border: 0px;box-shadow: rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;font-weight:bold;margin-top:30px;'>Sign here</button></a> </div> <div style='display: flex; justify-content: center;margin-top: 10px;'> </div></div></div><div><p> This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " +
sender +
' directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href=' +
openSignUrl +
' target=_blank>here</a>.</p> </div></div></body> </html>',
subject: replaceVar?.subject
? replaceVar?.subject
: `${document.ExtUserPtr.Name} has requested you to sign "${document.Name}"`,
from:
sender,
replyto:
sender ||
'',
html: replaceVar?.body
? replaceVar?.body
: "<html><head><meta http-equiv='Content-Type' content='text/html;charset=UTF-8' /></head><body><div style='background-color:#f5f5f5;padding:20px;'><div style='box-shadow:rgba(0, 0, 0, 0.1) 0px 4px 12px;background:white;padding-bottom:20px;'><div style='padding:10px 10px 0 10px'><img src=" +
imgPng +
" height='50' style='padding:20px;width:170px;height:40px;' /></div><div style='padding:2px;font-family:system-ui;background-color:" +
themeBGcolor +
";'><p style='font-size:20px;font-weight:400;color:white;padding-left:20px;' > Digital Signature Request</p></div><div><p style='padding:20px;font-family:system-ui;font-size:14px;margin-bottom:10px;'> " +
document.ExtUserPtr.Name +
' has requested you to review and sign <strong> ' +
document.Name +
"</strong>.</p><div style='padding: 5px 0px 5px 25px;display:flex;flex-direction:row;justify-content:space-around;'><table><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Sender</td><td></td><td style='color:#626363;font-weight:bold'>" +
sender +
"</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Organization</td><td></td><td style='color:#626363;font-weight:bold'> " +
orgName +
"</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Expires on</td><td></td><td style='color:#626363;font-weight:bold'>" +
localExpireDate +
"</td></tr><tr><td></td><td></td></tr></table></div><div style='margin-left:70px;'><a target=_blank href=" +
signPdf +
"><button style='padding:12px 12px 12px 12px;background-color:#d46b0f;color:white;border:0px;box-shadow:rgba(0, 0, 0, 0.05) 0px 6px 24px 0px,rgba(0, 0, 0, 0.08) 0px 0px 0px 1px;font-weight:bold;margin-top:30px;'>Sign here</button></a></div><div style='display:flex;justify-content:center;margin-top:10px;'></div></div></div><div><p> This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender " +
sender +
' directly.If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href=' +
openSignUrl +
' target=_blank>here</a>.</p></div></div></body></html>',
};
const sendMail = await axios.post(url, params, { headers: headers });
// if (sendMail.data.result.status === 'success') {
@@ -113,75 +129,13 @@ async function sendMail(document) {
}
}
}
async function callwebhookevent(document) {
const Signers = document.Signers;
const allSigner = document?.Placeholders?.map(item => {
if (item.signerObjId) {
const signer = Signers?.find(e => item?.signerPtr?.objectId === e?.objectId);
if (signer) {
return {
role: item?.Role || '',
name: signer?.Name || '',
email: signer?.Email || '',
phone: signer?.Phone || '',
};
}
} else {
return { role: item?.Role || '', name: '', email: item?.email || '', phone: '' };
}
});
const params = {
event: 'created',
body: {
objectId: document?.objectId,
file: document?.SignedUrl || document?.URL,
name: document?.Name,
note: document?.Note || '',
description: document?.Description || '',
signers: allSigner,
createdBy: document?.ExtUserPtr.Email,
createdAt: document?.createdAt,
},
};
try {
await axios
.post(document?.ExtUserPtr?.Webhook, params, {
headers: { 'Content-Type': 'application/json' },
})
.then(res => {
try {
const webhook = new Parse.Object('contracts_Webhook');
webhook.set('Log', res?.status);
webhook.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: document.CreatedBy.objectId,
});
webhook.save(null, { useMasterKey: true });
} catch (err) {
console.log('err save in contracts_Webhook', err);
}
})
.catch(err => {
console.log('Err send data to webhook', err?.message);
try {
const webhook = new Parse.Object('contracts_Webhook');
webhook.set('Log', err?.status);
webhook.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: document.CreatedBy.objectId,
});
webhook.save(null, { useMasterKey: true });
} catch (err) {
console.log('err save in contracts_Webhook', err?.message);
}
});
} catch (err) {
console.log('Err ', err?.message);
}
}
async function batchQuery(userId, Documents, Ip, parseConfig) {
async function batchQuery(
userId,
Documents,
Ip,
parseConfig,
type
) {
const extCls = new Parse.Query('contracts_Users');
extCls.equalTo('UserId', {
__type: 'Pointer',
@@ -202,10 +156,14 @@ async function batchQuery(userId, Documents, Ip, parseConfig) {
let Acl = { [x.CreatedBy.objectId]: { read: true, write: true } };
if (allSigner && allSigner.length > 0) {
allSigner.forEach(x => {
const obj = { [x.CreatedBy.objectId]: { read: true, write: true } };
Acl = { ...Acl, ...obj };
if (x?.CreatedBy?.objectId) {
const obj = { [x.CreatedBy.objectId]: { read: true, write: true } };
Acl = { ...Acl, ...obj };
}
});
}
const mailBody = x?.ExtUserPtr?.TenantId?.RequestBody || '';
const mailSubject = x?.ExtUserPtr?.TenantId?.RequestSubject || '';
return {
method: 'POST',
path: '/app/classes/contracts_Document',
@@ -231,8 +189,9 @@ async function batchQuery(userId, Documents, Ip, parseConfig) {
objectId: y.signerPtr.objectId,
},
signerObjId: y.signerObjId,
email: y?.signerPtr?.Email || y?.email || '',
}
: { ...y, signerPtr: {}, signerObjId: '' }
: { ...y, signerPtr: {}, signerObjId: '', email: y.email || '' }
),
SignedUrl: x.URL || x.SignedUrl,
SentToOthers: true,
@@ -250,55 +209,17 @@ async function batchQuery(userId, Documents, Ip, parseConfig) {
DocSentAt: { __type: 'Date', iso: isoDate },
IsEnableOTP: x?.IsEnableOTP || false,
IsTourEnabled: x?.IsTourEnabled || false,
FileAdapterId: x?.FileAdapterId || '',
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 } : {}),
},
};
});
// console.log('requests ', requests);
if (licenseKey) {
const subscription = new Parse.Query('contracts_Subscriptions');
subscription.equalTo('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: _resExt.TenantId.objectId,
});
subscription.include('ExtUserPtr');
subscription.greaterThanOrEqualTo('Next_billing_date', new Date());
const resSub = await subscription.first({ useMasterKey: true });
if (resSub) {
const _resSub = JSON.parse(JSON.stringify(resSub));
const allowedCredits = _resSub?.AllowedCredits || 0;
const addonCredits = _resSub?.AddonCredits || 0;
const totalcredits = allowedCredits + addonCredits;
if (requests?.length <= totalcredits) {
const response = await axios.post('batch', { requests: requests }, parseConfig);
// Handle the batch query response
// console.log('Batch query response:', response.data);
if (response.data && response.data.length > 0) {
const updateDocuments = Documents.map((x, i) => ({
...x,
objectId: response.data[i]?.success?.objectId,
createdAt: response.data[i]?.success?.createdAt,
}));
deductcount(response.data.length, resExt.id, _resSub);
for (let i = 0; i < updateDocuments.length; i++) {
sendMail(updateDocuments[i], ''); //sessionToken
}
callwebhookevent(Documents[0]);
return 'success';
}
} else {
throw new Parse.Error(429, 'Quota reached, Please buy credits and try again later.');
}
} else {
throw new Parse.Error(
Parse.Error.INVALID_QUERY,
'Please purchase or renew your subscription.'
);
}
} else {
if (requests?.length > 0) {
const newrequests = [requests?.[0]];
const response = await axios.post('batch', { requests: newrequests }, parseConfig);
@@ -313,10 +234,8 @@ async function batchQuery(userId, Documents, Ip, parseConfig) {
};
deductcount(response.data.length, resExt.id);
sendMail(updateDocuments); //sessionToken
callwebhookevent(Documents[0]);
return 'success';
}
}
}
} catch (error) {
const code = error?.response?.data?.code || error?.response?.status || error?.code || 400;
@@ -335,7 +254,7 @@ async function batchQuery(userId, Documents, Ip, parseConfig) {
export default async function createBatchDocs(request) {
const strDocuments = request.params.Documents;
const sessionToken = request.headers?.sessiontoken;
const jwttoken = request.headers?.jwttoken;
const type = request.headers?.type || 'quicksend';
const Documents = JSON.parse(strDocuments);
const Ip = request?.headers?.['x-real-ip'] || '';
const parseConfig = {
@@ -348,31 +267,9 @@ export default async function createBatchDocs(request) {
};
try {
if (request?.user) {
return await batchQuery(request.user.id, Documents, Ip, parseConfig);
} else if (jwttoken) {
const jwtDecode = parseJwt(jwttoken);
if (jwtDecode?.user_email) {
const userCls = new Parse.Query(Parse.User);
userCls.equalTo('email', jwtDecode?.user_email);
const userRes = await userCls.first({ useMasterKey: true });
const userId = userRes?.id;
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('userId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
const appRes = await tokenQuery.first({ useMasterKey: true });
const decoded = jwt.verify(jwttoken, appRes?.get('token'));
if (decoded?.user_email) {
return await batchQuery(userId, Documents, Ip, parseConfig);
} else {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid token.');
}
} else {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid token.');
}
} else {
return await batchQuery(request.user.id, Documents, Ip, parseConfig, '', type);
}
else {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
} catch (err) {
@@ -0,0 +1,112 @@
export default async function createDuplicate(request) {
const templateId = request.params.templateId;
if (!request.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
if (templateId) {
try {
const templateQuery = new Parse.Query('contracts_Template');
templateQuery.equalTo('objectId', templateId);
templateQuery.equalTo('CreatedBy', {
__type: 'Pointer',
className: '_User',
objectId: request.user.id,
});
templateQuery.notEqualTo('IsArchive', true);
const templateRes = await templateQuery.first({ useMasterKey: true });
if (templateRes?.id) {
const _templateRes = JSON.parse(JSON.stringify(templateRes));
const newTemplate = new Parse.Object('contracts_Template');
let signers = [];
if (_templateRes.Signers?.length > 0) {
_templateRes.Signers?.forEach(x => {
if (x.objectId) {
const obj = {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: x.objectId,
};
signers.push(obj);
}
});
}
newTemplate.set('Name', _templateRes.Name);
newTemplate.set('URL', _templateRes.URL);
newTemplate.set('SignedUrl', _templateRes.SignedUrl);
newTemplate.set('SentToOthers', _templateRes?.SentToOthers || false);
newTemplate.set('SendinOrder', _templateRes?.SendinOrder || false);
newTemplate.set('AutomaticReminders', _templateRes?.AutomaticReminders || false);
newTemplate.set('RemindOnceInEvery', _templateRes?.RemindOnceInEvery || 5);
newTemplate.set('IsEnableOTP', _templateRes?.IsEnableOTP || false);
newTemplate.set('AllowModifications', _templateRes?.AllowModifications || false);
newTemplate.set('Signers', signers);
newTemplate.set('ExtUserPtr', {
__type: 'Pointer',
className: 'contracts_Users',
objectId: _templateRes.ExtUserPtr.objectId,
});
newTemplate.set('CreatedBy', {
__type: 'Pointer',
className: '_User',
objectId: _templateRes.CreatedBy.objectId,
});
if (_templateRes?.Note) {
newTemplate.set('Note', _templateRes?.Note);
}
if (_templateRes?.Description) {
newTemplate.set('Description', _templateRes?.Description);
}
if (_templateRes?.Placeholders?.length > 0) {
newTemplate.set('Placeholders', _templateRes.Placeholders);
}
if (_templateRes?.SignatureType?.length > 0) {
newTemplate.set('SignatureType', _templateRes?.SignatureType);
}
if (_templateRes?.NotifyOnSignatures !== undefined) {
newTemplate.set('NotifyOnSignatures', _templateRes.NotifyOnSignatures);
}
if (_templateRes?.SharedWith?.length > 0) {
newTemplate.set('SharedWith', _templateRes.SharedWith);
}
if (_templateRes?.IsPublic) {
newTemplate.set('IsPublic', _templateRes?.IsPublic);
}
if (_templateRes?.PublicRole?.length > 0) {
newTemplate.set('PublicRole', _templateRes?.PublicRole);
}
if (_templateRes?.IsTourEnabled) {
newTemplate.set('IsTourEnabled', _templateRes?.IsTourEnabled);
}
if (_templateRes?.Bcc?.length) {
newTemplate.set('Bcc', _templateRes?.Bcc);
}
const OriginIp = _templateRes?.OriginIp || request?.headers?.['x-real-ip'] || '';
if (OriginIp) {
newTemplate.set('OriginIp', OriginIp);
}
if (_templateRes?.RedirectUrl) {
newTemplate.set('RedirectUrl', _templateRes?.RedirectUrl);
}
const acl = templateRes.getACL();
if (acl) {
newTemplate.setACL(acl);
}
const newTemplateRes = await newTemplate.save(null, { useMasterKey: true });
const _newTemplateRes = JSON.parse(JSON.stringify(newTemplateRes));
return _newTemplateRes;
} else {
throw new Parse.Error(
Parse.Error.INVALID_QUERY,
'You cannot create duplicate of this template.'
);
}
} catch (err) {
console.log('err while creating duplicate', err);
throw err;
}
}
}
@@ -1,24 +0,0 @@
export default async function deactivateMailAdapter(request) {
if (!request?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
try {
const extUserCls = new Parse.Query('contracts_Users');
extUserCls.equalTo('UserId', request.user);
const extUser = await extUserCls.first({ useMasterKey: true });
if (extUser) {
const extUserCls = new Parse.Object('contracts_Users');
extUserCls.id = extUser.id;
extUserCls.unset('active_mail_adapter');
const updateExtUser = await extUserCls.save(null, { useMasterKey: true });
// console.log('updateExtUser ', updateExtUser);
return updateExtUser.updatedAt;
}
return extUser;
} catch (err) {
console.log('Err in add custom smtp', err);
const code = err.code || 400;
const msg = err.message || 'Something went wrong.';
throw new Parse.Error(code, msg);
}
}
@@ -0,0 +1,90 @@
export default async function editContact(request) {
const { contactId, name, email, phone, tenantId } = request.params;
if (!request?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
const createdBy = { __type: 'Pointer', className: '_User', objectId: request.user.id };
try {
const contact = new Parse.Object('contracts_Contactbook');
contact.id = contactId;
contact.set('IsDeleted', true);
const contactRes = await contact.save(null, {
sessionToken: request?.user.getSessionToken(),
});
if (contactRes) {
const query = new Parse.Query('contracts_Contactbook');
query.equalTo('CreatedBy', createdBy);
query.notEqualTo('IsDeleted', true);
query.equalTo('Email', email);
const isContactExist = await query.first({ useMasterKey: true });
if (isContactExist) {
throw new Parse.Error(Parse.Error.DUPLICATE_VALUE, 'Contact already exists.');
}
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);
contactQuery.set('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: tenantId,
});
try {
const _users = Parse.Object.extend('User');
const _user = new _users();
_user.set('name', name);
_user.set('username', email);
_user.set('email', email);
_user.set('password', email);
if (phone) {
_user.set('phone', phone);
}
const user = await _user.save();
if (user) {
contactQuery.set('CreatedBy', createdBy);
contactQuery.set('UserId', user);
const acl = new Parse.ACL();
acl.setReadAccess(user.id, true);
acl.setWriteAccess(user.id, true);
acl.setReadAccess(createdBy.objectId, true);
acl.setWriteAccess(createdBy.objectId, true);
contactQuery.setACL(acl);
const res = await contactQuery.save();
const parseData = JSON.parse(JSON.stringify(res));
return parseData;
}
} catch (err) {
console.log('err ', err);
if (err.code === 202) {
const params = { email: email };
const userRes = await Parse.Cloud.run('getUserId', params);
contactQuery.set('CreatedBy', createdBy);
contactQuery.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: userRes.id,
});
const acl = new Parse.ACL();
acl.setReadAccess(userRes.id, true);
acl.setWriteAccess(userRes.id, true);
acl.setReadAccess(createdBy.objectId, true);
acl.setWriteAccess(createdBy.objectId, true);
contactQuery.setACL(acl);
const res = await contactQuery.save();
const parseData = JSON.parse(JSON.stringify(res));
return parseData;
}
}
} else {
throw new Parse.Error(400, 'Something went wrong.');
}
} catch (err) {
throw err;
}
}
@@ -0,0 +1,13 @@
import { getSignedLocalUrl } from './getSignedUrl.js';
export default async function fileUpload(request) {
const url = request.params.url;
try {
const urlwithjwt = getSignedLocalUrl(url, 200);
return { url: urlwithjwt };
} catch (err) {
console.log('Err ', err);
throw err;
}
}
@@ -1,45 +0,0 @@
import { generateApiKey } from 'generate-api-key';
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
export default async function generateApiToken(request) {
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
try {
const userRes = await axios.get(serverUrl + '/users/me', {
headers: {
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Session-Token': request.headers['sessiontoken'],
},
});
const userId = userRes.data && userRes.data.objectId;
if (userId) {
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('userId', { __type: 'Pointer', className: '_User', objectId: userId });
const token = await tokenQuery.first({ useMasterKey: true });
if (token !== undefined) {
// return exsiting Token
console.log('Regenerate API Token');
const AppToken = Parse.Object.extend('appToken');
const updateToken = new AppToken();
updateToken.id = token.id;
const newToken = generateApiKey({ method: 'base62', prefix: 'opensign' });
updateToken.set('token', newToken);
const updatedRes = await updateToken.save(null, { useMasterKey: true });
return updatedRes;
} else {
// Create New Token
console.log('New API Token Generation');
const appToken = Parse.Object.extend('appToken');
const appTokenQuery = new appToken();
const token = generateApiKey({ method: 'base62', prefix: 'opensign' });
appTokenQuery.set('token', token);
appTokenQuery.set('userId', { __type: 'Pointer', className: '_User', objectId: userId });
const newRes = await appTokenQuery.save(null, { useMasterKey: true });
return newRes;
}
} else {
return 'User not found!';
}
} catch (err) {
console.log('err ', err);
}
}
@@ -0,0 +1,109 @@
import { SignPdf } from '@signpdf/signpdf';
import { P12Signer } from '@signpdf/signer-p12';
import { pdflibAddPlaceholder } from '@signpdf/placeholder-pdf-lib';
import { PDFDocument } from 'pdf-lib';
import Parse from 'parse/node.js';
import fs from 'node:fs';
import dotenv from 'dotenv';
import GenerateCertificate from './pdf/GenerateCertificate.js';
import { getSecureUrl } from '../../Utils.js';
dotenv.config();
const eSignName = 'opensign';
const eSigncontact = 'hello@opensignlabs.com';
// `uploadFile` is used to create url in from pdfFile
async function uploadFile(
pdfName,
filepath,
) {
try {
const filedata = fs.readFileSync(filepath);
let fileUrl;
const file = new Parse.File(pdfName, [...filedata], 'application/pdf');
await file.save({ useMasterKey: true });
const fileRes = getSecureUrl(file.url());
fileUrl = fileRes.url;
return { imageUrl: fileUrl };
} catch (err) {
console.log('Err ', err);
// `unlinkCertificate` is used to remove exported signed pdf file from exports folder
unlinkCertificate(filepath);
}
}
async function unlinkCertificate(path) {
if (fs.existsSync(path)) {
try {
fs.unlinkSync(path);
} catch (err) {
console.log('Err in unlink certificate generatecertificatebydocid', err);
}
}
}
export default async function generateCertificatebydocId(req) {
const docId = req.params.docId;
// const userId = req.headers.userid;
if (!docId) {
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'please provide parameter.');
}
// `P12Buffer` used to create buffer from p12 certificate
const pfxFile = process.env.PFX_BASE64;
// const P12Buffer = fs.readFileSync();
const P12Buffer = Buffer.from(pfxFile, 'base64');
const certificatePath = `./exports/certificate_${docId}.pdf`;
try {
const getDocument = new Parse.Query('contracts_Document');
getDocument.include('ExtUserPtr,Signers,AuditTrail.UserPtr,Placeholders,ExtUserPtr.TenantId');
const docRes = await getDocument.get(docId, { useMasterKey: true });
if (docRes && docRes?.get('IsCompleted') && !docRes?.get('CertificateUrl')) {
const _docRes = JSON.parse(JSON.stringify(docRes));
const filteredaudit = _docRes?.AuditTrail?.filter(x => x?.UserPtr?.objectId);
// Create a reversed copy of the array and find the last object with 'signedOn'
const lastObj = [...filteredaudit].reverse().find(obj => obj.hasOwnProperty('SignedOn'));
const completedAt = lastObj.SignedOn;
const doc = { ..._docRes, completedAt: completedAt };
const certificate = await GenerateCertificate(doc);
const certificatePdf = await PDFDocument.load(certificate);
const p12 = new P12Signer(P12Buffer, { passphrase: process.env.PASS_PHRASE || null });
// `pdflibAddPlaceholder` is used to add code of only digitial sign in certificate
pdflibAddPlaceholder({
pdfDoc: certificatePdf,
reason: 'Digitally signed by OpenSign.',
location: 'n/a',
name: eSignName,
contactInfo: eSigncontact,
signatureLength: 15000,
});
const pdfWithPlaceholderBytes = await certificatePdf.save();
const CertificateBuffer = Buffer.from(pdfWithPlaceholderBytes);
//`new signPDF` create new instance of CertificateBuffer and p12Buffer
const certificateOBJ = new SignPdf();
// `signedCertificate` is used to sign certificate digitally
const signedCertificate = await certificateOBJ.sign(CertificateBuffer, p12);
//below is used to save signed certificate in exports folder
fs.writeFileSync(certificatePath, signedCertificate);
const file = await uploadFile(
'certificate.pdf',
certificatePath,
);
const updateDoc = new Parse.Object('contracts_Document');
updateDoc.id = doc.objectId;
updateDoc.set('CertificateUrl', file.imageUrl);
const updateDocRes = await updateDoc.save(null, { useMasterKey: true });
unlinkCertificate(certificatePath);
return { CertificateUrl: file.imageUrl };
} else {
return { CertificateUrl: '' };
}
} catch (error) {
console.error('Error fetching or processing document:', error);
const code = error?.code || 400;
const message = error?.message || 'Something went wrong.';
unlinkCertificate(certificatePath);
throw new Parse.Error(code, message);
}
}
@@ -1,26 +0,0 @@
export default async function getAllUserTeamByOrg(request) {
const OrgId = request.params.orgId;
if (!request?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
try {
const teamCls = new Parse.Query('contracts_Teams');
teamCls.equalTo('OrganizationId', {
__type: 'Pointer',
className: 'contracts_Organizations',
objectId: OrgId,
});
teamCls.equalTo('IsActive', true);
const teamRes = await teamCls.first({ useMasterKey: true });
if (teamRes) {
return teamRes;
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Team not found.');
}
} catch (err) {
console.log('err in getOrganizations', err);
const code = err?.code || 400;
const msg = err?.message || 'Something went wrong.';
throw new Parse.Error(code, msg);
}
}
@@ -1,11 +1,10 @@
import axios from 'axios';
import { cloudServerUrl, parseJwt } from '../../Utils.js';
import jwt from 'jsonwebtoken';
import {
cloudServerUrl,
} from '../../Utils.js';
export default async function getDocument(request) {
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const docId = request.params.docId;
const jwttoken = request?.headers?.jwttoken || '';
const sessiontoken = request?.headers?.sessiontoken || '';
try {
if (docId) {
@@ -48,38 +47,8 @@ export default async function getDocument(request) {
console.log('err user in not authenticated', err);
return { error: "You don't have access of this document!" };
}
} else if (jwttoken) {
try {
const jwtDecode = parseJwt(jwttoken);
if (jwtDecode?.user_email) {
const userCls = new Parse.Query(Parse.User);
userCls.equalTo('email', jwtDecode?.user_email);
const userRes = await userCls.first({ useMasterKey: true });
const userId = userRes?.id;
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('userId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
const appRes = await tokenQuery.first({ useMasterKey: true });
const decoded = jwt.verify(jwttoken, appRes?.get('token'));
if (decoded?.user_email) {
const acl = res.getACL();
if (userId && acl && acl.getReadAccess(userId)) {
return document;
} else {
return { error: "You don't have access of this document!" };
}
} else {
return { status: 'error', result: 'Invalid token!' };
}
}
} catch (err) {
console.log('err in jwt', err);
return { error: "You don't have access of this document!" };
}
} else {
}
else {
return { error: "You don't have access of this document!" };
}
}
@@ -16,25 +16,29 @@ export default async function getDrive(request) {
});
const userId = userRes.data && userRes.data.objectId;
if (userId) {
let url;
if (docId) {
url = `${classUrl}?where={"Folder":{"__type":"Pointer","className":"contracts_Document","objectId":"${docId}"},"CreatedBy":{"__type":"Pointer","className":"_User","objectId":"${userId}"},"IsArchive":{"$ne":true}}&include=ExtUserPtr,ExtUserPtr.TenantId,Signers,Folder&order=-updatedAt&skip=${skip}&limit=${limit}`;
} else {
url = `${classUrl}?where={"Folder":{"$exists":false},"CreatedBy":{"__type":"Pointer","className":"_User","objectId":"${userId}"},"IsArchive":{"$ne":true}}&include=ExtUserPtr,ExtUserPtr.TenantId,Signers&order=-updatedAt&skip=${skip}&limit=${limit}`;
}
try {
const res = await axios.get(url, {
headers: {
'X-Parse-Application-Id': appId,
'X-Parse-Master-key': process.env.MASTER_KEY,
},
});
// console.log('res.data.results ', res.data.results);
if (res.data && res.data.results) {
return res.data.results;
const query = new Parse.Query('contracts_Document');
if (docId) {
query.equalTo('Folder', {
__type: 'Pointer',
className: 'contracts_Document',
objectId: docId,
});
query.include('Folder');
} else {
return [];
query.doesNotExist('Folder', true);
}
query.equalTo('CreatedBy', { __type: 'Pointer', className: '_User', objectId: userId });
query.include('ExtUserPtr');
query.include('ExtUserPtr.TenantId');
query.include('Signers');
query.notEqualTo('IsArchive', true);
query.descending('updatedAt');
query.skip(skip);
query.limit(limit);
query.exclude('AuditTrail');
const res = await query.find({ useMasterKey: true });
return res;
} catch (err) {
console.log('err', err);
return { error: "You don't have access to drive" };
@@ -1,25 +0,0 @@
export default async function getFileAdapter(request) {
if (!request?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
try {
const extUserCls = new Parse.Query('contracts_Users');
extUserCls.equalTo('UserId', request.user);
extUserCls.include('TenantId');
const extUser = await extUserCls.first({ useMasterKey: true });
if (extUser) {
const _extUser = JSON.parse(JSON.stringify(extUser));
const FileAdapters =
_extUser?.TenantId?.FileAdapters?.length > 0 ? _extUser?.TenantId?.FileAdapters : [];
const ActiveFileAdapter = _extUser?.TenantId?.ActiveFileAdapter || 'opensign';
return { ActiveFileAdapter: ActiveFileAdapter, FileAdapters: FileAdapters };
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
}
} catch (err) {
console.log('Err in add custom file adapter', err);
const code = err.code || 400;
const msg = err.message || 'Something went wrong.';
throw new Parse.Error(code, msg);
}
}
@@ -1,44 +0,0 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const appId = process.env.APP_ID;
export default async function getInvoices(request) {
const limit = request.params.limit || 100;
const skip = request.params.skip || 0;
const extUserId = request.params.extUserId;
try {
const userRes = await axios.get(serverUrl + '/users/me', {
headers: {
'X-Parse-Application-Id': appId,
'X-Parse-Session-Token': request.headers['sessiontoken'],
},
});
const userId = userRes.data && userRes.data.objectId;
if (userId) {
if (!extUserId) {
return { status: 'error', result: 'Please provide parameter!' };
}
const invoiceCls = new Parse.Query('contracts_Invoices');
invoiceCls.equalTo('ExtUserPtr', {
__type: 'Pointer',
className: 'contracts_Users',
objectId: extUserId,
});
invoiceCls.limit(limit);
invoiceCls.skip(skip);
invoiceCls.descending('createdAt');
const invoices = await invoiceCls.find({ useMasterKey: true });
if (invoices?.length > 0) {
const _invoices = JSON.parse(JSON.stringify(invoices));
return { status: 'success', result: _invoices };
} else {
return { status: 'success', result: [] };
}
} else {
return { status: 'error', result: 'Invalid session token!' };
}
} catch (err) {
console.log('Err in get invoices', err.message);
return { status: 'error', result: err.message };
}
}
@@ -1,24 +0,0 @@
export default async function getOrgAdmins(req) {
if (!req?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
} else {
try {
const extUser = new Parse.Query('contracts_Users');
extUser.equalTo('UserRole', 'contracts_OrgAdmin');
extUser.equalTo('CreatedBy', req?.user);
extUser.notEqualTo('UserId', req?.user);
extUser.include('TeamIds,OrganizationId');
extUser.descending('createdAt');
const userRes = await extUser.find({ useMasterKey: true });
if (userRes.length > 0) {
const _userRes = JSON.parse(JSON.stringify(userRes));
return _userRes;
} else {
return [];
}
} catch (err) {
console.log('err in getOrgAdmins', err);
throw err;
}
}
}
@@ -1,39 +0,0 @@
export default async function getOrganizations(request) {
const limit = request.params.limit || 200;
const skip = request.params.skip || 0;
const extUserId = request.params.extUserId;
const activeOrgs = request.params.active;
if (!request?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
try {
const orgQuery = new Parse.Query('contracts_Organizations');
orgQuery.equalTo('CreatedBy', {
__type: 'Pointer',
className: '_User',
objectId: request.user.id,
});
orgQuery.equalTo('ExtUserId', {
__type: 'Pointer',
className: 'contracts_Users',
objectId: extUserId,
});
if (activeOrgs) {
orgQuery.equalTo('IsActive', true);
}
orgQuery.exclude('ExtUserId');
orgQuery.limit(limit);
orgQuery.skip(skip);
const resOrg = await orgQuery.find({ useMasterKey: true });
if (resOrg && resOrg.length > 0) {
return resOrg;
} else {
return [];
}
} catch (err) {
console.log('err in getOrganizations', err);
const code = err?.code || 400;
const msg = err?.message || 'Something went wrong.';
throw new Parse.Error(code, msg);
}
}
@@ -1,41 +0,0 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const appId = process.env.APP_ID;
export default async function getPayments(request) {
const limit = request.params.limit || 100;
const skip = request.params.skip || 0;
const extUserId = request.params.extUserId;
try {
const userRes = await axios.get(serverUrl + '/users/me', {
headers: {
'X-Parse-Application-Id': appId,
'X-Parse-Session-Token': request.headers['sessiontoken'],
},
});
const userId = userRes.data && userRes.data.objectId;
if (userId) {
const paymentsCls = new Parse.Query('contracts_Payments');
paymentsCls.equalTo('ExtUserPtr', {
__type: 'Pointer',
className: 'contracts_User',
objectId: extUserId,
});
paymentsCls.limit(limit);
paymentsCls.skip(skip);
paymentsCls.descending('createdAt');
const payments = await paymentsCls.find({ useMasterKey: true });
if (payments?.length > 0) {
const _payments = JSON.parse(JSON.stringify(payments));
return { status: 'success', result: _payments };
} else {
return { status: 'success', result: [] };
}
} else {
return { status: 'error', result: 'Invalid session token!' };
}
} catch (err) {
console.log('Err in get Payments', err.message);
return { status: 'error', result: err.message };
}
}
@@ -1,25 +0,0 @@
export default async function getPfxFile(request) {
if (!request?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
try {
const extUserCls = new Parse.Query('contracts_Users');
extUserCls.equalTo('UserId', request.user);
extUserCls.include('TenantId');
const extUser = await extUserCls.first({ useMasterKey: true });
if (extUser) {
const _extUser = JSON.parse(JSON.stringify(extUser));
const title = _extUser?.TenantId?.PfxFile?.title || '';
const pfxBase64 = _extUser?.TenantId?.PfxFile?.base64 || '';
const password = _extUser?.TenantId?.PfxFile?.password || '';
return { title: title, password: password, base64: pfxBase64 };
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
}
} catch (err) {
console.log('Err in add custom file adapter', err);
const code = err.code || 400;
const msg = err.message || 'Something went wrong.';
throw new Parse.Error(code, msg);
}
}
@@ -1,29 +1,47 @@
import AWS from 'aws-sdk';
import { useLocal } from '../../Utils.js';
export default function getPresignedUrl(url, adapter) {
const credentials = {
accessKeyId: adapter?.accessKeyId || process.env.DO_ACCESS_KEY_ID,
secretAccessKey: adapter?.secretAccessKey || process.env.DO_SECRET_ACCESS_KEY,
};
AWS.config.update({ credentials: credentials, region: adapter?.region || process.env.DO_REGION });
const spacesEndpoint = adapter?.endpoint || new AWS.Endpoint(process.env.DO_ENDPOINT);
import jwt from 'jsonwebtoken';
import dotenv from 'dotenv';
dotenv.config();
const s3 = new AWS.S3({ endpoint: spacesEndpoint, signatureVersion: "v4" });
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,
};
AWS.config.update({
credentials: credentials,
region:
process.env.DO_REGION,
});
const spacesEndpoint =
new AWS.Endpoint(process.env.DO_ENDPOINT);
// 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 s3 = new AWS.S3({ endpoint: spacesEndpoint, signatureVersion: 'v4' });
// presignedGETURL return presignedUrl with expires time
const presignedGETURL = s3.getSignedUrl('getObject', {
Bucket: adapter?.bucketName || process.env.DO_SPACE,
Key: filename, //filename
Expires: 160, //time to expire in seconds
});
return presignedGETURL;
// 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);
// 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
});
return presignedGETURL;
}
}
export async function getSignedUrl(request) {
@@ -31,10 +49,13 @@ export async function getSignedUrl(request) {
const docId = request.params.docId || '';
const templateId = request.params.templateId || '';
const url = request.params.url;
const fileAdapterId = request.params.fileAdapterId || '';
if (docId || templateId) {
try {
if (fileAdapterId || useLocal !== 'true') {
if (url?.includes('files')) {
return presignedlocalUrl(url);
} else if (
useLocal !== 'true'
) {
const query = new Parse.Query(docId ? 'contracts_Document' : 'contracts_Template');
query.equalTo('objectId', docId ? docId : templateId);
query.include('ExtUserPtr.TenantId');
@@ -49,26 +70,15 @@ export async function getSignedUrl(request) {
'User is not authenticated.'
);
} else {
let adapterConfig = {};
if (fileAdapterId) {
// `adapterConfig` is used to get file in user's fileAdapter
adapterConfig =
_resDoc?.ExtUserPtr?.TenantId?.FileAdapters?.find(
x => x.id === fileAdapterId
) || {};
}
const presignedUrl = getPresignedUrl(url, adapterConfig);
const presignedUrl = getPresignedUrl(
url,
);
return presignedUrl;
}
} else {
let adapterConfig = {};
if (fileAdapterId) {
// `adapterConfig` is used to get file in user's fileAdapter
adapterConfig =
_resDoc?.ExtUserPtr?.TenantId?.FileAdapters?.find(x => x.id === fileAdapterId) ||
{};
}
const presignedUrl = getPresignedUrl(url, adapterConfig);
const presignedUrl = getPresignedUrl(
url,
);
return presignedUrl;
}
}
@@ -83,7 +93,9 @@ export async function getSignedUrl(request) {
if (!request?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
} else {
if (useLocal !== 'true') {
if (url?.includes('files')) {
return presignedlocalUrl(url);
} else if (useLocal !== 'true') {
const presignedUrl = getPresignedUrl(url);
return presignedUrl;
} else {
@@ -99,3 +111,71 @@ export async function getSignedUrl(request) {
throw error;
}
}
// Function to generate a signed URL with JWT
export function getSignedLocalUrl(fileUrl, expirationTimeInSeconds) {
const secretKey = process.env.MASTER_KEY;
const exp = expirationTimeInSeconds || 200;
try {
// Create the payload with the file URL and expiration time
const payload = {
fileUrl,
exp: Math.floor(Date.now() / 1000) + exp, // Expiry time in seconds
};
// Generate the JWT token
const token = jwt.sign(payload, secretKey);
// Return the signed URL containing the token
return `${fileUrl}?token=${token}`;
} catch (err) {
console.log('Err while siging local url', err);
throw new Error('Invalid or expired token.');
}
}
export function presignedlocalUrl(signedUrl, expirationTimeInSeconds) {
if (signedUrl?.includes('files')) {
const fileUrl = signedUrl.split('?')?.[0];
const secretKey = process.env.MASTER_KEY;
const exp = expirationTimeInSeconds || 200;
try {
// Create the payload with the file URL and expiration time
const payload = {
fileUrl,
exp: Math.floor(Date.now() / 1000) + exp, // Expiry time in seconds
};
// Generate the JWT token
const token = jwt.sign(payload, secretKey);
// Return the signed URL containing the token
return `${fileUrl}?token=${token}`;
} catch (err) {
throw new Error('Invalid or expired token.');
}
} else {
return signedUrl;
}
}
// Function to validate the signed URL
export async function validateSignedLocalUrl(signedUrl) {
const urlParams = new URLSearchParams(signedUrl.split('?')[1]);
const token = urlParams.get('token');
try {
if (!token) {
throw new Error('No token provided.');
}
const secretKey = process.env.MASTER_KEY;
// Now verify the token (validate signature and expiration automatically)
const decoded = jwt.verify(token, secretKey);
// Check if the file URL in the JWT matches the requested file URL
const fileUrl = signedUrl.split('?')[0];
if (decoded.fileUrl !== fileUrl) {
throw new Error('Invalid file URL in token.');
}
// If the token is valid and not expired, return the file URL
return signedUrl;
} catch (error) {
console.log('Error validating file', error.message);
return 'Unauthorized';
}
}
@@ -1,61 +1,42 @@
import { parseJwt } from '../../Utils.js';
import jwt from 'jsonwebtoken';
// Function to escape special characters in the search string
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // Escape special characters
}
async function getContacts(searchObj, isJWT) {
try {
const escapedSearch = escapeRegExp(searchObj.search); // Escape the search input
const searchRegex = new RegExp(escapedSearch, 'i'); // Create regex once to reuse
const contactNameQuery = new Parse.Query('contracts_Contactbook');
contactNameQuery.matches('Name', searchRegex);
const conatctEmailQuery = new Parse.Query('contracts_Contactbook');
conatctEmailQuery.matches('Email', searchRegex);
// Combine the two queries with OR
const mainQuery = Parse.Query.or(contactNameQuery, conatctEmailQuery);
// Add the common condition for 'CreatedBy'
mainQuery.equalTo('CreatedBy', searchObj.CreatedBy);
mainQuery.notEqualTo('IsDeleted', true);
const findOpt = isJWT ? { useMasterKey: true } : { sessionToken: searchObj.sessionToken };
const contactRes = await mainQuery.find(findOpt);
const _contactRes = JSON.parse(JSON.stringify(contactRes));
return _contactRes;
} catch (err) {
console.log('err while fetch contacts', err);
throw err;
}
}
export default async function getSigners(request) {
const jwttoken = request.headers.jwttoken || '';
const search = request.params.search || '';
const searchEmail = request.params.searchEmail || '';
const searchObj = { search: request.params.search || '', sessionToken: '' };
try {
if (request.user) {
const contactbook = new Parse.Query('contracts_Contactbook');
contactbook.equalTo('CreatedBy', {
__type: 'Pointer',
className: '_User',
objectId: request?.user?.id,
});
if (search) {
contactbook.matches('Name', new RegExp(search, 'i'));
} else if (searchEmail) {
contactbook.matches('Email', new RegExp(searchEmail, 'i'));
}
contactbook.notEqualTo('IsDeleted', true);
const contactRes = await contactbook.find({ sessionToken: request.user.getSessionToken() });
const _contactRes = JSON.parse(JSON.stringify(contactRes));
return _contactRes;
} else if (jwttoken) {
const jwtDecode = parseJwt(jwttoken);
const userCls = new Parse.Query(Parse.User);
userCls.equalTo('email', jwtDecode?.user_email);
const userRes = await userCls.first({ useMasterKey: true });
const userId = userRes?.id;
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('userId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
const appRes = await tokenQuery.first({ useMasterKey: true });
const decoded = jwt.verify(jwttoken, appRes?.get('token'));
if (decoded?.user_email) {
const contactbook = new Parse.Query('contracts_Contactbook');
contactbook.equalTo('CreatedBy', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
if (search) {
contactbook.matches('Name', new RegExp(search, 'i'));
} else if (searchEmail) {
contactbook.matches('Email', new RegExp(searchEmail, 'i'));
}
contactbook.notEqualTo('IsDeleted', true);
const contactRes = await contactbook.find({ useMasterKey: true });
const _contactRes = JSON.parse(JSON.stringify(contactRes));
return _contactRes;
} else {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid token');
}
} else {
searchObj.CreatedBy = { __type: 'Pointer', className: '_User', objectId: request?.user?.id };
searchObj.sessionToken = request.user.getSessionToken();
return await getContacts(searchObj);
}
else {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid session token');
}
} catch (err) {
@@ -1,134 +0,0 @@
import axios from 'axios';
import { cloudServerUrl, parseJwt } from '../../Utils.js';
import jwt from 'jsonwebtoken';
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const appId = process.env.APP_ID;
export default async function getSubscription(request) {
const extUserId = request.params.extUserId || '';
const contactId = request.params.contactId || '';
const ispublic = request.params.ispublic || false;
const jwttoken = request.headers?.jwttoken || '';
if (jwttoken) {
const jwtDecode = parseJwt(jwttoken);
if (jwtDecode?.user_email) {
const verifyToken = jwttoken;
const userCls = new Parse.Query(Parse.User);
userCls.equalTo('email', jwtDecode?.user_email);
const userRes = await userCls.first({ useMasterKey: true });
const userId = userRes?.id;
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('userId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
const appRes = await tokenQuery.first({ useMasterKey: true });
const decoded = jwt.verify(verifyToken, appRes?.get('token'));
if (decoded?.user_email) {
const extCls = new Parse.Query('contracts_Users');
extCls.equalTo('Email', decoded?.user_email);
const exUser = await extCls.first({ useMasterKey: true });
if (exUser) {
const subscriptionCls = new Parse.Query('contracts_Subscriptions');
subscriptionCls.equalTo('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: exUser.get('TenantId').id,
});
subscriptionCls.descending('createdAt');
const subcripitions = await subscriptionCls.first({ useMasterKey: true });
if (subcripitions) {
const _subcripitions = JSON.parse(JSON.stringify(subcripitions));
return { status: 'success', result: _subcripitions };
} else {
return { status: 'success', result: {} };
}
} else {
return { status: 'error', result: 'User not found!' };
}
} else {
return { status: 'error', result: 'Invalid token!' };
}
}
} else if (extUserId) {
try {
let userId;
//`ispublic` is used in public profile to get subscription details
if (!ispublic) {
const userRes = await axios.get(serverUrl + '/users/me', {
headers: {
'X-Parse-Application-Id': appId,
'X-Parse-Session-Token': request.headers['sessiontoken'],
},
});
userId = userRes.data && userRes.data.objectId;
}
if (userId || ispublic) {
const extCls = new Parse.Query('contracts_Users');
const exUser = await extCls.get(extUserId, { useMasterKey: true });
if (exUser) {
const subscriptionCls = new Parse.Query('contracts_Subscriptions');
subscriptionCls.equalTo('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: exUser.get('TenantId').id,
});
subscriptionCls.descending('createdAt');
const subcripitions = await subscriptionCls.first({ useMasterKey: true });
if (subcripitions) {
const _subcripitions = JSON.parse(JSON.stringify(subcripitions));
return { status: 'success', result: _subcripitions };
} else {
return { status: 'success', result: {} };
}
} else {
return { status: 'error', result: 'User not found!' };
}
} else {
return { status: 'error', result: 'Invalid session token!' };
}
} catch (err) {
console.log('Err in get subscription', err.message);
return { status: 'error', result: err.message };
}
} else if (contactId) {
try {
const contactCls = new Parse.Query('contracts_Contactbook');
const contactUser = await contactCls.get(contactId, { useMasterKey: true });
if (contactUser) {
const subscriptionCls = new Parse.Query('contracts_Subscriptions');
subscriptionCls.equalTo('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: contactUser.get('TenantId').id,
});
subscriptionCls.descending('createdAt');
const subcripitions = await subscriptionCls.first({ useMasterKey: true });
if (subcripitions) {
const _subcripitions = JSON.parse(JSON.stringify(subcripitions));
if (_subcripitions.PlanCode === 'freeplan') {
return { status: 'success', result: { isSubscribed: false, plan: 'freeplan' } };
} else if (_subcripitions?.Next_billing_date?.iso) {
if (new Date(_subcripitions.Next_billing_date.iso) > new Date()) {
return { status: 'success', result: { isSubscribed: true } };
} else {
return { status: 'success', result: { isSubscribed: false } };
}
} else {
return { status: 'success', result: { isSubscribed: false } };
}
} else {
return { status: 'success', result: { isSubscribed: false } };
}
} else {
return { status: 'error', result: 'User not found!' };
}
} catch (err) {
console.log('Err in get subscription2', err.message);
return { status: 'error', result: err.message };
}
} else {
return { status: 'error', result: 'Invalid session token!' };
}
}
@@ -30,7 +30,7 @@ export default async function getTeams(request) {
return [];
}
} catch (err) {
console.log('err in getOrganizations', err);
console.log('err in getTeams', err);
const code = err?.code || 400;
const msg = err?.message || 'Something went wrong.';
throw new Parse.Error(code, msg);
@@ -1,5 +1,3 @@
import { parseJwt } from '../../Utils.js';
import jwt from 'jsonwebtoken';
async function getTenantByUserId(userId, contactId) {
try {
@@ -50,34 +48,13 @@ async function getTenantByUserId(userId, contactId) {
}
}
export default async function getTenant(request) {
const jwttoken = request.headers.jwttoken || '';
const userId = request.params.userId || '';
const contactId = request.params.contactId || '';
if (jwttoken) {
const jwtDecode = parseJwt(jwttoken);
if (jwtDecode?.user_email) {
const verifyToken = jwttoken;
const userCls = new Parse.Query(Parse.User);
userCls.equalTo('email', jwtDecode?.user_email);
const userRes = await userCls.first({ useMasterKey: true });
const apiUserId = userRes?.id;
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('userId', {
__type: 'Pointer',
className: '_User',
objectId: apiUserId,
});
const appRes = await tokenQuery.first({ useMasterKey: true });
const decoded = jwt.verify(verifyToken, appRes?.get('token'));
if (decoded?.user_email) {
return await getTenantByUserId(apiUserId, contactId);
} else {
return { status: 'error', result: 'Invalid token!' };
}
}
} else if (userId || contactId) {
if (userId || contactId) {
return await getTenantByUserId(userId, contactId);
} else {
}
else {
return {};
}
}
@@ -1,27 +0,0 @@
export default async function getUserByOrg(req) {
const OrganizationId = req.params.organizationId;
const orgPtr = {
__type: 'Pointer',
className: 'contracts_Organizations',
objectId: OrganizationId,
};
if (!req?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
} else {
try {
const extUser = new Parse.Query('contracts_Users');
extUser.include('TeamIds');
extUser.equalTo('OrganizationId', orgPtr);
const userRes = await extUser.first({ useMasterKey: true });
if (userRes.length > 0) {
const _userRes = JSON.parse(JSON.stringify(userRes));
return _userRes;
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
}
} catch (err) {
console.log('err in getuserlist', err);
throw err;
}
}
}
@@ -1,9 +1,5 @@
import { parseJwt } from '../../Utils.js';
import jwt from 'jsonwebtoken';
async function getUserDetails(request) {
const reqEmail = request.params.email;
const jwttoken = request?.headers?.jwttoken || '';
if (reqEmail || request.user) {
try {
const userId = request.params.userId;
@@ -40,49 +36,8 @@ async function getUserDetails(request) {
const msg = err?.message || 'Something went wrong.';
throw new Parse.Error(code, msg);
}
} else if (jwttoken) {
const jwtDecode = parseJwt(jwttoken);
if (jwtDecode?.user_email) {
const userCls = new Parse.Query(Parse.User);
userCls.equalTo('email', jwtDecode?.user_email);
const userRes = await userCls.first({ useMasterKey: true });
const userId = userRes?.id;
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('userId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
const appRes = await tokenQuery.first({ useMasterKey: true });
const decoded = jwt.verify(jwttoken, appRes?.get('token'));
if (decoded?.user_email) {
try {
const userQuery = new Parse.Query('contracts_Users');
userQuery.equalTo('Email', decoded?.user_email);
userQuery.include('TenantId');
userQuery.include('UserId');
userQuery.include('CreatedBy');
userQuery.exclude('CreatedBy.authData');
userQuery.exclude('TenantId.FileAdapters');
userQuery.exclude('google_refresh_token');
userQuery.exclude('TenantId.PfxFile');
const res = await userQuery.first({ useMasterKey: true });
if (res) {
return res;
} else {
return '';
}
} catch (err) {
console.log('Err ', err);
const code = err?.code || 400;
const msg = err?.message || 'Something went wrong.';
throw new Parse.Error(code, msg);
}
} else {
return { status: 'error', result: 'Invalid token!' };
}
}
} else {
}
else {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
}
}
@@ -1,34 +0,0 @@
import axios from 'axios';
import { cloudServerUrl } from '../../Utils.js';
export default async function getapitoken(request) {
try {
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const userRes = await axios.get(serverUrl + '/users/me', {
headers: {
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Session-Token': request.headers['sessiontoken'],
},
});
const userId = userRes.data && userRes.data.objectId;
if (userId) {
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('userId', { __type: 'Pointer', className: '_User', objectId: userId });
const res = await tokenQuery.first({ useMasterKey: true });
if (res) {
return { status: 'success', result: res.get('token') };
} else {
return { error: 'api token found.' };
}
} else {
return { error: 'Invalid session token.' };
}
} catch (err) {
console.log('Err in getapitoken', err);
if (err.code == 209) {
return { error: 'Invalid session token.' };
} else {
return { error: "You don't have access." };
}
}
}
@@ -1,36 +1,7 @@
import { parseJwt } from '../../Utils.js';
import jwt from 'jsonwebtoken';
export default async function isUserInContactBook(request) {
try {
const jwttoken = request.headers.jwttoken;
if (jwttoken) {
const jwtDecode = parseJwt(jwttoken);
const userCls = new Parse.Query(Parse.User);
userCls.equalTo('email', jwtDecode?.user_email);
const userRes = await userCls.first({ useMasterKey: true });
const userId = userRes?.id;
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('userId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
const appRes = await tokenQuery.first({ useMasterKey: true });
const decoded = jwt.verify(jwttoken, appRes?.get('token'));
if (decoded?.user_email) {
const email = userRes?.get('email');
const userPtr = { __type: 'Pointer', className: '_User', objectId: userId };
const query = new Parse.Query('contracts_Contactbook');
query.equalTo('CreatedBy', userPtr);
query.notEqualTo('IsDeleted', true);
query.equalTo('Email', email);
const res = await query.first({ useMasterKey: true });
return res;
} else {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid token');
}
} else if (request.user) {
if (request.user) {
const email = request.user.get('email');
const userPtr = { __type: 'Pointer', className: '_User', objectId: request.user?.id };
const query = new Parse.Query('contracts_Contactbook');
@@ -1,8 +1,10 @@
import { PDFDocument, StandardFonts, rgb } from 'pdf-lib';
import fs from 'node:fs';
import fontkit from '@pdf-lib/fontkit';
import { formatTimeInTimezone } from '../../../Utils.js';
export default async function GenerateCertificate(docDetails) {
const timezone = docDetails?.ExtUserPtr?.Timezone || '';
const pdfDoc = await PDFDocument.create();
// `fontBytes` is used to embed custom font in pdf
const fontBytes = fs.readFileSync('./font/times.ttf'); //
@@ -23,19 +25,23 @@ export default async function GenerateCertificate(docDetails) {
const timeText = 11;
const textKeyColor = rgb(0.12, 0.12, 0.12);
const textValueColor = rgb(0.3, 0.3, 0.3);
const completedAt = new Date();
const completedUTCtime = completedAt.toUTCString();
const completedAt = docDetails?.completedAt ? new Date(docDetails?.completedAt) : new Date();
const completedAtperTimezone = formatTimeInTimezone(completedAt, timezone);
const completedUTCtime = completedAtperTimezone;
const signersCount = docDetails?.Signers?.length || 1;
const generateAt = new Date();
const generatedUTCTime = generateAt.toUTCString();
const generateAt = docDetails?.completedAt ? new Date(docDetails?.completedAt) : new Date();
const generatedAtperTimezone = formatTimeInTimezone(generateAt, timezone);
const generatedUTCTime = generatedAtperTimezone;
const generatedOn = 'Generated On ' + generatedUTCTime;
const OriginIp = docDetails?.OriginIp || '';
const company = docDetails?.ExtUserPtr?.Company || '';
const createdAt = docDetails?.DocSentAt?.iso || docDetails.createdAt;
const createdAtperTimezone = formatTimeInTimezone(createdAt, timezone);
const IsEnableOTP = docDetails?.IsEnableOTP || false;
const filteredaudit = docDetails?.AuditTrail?.filter(x => x?.UserPtr?.objectId);
const auditTrail =
docDetails?.Signers?.length > 0
? docDetails.AuditTrail.map(x => {
? filteredaudit?.map(x => {
const data = docDetails.Signers.find(y => y.objectId === x.UserPtr.objectId);
return {
...data,
@@ -48,16 +54,15 @@ export default async function GenerateCertificate(docDetails) {
: [
{
...docDetails.ExtUserPtr,
ipAddress: docDetails?.AuditTrail[0].ipAddress,
SignedOn: docDetails?.AuditTrail[0]?.SignedOn || generatedUTCTime,
ViewedOn:
docDetails?.AuditTrail[0]?.ViewedOn ||
docDetails?.AuditTrail[0]?.SignedOn ||
generatedUTCTime,
Signature: docDetails?.AuditTrail[0]?.Signature || '',
ipAddress: filteredaudit[0].ipAddress,
SignedOn: filteredaudit[0]?.SignedOn || generatedUTCTime,
ViewedOn: filteredaudit[0]?.ViewedOn || filteredaudit[0]?.SignedOn || generatedUTCTime,
Signature: filteredaudit[0]?.Signature || '',
},
];
const ownerName = docDetails?.SenderName || docDetails.ExtUserPtr?.Name || 'n/a';
const ownerEmail = docDetails?.SenderMail || docDetails.ExtUserPtr?.Email || 'n/a';
const half = width / 2;
// Draw a border
page.drawRectangle({
@@ -131,10 +136,10 @@ export default async function GenerateCertificate(docDetails) {
color: textKeyColor,
});
page.drawText(docDetails.Name, {
page.drawText(docDetails?.Name, {
x: 140,
y: 665,
size: text,
size: docDetails?.Name?.length >= 78 ? 12 : text,
font: timesRomanFont,
color: textValueColor,
});
@@ -162,7 +167,7 @@ export default async function GenerateCertificate(docDetails) {
color: textKeyColor,
});
page.drawText(`${new Date(createdAt).toUTCString()}`, {
page.drawText(`${createdAtperTimezone}`, {
x: 105,
y: 625,
size: text,
@@ -213,7 +218,7 @@ export default async function GenerateCertificate(docDetails) {
font: timesRomanFont,
color: textKeyColor,
});
page.drawText(`${docDetails.ExtUserPtr.Name}`, {
page.drawText(ownerName, {
x: 105,
y: 545,
size: text,
@@ -227,7 +232,7 @@ export default async function GenerateCertificate(docDetails) {
font: timesRomanFont,
color: textKeyColor,
});
page.drawText(`${docDetails.ExtUserPtr.Email}`, {
page.drawText(ownerEmail, {
x: 105,
y: 525,
size: text,
@@ -287,15 +292,16 @@ export default async function GenerateCertificate(docDetails) {
});
page.drawText('Viewed on :', {
x: half + 55,
x: half + 45,
y: yPosition2,
size: timeText,
font: timesRomanFont,
color: textKeyColor,
});
page.drawText(`${new Date(x.ViewedOn).toUTCString()}`, {
x: half + 112,
//new Date(x.ViewedOn).toUTCString()
page.drawText(`${formatTimeInTimezone(x.ViewedOn, timezone)}`, {
x: half + 102,
y: yPosition2,
size: timeText,
font: timesRomanFont,
@@ -319,15 +325,16 @@ export default async function GenerateCertificate(docDetails) {
});
page.drawText('Signed on :', {
x: half + 55,
x: half + 45,
y: yPosition3 + 5,
size: timeText,
font: timesRomanFont,
color: textKeyColor,
});
page.drawText(`${new Date(x.SignedOn).toUTCString()}`, {
x: half + 108,
// new Date(x.SignedOn).toUTCString()
page.drawText(`${formatTimeInTimezone(x.SignedOn, timezone)}`, {
x: half + 98,
y: yPosition3 + 5,
size: timeText,
font: timesRomanFont,
@@ -351,14 +358,14 @@ export default async function GenerateCertificate(docDetails) {
});
if (IsEnableOTP) {
page.drawText('Security level :', {
x: half + 55,
x: half + 45,
y: yPosition4 + 10,
size: timeText,
font: timesRomanFont,
color: textKeyColor,
});
page.drawText('Email, OTP Auth', {
x: half + 125,
x: half + 115,
y: yPosition4 + 10,
size: timeText,
font: timesRomanFont,
@@ -458,15 +465,16 @@ export default async function GenerateCertificate(docDetails) {
});
currentPage.drawText('Viewed on :', {
x: half + 55,
x: half + 45,
y: yPosition2,
size: timeText,
font: timesRomanFont,
color: textKeyColor,
});
currentPage.drawText(`${new Date(x.ViewedOn).toUTCString()}`, {
x: half + 112,
// new Date(x.ViewedOn).toUTCString()
currentPage.drawText(`${formatTimeInTimezone(x.ViewedOn, timezone)}`, {
x: half + 102,
y: yPosition2,
size: timeText,
font: timesRomanFont,
@@ -490,15 +498,16 @@ export default async function GenerateCertificate(docDetails) {
});
currentPage.drawText('Signed on :', {
x: half + 55,
x: half + 45,
y: yPosition3 + 5,
size: timeText,
font: timesRomanFont,
color: textKeyColor,
});
currentPage.drawText(`${new Date(x.SignedOn).toUTCString()}`, {
x: half + 108,
// new Date(x.SignedOn).toUTCString()
currentPage.drawText(`${formatTimeInTimezone(x.SignedOn, timezone)}`, {
x: half + 98,
y: yPosition3 + 5,
size: timeText,
font: timesRomanFont,
@@ -523,14 +532,14 @@ export default async function GenerateCertificate(docDetails) {
if (IsEnableOTP) {
currentPage.drawText('Security level :', {
x: half + 55,
x: half + 45,
y: yPosition4 + 10,
size: timeText,
font: timesRomanFont,
color: textKeyColor,
});
currentPage.drawText(`Email, OTP Auth`, {
x: half + 125,
x: half + 115,
y: yPosition4 + 10,
size: timeText,
font: timesRomanFont,
+121 -178
View File
@@ -4,37 +4,31 @@ import { SignPdf } from '@signpdf/signpdf';
import { P12Signer } from '@signpdf/signer-p12';
import { pdflibAddPlaceholder } from '@signpdf/placeholder-pdf-lib';
import { PDFDocument } from 'pdf-lib';
import { cloudServerUrl, replaceMailVaribles, saveFileUsage } from '../../../Utils.js';
import {
cloudServerUrl,
replaceMailVaribles,
saveFileUsage,
getSecureUrl,
} from '../../../Utils.js';
import GenerateCertificate from './GenerateCertificate.js';
import uploadFileToS3 from '../uploadFiletoS3.js';
import { Placeholder } from './Placeholder.js';
const serverUrl = cloudServerUrl; // process.env.SERVER_URL;
const APPID = process.env.APP_ID;
const masterKEY = process.env.MASTER_KEY;
const eSignName = 'opensign';
const eSigncontact = 'hello@opensignlabs.com';
// `updateDoc` is used to create url in from pdfFile
async function uploadFile(pdfName, filepath, adapter) {
async function uploadFile(
pdfName,
filepath,
) {
try {
const filedata = fs.readFileSync(filepath);
let fileUrl;
if (adapter?.bucketName) {
const adapterConfig = {
id: adapter?.id,
fileAdapter: adapter?.fileAdapter,
bucketName: adapter?.bucketName,
region: adapter?.region,
endpoint: adapter?.endpoint,
accessKeyId: adapter?.accessKeyId,
secretAccessKey: adapter?.secretAccessKey,
baseUrl: adapter?.baseUrl,
};
// `uploadFileToS3` is used to save document in user's file storage
fileUrl = await uploadFileToS3(filedata, pdfName, 'application/pdf', adapterConfig);
} else {
const file = new Parse.File(pdfName, [...filedata], 'application/pdf');
await file.save({ useMasterKey: true });
fileUrl = file.url();
}
const fileRes = getSecureUrl(file.url());
fileUrl = fileRes.url;
return { imageUrl: fileUrl };
} catch (err) {
@@ -161,67 +155,80 @@ async function sendCompletedMail(obj) {
const recipient = signersMail;
let subject = `Document "${pdfName}" has been signed by all parties`;
let body =
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body> <div style='background-color:#f5f5f5;padding:20px'> <div style='box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;background-color:white;'> <div><img src=" +
mailLogo +
" height='50' style='padding:20px'/> </div><div style='padding:2px;font-family:system-ui; background-color: #47a3ad;'> <p style='font-size:20px;font-weight:400;color:white;padding-left:20px',> Document signed successfully</p></div><div><p style='padding:20px;font-family:system-ui;font-size:14px'>All parties have successfully signed the document " +
`<b>"${pdfName}"</b>` +
'. Kindly download the document from the attachment.</p></div> </div><div><p>This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender ' +
sender.Email +
' directly. If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href=www.opensignlabs.com target=_blank>here</a>.</p></div></div></body></html>';
"<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><img src=${mailLogo} height='50' style='padding:20px'/></div><div style='padding:2px;font-family:system-ui; background-color: #47a3ad;'>` +
`<p style='font-size:20px;font-weight:400;color:white;padding-left:20px;'>Document signed successfully</p></div><div>` +
`<p style='padding:20px;font-family:system-ui;font-size:14px;'>All parties have successfully signed the document <b>"${pdfName}"</b>. Kindly download the document from the attachment.</p>` +
`</div> </div><div><p>This is an automated email from OpenSign™. For any queries regarding this email, please contact the sender ${sender.Email} directly.` +
'If you think this email is inappropriate or spam, you may file a complaint with OpenSign™ <a href=www.opensignlabs.com target=_blank>here</a>.</p></div></div></body></html>';
if (obj?.isCustomMail) {
try {
const tenantCreditsQuery = new Parse.Query('partners_Tenant');
tenantCreditsQuery.equalTo('UserId', {
__type: 'Pointer',
className: '_User',
objectId: sender.UserId.objectId,
});
const res = await tenantCreditsQuery.first();
if (res) {
const _res = JSON.parse(JSON.stringify(res));
if (_res?.CompletionSubject) {
subject = _res?.CompletionSubject;
const tenant = sender?.TenantId;
if (tenant) {
subject = tenant?.CompletionSubject || '';
body = tenant?.CompletionBody || '';
} else {
const userId = sender?.CreatedBy?.objectId || sender?.UserId?.objectId;
if (userId) {
try {
const tenantQuery = new Parse.Query('partners_Tenant');
tenantQuery.equalTo('UserId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
const tenantRes = await tenantQuery.first();
if (tenantRes) {
const _tenantRes = JSON.parse(JSON.stringify(tenantRes));
subject = _tenantRes?.CompletionSubject || '';
body = _tenantRes?.CompletionBody || '';
}
} catch (err) {
console.log('error in fetch tenant in signpdf', err.message);
}
if (_res?.CompletionBody) {
body = _res?.CompletionBody;
}
const expireDate = doc.ExpiryDate.iso;
const newDate = new Date(expireDate);
const localExpireDate = newDate.toLocaleDateString('en-US', {
day: 'numeric',
month: 'long',
year: 'numeric',
});
const variables = {
document_title: pdfName,
sender_name: sender.Name,
sender_mail: sender.Email,
sender_phone: sender?.Phone || '',
receiver_name: sender.Name,
receiver_email: sender.Email,
receiver_phone: sender?.Phone || '',
expiry_date: localExpireDate,
company_name: sender.Company,
};
const replaceVar = replaceMailVaribles(subject, body, variables);
subject = replaceVar.subject;
body = replaceVar.body;
}
} catch (err) {
console.log('error in fetch tenant in signpdf', err.message);
}
const expireDate = doc.ExpiryDate.iso;
const newDate = new Date(expireDate);
const localExpireDate = newDate.toLocaleDateString('en-US', {
day: 'numeric',
month: 'long',
year: 'numeric',
});
const variables = {
document_title: pdfName,
sender_name:
sender.Name,
sender_mail: doc?.SenderMail || sender.Email,
sender_phone: sender?.Phone || '',
receiver_name: sender.Name,
receiver_email: sender.Email,
receiver_phone: sender?.Phone || '',
expiry_date: localExpireDate,
company_name: sender.Company,
};
const replaceVar = replaceMailVaribles(subject, body, variables);
subject = replaceVar.subject;
body = replaceVar.body;
}
const Bcc = doc?.Bcc?.length > 0 ? doc.Bcc.map(x => x.Email) : '';
const params = {
extUserId: sender.objectId,
url: url,
from: 'OpenSign™',
from:
'OpenSign™',
replyto:
doc?.ExtUserPtr?.Email ||
'',
recipient: recipient,
subject: subject,
pdfName: pdfName,
html: body,
mailProvider: obj.mailProvider,
bcc: Bcc,
certificatePath: `./exports/certificate_${doc.objectId}.pdf`,
filename: obj?.filename,
};
const res = await axios.post(serverUrl + '/functions/sendmailv3', params, {
headers: {
@@ -232,72 +239,15 @@ async function sendCompletedMail(obj) {
});
}
// `sendDoctoWebhook` is used to send res data of document on webhook
async function sendDoctoWebhook(doc, Url, event, signUser, certificateUrl) {
let signers = [];
if (signUser) {
signers = { name: signUser?.Name, email: signUser?.Email, phone: signUser?.Phone };
} else {
signers = doc?.Signers?.map(x => ({ name: x.Name, email: x.Email, phone: x.Phone })) || [
{ name: doc?.ExtUserPtr?.Name, email: doc?.ExtUserPtr?.Email, phone: doc?.ExtUserPtr?.Phone },
];
}
if (doc.ExtUserPtr?.Webhook) {
const time =
event === 'signed'
? { signer: signers, signedAt: new Date() }
: { signers: signers, completedAt: new Date() };
const certificate = certificateUrl ? { certificate: certificateUrl } : {};
const params = {
event: event,
objectId: doc?.objectId,
file: Url || '',
...certificate,
name: doc?.Name,
note: doc?.Note || '',
description: doc?.Description || '',
...time,
createdAt: doc?.createdAt,
};
axios
.post(doc?.ExtUserPtr?.Webhook, params, {
headers: { 'Content-Type': 'application/json' },
})
.then(res => {
try {
const webhook = new Parse.Object('contracts_Webhook');
webhook.set('Log', res?.status);
webhook.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: doc.ExtUserPtr.UserId.objectId,
});
webhook.save(null, { useMasterKey: true });
} catch (err) {
console.log('err save in contracts_Webhook', err.message);
}
})
.catch(err => {
console.log('Err send data to webhook', err.message);
try {
const webhook = new Parse.Object('contracts_Webhook');
webhook.set('Log', err?.status);
webhook.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: doc.ExtUserPtr.UserId.objectId,
});
webhook.save(null, { useMasterKey: true });
} catch (err) {
console.log('err save in contracts_Webhook', err.message);
}
});
}
}
// `sendMailsaveCertifcate` is used send completion mail and update complete status of document
async function sendMailsaveCertifcate(doc, P12Buffer, isCustomMail, mailProvider, adapterConfig) {
async function sendMailsaveCertifcate(
doc,
P12Buffer,
isCustomMail,
mailProvider,
filename
) {
const certificate = await GenerateCertificate(doc);
const certificatePdf = await PDFDocument.load(certificate);
let passphrase = process.env.PASS_PHRASE;
@@ -320,10 +270,13 @@ async function sendMailsaveCertifcate(doc, P12Buffer, isCustomMail, mailProvider
const certificateOBJ = new SignPdf();
// `signedCertificate` is used to sign certificate digitally
const signedCertificate = await certificateOBJ.sign(CertificateBuffer, p12);
const certificatePath = `./exports/certificate_${doc.objectId}.pdf`;
//below is used to save signed certificate in exports folder
fs.writeFileSync('./exports/certificate.pdf', signedCertificate);
const file = await uploadFile('certificate.pdf', './exports/certificate.pdf', adapterConfig);
fs.writeFileSync(certificatePath, signedCertificate);
const file = await uploadFile(
'certificate.pdf',
certificatePath,
);
const body = { CertificateUrl: file.imageUrl };
await axios.put(serverUrl + '/classes/contracts_Document/' + doc.objectId, body, {
headers: {
@@ -336,10 +289,9 @@ async function sendMailsaveCertifcate(doc, P12Buffer, isCustomMail, mailProvider
if (doc.IsSendMail === false) {
console.log("don't send mail");
} else {
sendCompletedMail({ isCustomMail, doc, mailProvider });
sendCompletedMail({ isCustomMail, doc, mailProvider, filename });
}
saveFileUsage(CertificateBuffer.length, file.imageUrl, doc?.CreatedBy?.objectId);
sendDoctoWebhook(doc, doc?.SignedUrl, 'completed', '', file.imageUrl);
}
/**
*
@@ -357,7 +309,7 @@ async function PDF(req) {
const sign = req.params.signature || '';
// below bode is used to get info of docId
const docQuery = new Parse.Query('contracts_Document');
docQuery.include('ExtUserPtr,Signers,ExtUserPtr.TenantId');
docQuery.include('ExtUserPtr,Signers,ExtUserPtr.TenantId,Bcc');
docQuery.equalTo('objectId', docId);
const resDoc = await docQuery.first({ useMasterKey: true });
if (!resDoc) {
@@ -371,17 +323,6 @@ async function PDF(req) {
}
}
const _resDoc = resDoc?.toJSON();
// `fileAdapterId` is used check document uploaded in custom file adapter and get customFileAdapter id
const fileAdapterId = _resDoc?.FileAdapterId || '';
let adapterConfig = {};
if (fileAdapterId) {
// `FileAdapter` is used to credintials of file adapter
const FileAdapter =
_resDoc?.ExtUserPtr?.TenantId?.FileAdapters?.find(x => x.id === fileAdapterId) || {};
if (FileAdapter) {
adapterConfig = FileAdapter;
}
}
let signUser;
let className;
// `reqUserId` is send throught pdfrequest signing flow
@@ -433,38 +374,32 @@ async function PDF(req) {
const randomNumber = Math.floor(Math.random() * 5000);
// below regex is used to replace all word with "_" except A to Z, a to z, numbers
const docName = _resDoc?.Name?.replace(/[^a-zA-Z0-9._-]/g, '_')?.toLowerCase();
const name = `signed_${docName}_${randomNumber}.pdf`;
const filename = docName?.length > 100 ? docName?.slice(0, 100) : docName;
const name = `signed_${filename}_${randomNumber}.pdf`;
const filePath = `./exports/${name}`;
let pdfSize = PdfBuffer.length;
if (isCompleted) {
const signersName = _resDoc.Signers?.map(x => x.Name + ' <' + x.Email + '>');
if (signersName && signersName.length > 0) {
// `pdflibAddPlaceholder` is used to add code of only digitial sign without widget
const pdfDoc = await PDFDocument.load(PdfBuffer);
pdflibAddPlaceholder({
pdfDoc: pdfDoc,
reason: 'Digitally signed by OpenSign for ' + signersName?.join(', '),
location: 'n/a',
name: eSignName,
contactInfo: eSigncontact,
signatureLength: 15000,
});
const pdfWithPlaceholderBytes = await pdfDoc.save();
PdfBuffer = Buffer.from(pdfWithPlaceholderBytes);
} else {
// `pdflibAddPlaceholder` is used to add code of only digitial sign without widget (signyourself)
const pdfDoc = await PDFDocument.load(PdfBuffer);
pdflibAddPlaceholder({
pdfDoc: pdfDoc,
reason: 'Digitally signed by OpenSign for ' + username + ' <' + userEmail + '>',
location: 'n/a',
name: eSignName,
contactInfo: eSigncontact,
signatureLength: 15000,
});
const pdfWithPlaceholderBytes = await pdfDoc.save();
PdfBuffer = Buffer.from(pdfWithPlaceholderBytes);
}
const reason =
signersName && signersName.length > 0
? signersName?.join(', ')
: username + ' <' + userEmail + '>';
const pdfDoc = await PDFDocument.load(PdfBuffer);
const form = pdfDoc.getForm();
// Updates the field appearances to ensure visual changes are reflected.
form.updateFieldAppearances();
// Flattens the form, converting all form fields into non-editable, static content
form.flatten();
Placeholder({
pdfDoc: pdfDoc,
reason: 'Digitally signed by OpenSign for ' + reason,
location: 'n/a',
name: eSignName,
contactInfo: eSigncontact,
signatureLength: 15000,
});
const pdfWithPlaceholderBytes = await pdfDoc.save();
PdfBuffer = Buffer.from(pdfWithPlaceholderBytes);
//`new signPDF` create new instance of pdfBuffer and p12Buffer
const OBJ = new SignPdf();
// `signedDocs` is used to signpdf digitally
@@ -480,7 +415,10 @@ async function PDF(req) {
}
// `uploadFile` is used to upload pdf to aws s3 and get it's url
const data = await uploadFile(name, filePath, adapterConfig);
const data = await uploadFile(
name,
filePath,
);
if (data && data.imageUrl) {
// `axios` is used to update signed pdf url in contracts_Document classes for given DocId
@@ -493,12 +431,17 @@ async function PDF(req) {
className, // className based on flow
sign // sign base64
);
sendDoctoWebhook(_resDoc, data.imageUrl, 'signed', signUser);
sendNotifyMail(_resDoc, signUser, mailProvider);
saveFileUsage(pdfSize, data.imageUrl, _resDoc?.CreatedBy?.objectId);
if (updatedDoc && updatedDoc.isCompleted) {
const doc = { ..._resDoc, AuditTrail: updatedDoc.AuditTrail, SignedUrl: data.imageUrl };
sendMailsaveCertifcate(doc, P12Buffer, isCustomMail, mailProvider, adapterConfig);
sendMailsaveCertifcate(
doc,
P12Buffer,
isCustomMail,
mailProvider,
name
);
}
// `fs.unlinkSync` is used to remove exported signed pdf file from exports folder
fs.unlinkSync(filePath);
@@ -0,0 +1,53 @@
import { PDFArray, CharCodes } from 'pdf-lib';
/**
* Extends PDFArray class in order to make ByteRange look like this:
* /ByteRange [0 /********** /********** /**********]
* Not this:
* /ByteRange [ 0 /********** /********** /********** ]
*/
export default class PDFArrayCustom extends PDFArray {
static withContext(context) {
return new PDFArrayCustom(context);
}
clone(context) {
const clone = PDFArrayCustom.withContext(context || this.context);
for (let idx = 0, len = this.size(); idx < len; idx++) {
clone.push(this.array[idx]);
}
return clone;
}
toString() {
let arrayString = '[';
for (let idx = 0, len = this.size(); idx < len; idx++) {
arrayString += this.get(idx).toString();
if (idx < len - 1) arrayString += ' ';
}
arrayString += ']';
return arrayString;
}
sizeInBytes() {
let size = 2;
for (let idx = 0, len = this.size(); idx < len; idx++) {
size += this.get(idx).sizeInBytes();
if (idx < len - 1) size += 1;
}
return size;
}
copyBytesInto(buffer, offset) {
const initialOffset = offset;
buffer[offset++] = CharCodes.LeftSquareBracket;
for (let idx = 0, len = this.size(); idx < len; idx++) {
offset += this.get(idx).copyBytesInto(buffer, offset);
if (idx < len - 1) buffer[offset++] = CharCodes.Space;
}
buffer[offset++] = CharCodes.RightSquareBracket;
return offset - initialOffset;
}
}
@@ -0,0 +1,130 @@
import {
DEFAULT_SIGNATURE_LENGTH,
DEFAULT_BYTE_RANGE_PLACEHOLDER,
SUBFILTER_ADOBE_PKCS7_DETACHED,
ANNOTATION_FLAGS,
SIG_FLAGS,
SignPdfError,
} from '@signpdf/utils';
import {
PDFArray,
PDFNumber,
PDFName,
PDFHexString,
PDFString,
PDFInvalidObject,
PDFDict,
} from 'pdf-lib';
export const Placeholder = ({
pdfDoc,
pdfPage,
reason,
contactInfo,
name,
location,
signingTime = new Date(),
signatureLength = DEFAULT_SIGNATURE_LENGTH,
byteRangePlaceholder = DEFAULT_BYTE_RANGE_PLACEHOLDER,
subFilter = SUBFILTER_ADOBE_PKCS7_DETACHED,
widgetRect = [0, 0, 0, 0],
appName,
}) => {
if (!pdfDoc && !pdfPage) {
throw new SignPdfError('PDFDoc or PDFPage must be set.', SignPdfError.TYPE_INPUT);
}
const doc = pdfDoc || pdfPage.doc;
const page = pdfPage || doc.getPages()[0];
const byteRange = PDFArray.withContext(doc.context);
byteRange.push(PDFNumber.of(0));
byteRange.push(PDFName.of(byteRangePlaceholder));
byteRange.push(PDFName.of(byteRangePlaceholder));
byteRange.push(PDFName.of(byteRangePlaceholder));
const placeholder = PDFHexString.of(String.fromCharCode(0).repeat(signatureLength));
const appBuild = appName ? { App: { Name: appName } } : {};
const signatureDict = doc.context.obj({
Type: 'Sig',
Filter: 'Adobe.PPKLite',
SubFilter: subFilter,
ByteRange: byteRange,
Contents: placeholder,
Reason: PDFString.of(reason),
M: PDFString.fromDate(signingTime),
ContactInfo: PDFString.of(contactInfo),
Name: PDFString.of(name),
Location: PDFString.of(location),
Prop_Build: {
Filter: { Name: 'Adobe.PPKLite' },
...appBuild,
},
});
const signatureBuffer = new Uint8Array(signatureDict.sizeInBytes());
signatureDict.copyBytesInto(signatureBuffer, 0);
const signatureObj = PDFInvalidObject.of(signatureBuffer);
const signatureDictRef = doc.context.register(signatureObj);
const rect = PDFArray.withContext(doc.context);
widgetRect.forEach(c => rect.push(PDFNumber.of(c)));
const apStream = doc.context.formXObject([], {
BBox: widgetRect,
Resources: {},
});
const widgetDict = doc.context.obj({
Type: 'Annot',
Subtype: 'Widget',
FT: 'Sig',
Rect: rect,
V: signatureDictRef,
T: PDFString.of('Signature1'),
F: ANNOTATION_FLAGS.PRINT,
P: page.ref,
AP: { N: doc.context.register(apStream) },
});
const widgetDictRef = doc.context.register(widgetDict);
let annotations = page.node.lookupMaybe(PDFName.of('Annots'), PDFArray);
if (!annotations) {
annotations = doc.context.obj([]);
}
annotations.push(widgetDictRef);
page.node.set(PDFName.of('Annots'), annotations);
let acroForm = doc.catalog.lookupMaybe(PDFName.of('AcroForm'), PDFDict);
if (!acroForm) {
acroForm = doc.context.obj({ Fields: [] });
const acroFormRef = doc.context.register(acroForm);
doc.catalog.set(PDFName.of('AcroForm'), acroFormRef);
}
let sigFlags = acroForm.has(PDFName.of('SigFlags'))
? acroForm.get(PDFName.of('SigFlags'))
: PDFNumber.of(0);
const updatedFlags = PDFNumber.of(
sigFlags.asNumber() | SIG_FLAGS.SIGNATURES_EXIST | SIG_FLAGS.APPEND_ONLY
);
acroForm.set(PDFName.of('SigFlags'), updatedFlags);
let fields = acroForm.get(PDFName.of('Fields'));
if (fields instanceof PDFArray) {
fields.push(widgetDictRef);
}
// else if (fields) {
// const newFields = PDFArray.withContext(doc.context);
// fields.asArray().forEach(field => newFields.push(field));
// newFields.push(widgetDictRef);
// acroForm.set(PDFName.of('Fields'), newFields);
// }
else {
const newFields = PDFArray.withContext(doc.context);
newFields.push(widgetDictRef);
acroForm.set(PDFName.of('Fields'), newFields);
}
};
@@ -12,11 +12,7 @@ export default function reportJson(id, userId) {
IsDeclined: { $ne: true },
IsArchive: { $ne: true },
SignedUrl: { $exists: false },
CreatedBy: {
__type: 'Pointer',
className: '_User',
objectId: currentUserId,
},
CreatedBy: { __type: 'Pointer', className: '_User', objectId: currentUserId },
},
keys: [
'Name',
@@ -29,7 +25,6 @@ export default function reportJson(id, userId) {
'Signers.Phone',
'Placeholders',
'IsSignyourself',
'FileAdapterId',
],
};
@@ -43,19 +38,11 @@ export default function reportJson(id, userId) {
IsDeclined: { $ne: true },
IsArchive: { $ne: true },
SignedUrl: { $ne: null },
ExpiryDate: {
$gt: { __type: 'Date', iso: new Date().toISOString() },
},
ExpiryDate: { $gt: { __type: 'Date', iso: new Date().toISOString() } },
Placeholders: { $ne: null },
Signers: {
$inQuery: {
where: {
UserId: {
__type: 'Pointer',
className: '_User',
objectId: currentUserId,
},
},
where: { UserId: { __type: 'Pointer', className: '_User', objectId: currentUserId } },
className: 'contracts_Contactbook',
},
},
@@ -67,7 +54,6 @@ export default function reportJson(id, userId) {
'URL',
'ExtUserPtr.Name',
'ExtUserPtr.Email',
'ExtUserPtr.active_mail_adapter',
'Signers.Name',
'Signers.Email',
'Signers.Phone',
@@ -75,7 +61,6 @@ export default function reportJson(id, userId) {
'AuditTrail',
'Placeholders',
'SignedUrl',
'FileAdapterId',
'ExpiryDate',
],
};
@@ -90,14 +75,8 @@ export default function reportJson(id, userId) {
IsCompleted: { $ne: true },
IsDeclined: { $ne: true },
IsArchive: { $ne: true },
CreatedBy: {
__type: 'Pointer',
className: '_User',
objectId: currentUserId,
},
ExpiryDate: {
$gt: { __type: 'Date', iso: new Date().toISOString() },
},
CreatedBy: { __type: 'Pointer', className: '_User', objectId: currentUserId },
ExpiryDate: { $gt: { __type: 'Date', iso: new Date().toISOString() } },
},
keys: [
'Name',
@@ -106,7 +85,6 @@ export default function reportJson(id, userId) {
'URL',
'ExtUserPtr.Name',
'ExtUserPtr.Email',
'ExtUserPtr.active_mail_adapter',
'Signers.Name',
'Signers.Email',
'Signers.Phone',
@@ -116,7 +94,6 @@ export default function reportJson(id, userId) {
'SendMail',
'Placeholders',
'SignedUrl',
'FileAdapterId',
],
};
// completed documents report
@@ -126,13 +103,28 @@ export default function reportJson(id, userId) {
params: {
Type: { $ne: 'Folder' },
IsCompleted: true,
CreatedBy: {
__type: 'Pointer',
className: '_User',
objectId: currentUserId,
},
IsDeclined: { $ne: true },
IsArchive: { $ne: true },
// CreatedBy: {
// __type: 'Pointer',
// className: '_User',
// objectId: currentUserId,
// },
$or: [
// Condition 1: If `CreatedBy` exists, no need for `Signers` filter
{ CreatedBy: { __type: 'Pointer', className: '_User', objectId: currentUserId } },
// Condition 2: If `CreatedBy` does not exist, apply the `Signers` filter
{
Signers: {
$inQuery: {
where: {
UserId: { __type: 'Pointer', className: '_User', objectId: currentUserId },
},
className: 'contracts_Contactbook',
},
},
},
],
},
keys: [
'Name',
@@ -148,7 +140,6 @@ export default function reportJson(id, userId) {
'Placeholders',
'IsSignyourself',
'IsCompleted',
'FileAdapterId',
],
};
// declined documents report
@@ -159,13 +150,8 @@ export default function reportJson(id, userId) {
Type: null,
IsArchive: { $ne: true },
IsDeclined: true,
CreatedBy: {
__type: 'Pointer',
className: '_User',
objectId: currentUserId,
},
CreatedBy: { __type: 'Pointer', className: '_User', objectId: currentUserId },
},
keys: [
'Name',
'Note',
@@ -178,7 +164,6 @@ export default function reportJson(id, userId) {
'Placeholders',
'DeclineReason',
'SignedUrl',
'FileAdapterId',
],
};
// Expired Documents report
@@ -191,14 +176,8 @@ export default function reportJson(id, userId) {
IsArchive: { $ne: true },
Type: { $ne: 'Folder' },
SignedUrl: { $ne: null },
ExpiryDate: {
$lt: { __type: 'Date', iso: new Date().toISOString() },
},
CreatedBy: {
__type: 'Pointer',
className: '_User',
objectId: currentUserId,
},
ExpiryDate: { $lt: { __type: 'Date', iso: new Date().toISOString() } },
CreatedBy: { __type: 'Pointer', className: '_User', objectId: currentUserId },
},
keys: [
'Name',
@@ -211,7 +190,6 @@ export default function reportJson(id, userId) {
'Signers.Phone',
'Placeholders',
'SignedUrl',
'FileAdapterId',
'ExpiryDate',
],
};
@@ -226,14 +204,8 @@ export default function reportJson(id, userId) {
IsCompleted: { $ne: true },
IsDeclined: { $ne: true },
IsArchive: { $ne: true },
CreatedBy: {
__type: 'Pointer',
className: '_User',
objectId: currentUserId,
},
ExpiryDate: {
$gt: { __type: 'Date', iso: new Date().toISOString() },
},
CreatedBy: { __type: 'Pointer', className: '_User', objectId: currentUserId },
ExpiryDate: { $gt: { __type: 'Date', iso: new Date().toISOString() } },
},
keys: [
'Name',
@@ -241,7 +213,6 @@ export default function reportJson(id, userId) {
'URL',
'ExtUserPtr.Name',
'ExtUserPtr.Email',
'ExtUserPtr.active_mail_adapter',
'Signers.Name',
'Signers.Email',
'Signers.Phone',
@@ -250,7 +221,6 @@ export default function reportJson(id, userId) {
'ExpiryDate',
'Placeholders',
'SignedUrl',
'FileAdapterId',
],
};
// Recent signature requests report show on dashboard
@@ -263,19 +233,11 @@ export default function reportJson(id, userId) {
IsCompleted: { $ne: true },
IsDeclined: { $ne: true },
IsArchive: { $ne: true },
ExpiryDate: {
$gt: { __type: 'Date', iso: new Date().toISOString() },
},
ExpiryDate: { $gt: { __type: 'Date', iso: new Date().toISOString() } },
Placeholders: { $ne: null },
Signers: {
$inQuery: {
where: {
UserId: {
__type: 'Pointer',
className: '_User',
objectId: currentUserId,
},
},
where: { UserId: { __type: 'Pointer', className: '_User', objectId: currentUserId } },
className: 'contracts_Contactbook',
},
},
@@ -285,7 +247,6 @@ export default function reportJson(id, userId) {
'URL',
'ExtUserPtr.Name',
'ExtUserPtr.Email',
'ExtUserPtr.active_mail_adapter',
'Signers.Name',
'Signers.UserId',
'AuditTrail',
@@ -293,7 +254,6 @@ export default function reportJson(id, userId) {
'Signers.Phone',
'Placeholders',
'SignedUrl',
'FileAdapterId',
'ExpiryDate',
],
};
@@ -307,11 +267,7 @@ export default function reportJson(id, userId) {
IsDeclined: { $ne: true },
IsArchive: { $ne: true },
SignedUrl: { $exists: false },
CreatedBy: {
__type: 'Pointer',
className: '_User',
objectId: currentUserId,
},
CreatedBy: { __type: 'Pointer', className: '_User', objectId: currentUserId },
},
keys: [
'Name',
@@ -323,20 +279,15 @@ export default function reportJson(id, userId) {
'Signers.Email',
'Signers.Phone',
'Placeholders',
'FileAdapterId',
],
};
// contact book report
case '5KhaPr482K':
case 'contacts':
return {
reportName: 'Contactbook',
reportClass: 'contracts_Contactbook',
params: {
CreatedBy: {
__type: 'Pointer',
className: '_User',
objectId: currentUserId,
},
CreatedBy: { __type: 'Pointer', className: '_User', objectId: currentUserId },
IsDeleted: { $ne: true },
},
keys: ['Name', 'Email', 'Phone'],
@@ -346,17 +297,13 @@ export default function reportJson(id, userId) {
return {
reportName: 'Templates',
reportClass: 'contracts_Template',
params: {
Type: { $ne: 'Folder' },
IsArchive: { $ne: true },
},
params: { Type: { $ne: 'Folder' }, IsArchive: { $ne: true } },
keys: [
'Name',
'Note',
'Folder.Name',
'URL',
'ExtUserPtr.Name',
'ExtUserPtr.active_mail_adapter',
'Signers.Name',
'Signers.Email',
'Signers.Phone',
@@ -364,7 +311,6 @@ export default function reportJson(id, userId) {
'IsPublic',
'SharedWith.Name',
'SendinOrder',
'FileAdapterId',
'SignatureType',
'NotifyOnSignatures',
],
@@ -1,9 +1,8 @@
import uploadFileToS3 from './uploadFiletoS3.js';
import { parseJwt } from '../../Utils.js';
import jwt from 'jsonwebtoken';
import {
flattenPdf,
getSecureUrl,
} from '../../Utils.js';
export default async function saveFile(request) {
const jwttoken = request.headers.jwttoken || '';
if (!request.params.fileBase64) {
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide file.');
}
@@ -17,111 +16,29 @@ export default async function saveFile(request) {
const resExt = await extCls.first({ useMasterKey: true });
if (resExt) {
const _resExt = JSON.parse(JSON.stringify(resExt));
const fileAdapters = _resExt?.TenantId.FileAdapters || [];
const fileAdapter = fileAdapters?.find(x => x.id === id) || {};
if (fileAdapter?.accessKeyId) {
const adapterConfig = {
id: id,
fileAdapter: fileAdapter?.fileAdapter,
bucketName: fileAdapter?.bucketName,
region: fileAdapter?.region,
endpoint: fileAdapter?.endpoint,
accessKeyId: fileAdapter?.accessKeyId,
secretAccessKey: fileAdapter?.secretAccessKey,
baseUrl: fileAdapter?.baseUrl,
};
const buffer = Buffer.from(fileBase64, 'base64');
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 };
}
try {
const presignedUrl = await uploadFileToS3(buffer, fileName, mimeType, adapterConfig);
return { url: presignedUrl };
} catch (err) {
console.error('Error generate presigned url:', err);
const msg = 'Fileadapter credentials are invalid.';
throw new Parse.Error(400, msg);
}
} else {
const fileName = request.params.fileName;
const pdfFile = new Parse.File(fileName, { 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();
return { url: presignedUrl };
}
const fileRes = getSecureUrl(presignedUrl);
return { url: fileRes.url };
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
}
} else if (jwttoken) {
const jwtDecode = parseJwt(jwttoken);
const userCls = new Parse.Query(Parse.User);
userCls.equalTo('email', jwtDecode?.user_email);
const userRes = await userCls.first({ useMasterKey: true });
const userId = userRes?.id;
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('userId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
const appRes = await tokenQuery.first({ useMasterKey: true });
const decoded = jwt.verify(jwttoken, appRes?.get('token'));
if (decoded?.user_email) {
const extCls = new Parse.Query('contracts_Users');
extCls.equalTo('Email', decoded?.user_email);
extCls.include('TenantId');
const resExt = await extCls.first({ useMasterKey: true });
if (resExt) {
const _resExt = JSON.parse(JSON.stringify(resExt));
const fileAdapters = _resExt?.TenantId.FileAdapters || [];
const fileAdapter = fileAdapters?.find(x => x.id === id) || {};
if (fileAdapter?.accessKeyId) {
const adapterConfig = {
id: id,
fileAdapter: fileAdapter?.fileAdapter,
bucketName: fileAdapter?.bucketName,
region: fileAdapter?.region,
endpoint: fileAdapter?.endpoint,
accessKeyId: fileAdapter?.accessKeyId,
secretAccessKey: fileAdapter?.secretAccessKey,
baseUrl: fileAdapter?.baseUrl,
};
const buffer = Buffer.from(fileBase64, 'base64');
const fileName = request.params.fileName;
const ext = request.params.fileName?.split('.')?.pop();
let mimeType;
if (ext === 'pdf') {
mimeType = 'application/pdf';
} else if (ext === 'png' || ext === 'jpeg' || ext === 'jpg') {
mimeType = `image/${ext}`;
}
try {
const presignedUrl = await uploadFileToS3(buffer, fileName, mimeType, adapterConfig);
return { url: presignedUrl };
} catch (err) {
console.error('Error generate presigned url:', err);
const msg = 'Fileadapter credentials are invalid.';
throw new Parse.Error(400, msg);
}
} else {
const fileName = request.params.fileName;
const pdfFile = new Parse.File(fileName, { base64: fileBase64 });
// Save the Parse File if needed
const pdfData = await pdfFile.save({ useMasterKey: true });
const presignedUrl = pdfData.url();
return { url: presignedUrl };
}
} 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) {
@@ -1,193 +0,0 @@
import axios from 'axios';
import { cloudServerUrl, planCredits } from '../../Utils.js';
export default async function saveSubscription(request) {
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
const appId = process.env.APP_ID;
const subscription = request.params.subscription;
const SubscriptionId = subscription.data.subscription.subscription_id;
const body = subscription;
const Next_billing_date = subscription.data.subscription.next_billing_at;
const planCode = subscription.data.subscription.plan.plan_code;
const event = subscription?.data?.event_type || '';
const credits = planCredits?.[planCode] || 0;
const isTeamPlan = planCode?.includes('team');
let newAddons = 0;
try {
const userRes = await axios.get(serverUrl + '/users/me', {
headers: {
'X-Parse-Application-Id': appId,
'X-Parse-Session-Token': request.headers['sessiontoken'],
},
});
if (userRes.data && userRes.data.objectId) {
const extUserCls = new Parse.Query('contracts_Users');
extUserCls.equalTo('UserId', {
__type: 'Pointer',
className: '_User',
objectId: userRes.data.objectId,
});
const extUser = await extUserCls.first({ useMasterKey: true });
if (extUser) {
const subcriptionCls = new Parse.Query('contracts_Subscriptions');
subcriptionCls.equalTo('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: extUser.get('TenantId').id,
});
const resSubscription = await subcriptionCls.first({ useMasterKey: true });
const addons = subscription?.data?.subscription?.addons || [];
if (addons?.length > 0) {
let allowedUsersMonthly = 0;
let allowedUsersYearly = 0;
addons?.forEach(item => {
if (item.addon_code === 'extra-teams-users-monthly') {
allowedUsersMonthly += item.quantity;
} else if (item.addon_code === 'extra-teams-users-yearly') {
allowedUsersYearly += item.quantity;
} else if (item.addon_code === 'extra-users') {
allowedUsersMonthly += item.quantity;
}
});
if (allowedUsersMonthly > 0 || allowedUsersYearly > 0) {
newAddons = allowedUsersMonthly + allowedUsersYearly + 1; // + 1 is Admin user
}
} else {
if (
planCode === 'teams-yearly' ||
planCode === 'teams-monthly' ||
planCode === 'team-weekly'
) {
newAddons = 1; // 1 is Admin user
}
}
if (resSubscription) {
const _resSub = JSON.parse(JSON.stringify(resSubscription));
const updateSubscription = new Parse.Object('contracts_Subscriptions');
updateSubscription.id = resSubscription.id;
updateSubscription.set('SubscriptionId', SubscriptionId);
updateSubscription.set('SubscriptionDetails', body);
updateSubscription.set('Next_billing_date', new Date(Next_billing_date));
updateSubscription.set('PlanCode', planCode);
if (newAddons > 0) {
updateSubscription.set('AllowedUsers', parseInt(newAddons));
}
let existAddon = 0;
let allowedUsersMonthly = 0;
let allowedUsersYearly = 0;
_resSub.SubscriptionDetails?.data?.subscription?.addons?.forEach(item => {
if (item.addon_code === 'extra-teams-users-monthly') {
allowedUsersMonthly += item.quantity;
} else if (item.addon_code === 'extra-teams-users-yearly') {
allowedUsersYearly += item.quantity;
} else if (item.addon_code === 'extra-users') {
allowedUsersMonthly += item.quantity;
}
});
if (allowedUsersMonthly > 0 || allowedUsersYearly > 0) {
existAddon = allowedUsersMonthly + allowedUsersYearly + 1; // + 1 is Admin user
} else {
if (
planCode === 'teams-yearly' ||
planCode === 'teams-monthly' ||
planCode === 'team-weekly'
) {
existAddon = 1; // 1 is Admin user
}
}
const isSameAsPrevPlan = resSubscription?.get('PlanCode') === planCode;
if (isSameAsPrevPlan) {
const planCredits = resSubscription?.get('PlanCredits');
const existAllowedCredits = resSubscription?.get('AllowedCredits') || 0;
if (planCredits) {
const oldAddons = existAddon;
const substractedAddon = newAddons - oldAddons;
if (isTeamPlan && substractedAddon > 0) {
const newCredits = existAllowedCredits + substractedAddon * planCredits;
updateSubscription.set('AllowedCredits', newCredits);
if (event === 'subscription_created') {
updateSubscription.set('PlanCredits', credits);
}
} else if (isTeamPlan) {
const existCredits = existAddon * planCredits;
updateSubscription.set('AllowedCredits', existCredits);
if (event === 'subscription_created') {
updateSubscription.set('PlanCredits', credits);
}
} else {
updateSubscription.set('AllowedCredits', planCredits);
if (event === 'subscription_created') {
updateSubscription.set('PlanCredits', credits);
}
}
} else {
if (isTeamPlan) {
const newCredits = newAddons * credits;
updateSubscription.set('AllowedCredits', newCredits);
updateSubscription.set('PlanCredits', credits);
} else {
updateSubscription.set('AllowedCredits', credits);
updateSubscription.set('PlanCredits', credits);
}
}
} else {
if (credits > 0) {
if (isTeamPlan) {
const newCredits = newAddons * credits;
updateSubscription.set('AllowedCredits', newCredits);
updateSubscription.set('PlanCredits', credits);
} else {
updateSubscription.set('AllowedCredits', credits);
updateSubscription.set('PlanCredits', credits);
}
}
}
await updateSubscription.save(null, { useMasterKey: true });
return { status: 'update subscription!' };
} else {
const createSubscription = new Parse.Object('contracts_Subscriptions');
createSubscription.set('SubscriptionId', SubscriptionId);
createSubscription.set('SubscriptionDetails', body);
createSubscription.set('ExtUserPtr', {
__type: 'Pointer',
className: 'contracts_Users',
objectId: extUser.id,
});
createSubscription.set('CreatedBy', {
__type: 'Pointer',
className: '_User',
objectId: extUser.get('UserId').id,
});
createSubscription.set('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
objectId: extUser.get('TenantId').id,
});
createSubscription.set('Next_billing_date', new Date(Next_billing_date));
createSubscription.set('PlanCode', planCode);
if (newAddons > 0) {
createSubscription.set('AllowedUsers', parseInt(newAddons));
}
if (credits > 0) {
if (isTeamPlan) {
const totalCredits = parseInt(newAddons) * credits;
createSubscription.set('AllowedCredits', totalCredits);
createSubscription.set('PlanCredits', credits);
} else {
createSubscription.set('AllowedCredits', credits);
createSubscription.set('PlanCredits', credits);
}
}
await createSubscription.save(null, { useMasterKey: true });
return { status: 'create subscription!' };
}
} else {
return { status: 'user not found!' };
}
} else {
return { status: 'user not found!' };
}
} catch (err) {
console.log('Err in save subscription', err);
throw new Error(err.message);
}
}
@@ -1,114 +0,0 @@
import { parseJwt } from '../../Utils.js';
import jwt from 'jsonwebtoken';
async function updateTemplate(template, isJwt = false) {
try {
if (template?.Id) {
const updateTemplate = new Parse.Object('contracts_Template');
updateTemplate.id = template.Id;
if (template?.URL) {
updateTemplate.set('URL', template.URL);
}
if (template?.Name) {
updateTemplate.set('Name', template.Name);
}
if (template?.Note) {
updateTemplate.set('Note', template.Note);
}
if (template?.Description) {
updateTemplate.set('Description', template.Description);
}
if (template?.SendinOrder) {
updateTemplate.set('SendinOrder', template.SendinOrder);
}
if (template?.AutomaticReminders) {
updateTemplate.set('AutomaticReminders', template.AutomaticReminders);
}
if (template?.RemindOnceInEvery) {
updateTemplate.set('RemindOnceInEvery', template.RemindOnceInEvery);
}
if (template?.NextReminderDate) {
updateTemplate.set('NextReminderDate', new Date(template.NextReminderDate));
}
if (template?.IsEnableOTP) {
updateTemplate.set('IsEnableOTP', template.IsEnableOTP);
}
if (template?.IsTourEnabled) {
updateTemplate.set('IsTourEnabled', template.IsTourEnabled);
}
const isPublic = template?.IsPublic !== undefined ? template?.IsPublic : false;
if (template?.IsPublic !== undefined) {
updateTemplate.set('IsPublic', isPublic);
}
updateTemplate.set('Placeholders', template.Placeholders);
updateTemplate.set('Signers', template.Signers);
if (template?.SignatureType?.length > 0) {
updateTemplate.set('SignatureType', template.SignatureType);
}
let updateTemplateRes;
if (isJwt) {
updateTemplateRes = await updateTemplate.save(null, { useMasterKey: true });
} else {
updateTemplateRes = await updateTemplate.save();
}
return updateTemplateRes;
} else {
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide Id.');
}
} catch (err) {
console.log('err in update template', err);
throw err;
}
}
export default async function saveTemplate(request) {
const jwttoken = request.headers.jwttoken || '';
const template = {
Id: request.params?.templateId,
URL: request.params?.URL || '',
Name: request.params?.Name,
Note: request.params?.Note,
Description: request.params?.Description,
Placeholders: request.params?.Placeholders,
Signers: request.params?.Signers,
SendMail: request.params?.SendMail || false,
SendinOrder: request.params?.SendinOrder || true,
AutomaticReminders: request.params?.AutomaticReminders,
RemindOnceInEvery: parseInt(request.params.RemindOnceInEvery) || 15,
NextReminderDate: request.params?.NextReminderDate,
IsEnableOTP: request.params?.IsEnableOTP === true ? true : false,
IsTourEnabled: request.params?.IsTourEnabled === true ? true : false,
IsPublic: request.params?.IsPublic,
SignatureType: request.params?.SignatureType || [],
};
try {
if (request.user) {
return await updateTemplate(template);
} else if (jwttoken) {
const jwtDecode = parseJwt(jwttoken);
const userCls = new Parse.Query(Parse.User);
userCls.equalTo('email', jwtDecode?.user_email);
const userRes = await userCls.first({ useMasterKey: true });
const userId = userRes?.id;
const tokenQuery = new Parse.Query('appToken');
tokenQuery.equalTo('userId', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
const appRes = await tokenQuery.first({ useMasterKey: true });
const decoded = jwt.verify(jwttoken, appRes?.get('token'));
if (decoded?.user_email) {
return await updateTemplate(template, true);
} else {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid token');
}
} else {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid session token');
}
} catch (err) {
console.log('err in get signers', err);
throw err;
}
}

Some files were not shown because too many files have changed in this diff Show More