Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into fix_smtp

This commit is contained in:
prafull-opensignlabs
2024-06-12 16:58:21 +05:30
100 changed files with 10428 additions and 6836 deletions
@@ -8,7 +8,9 @@ async function ContractUsersAftersave(request) {
contactbook.set('Name', request.object.get('Name'));
contactbook.set('Email', request.object.get('Email'));
contactbook.set('ExtUserPtr', request.object.get('objectId'));
contactbook.set('Phone', request.object.get('Phone'));
if (request.object?.get('Phone')) {
contactbook.set('Phone', request.object.get('Phone'));
}
contactbook.set('ExtUserPtr', {
__type: 'Pointer',
className: 'contracts_Users',
@@ -0,0 +1,41 @@
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;
}
}
@@ -14,19 +14,29 @@ async function DocumentAftersave(request) {
const updateQuery = await documentQuery.get(request.object.id, { useMasterKey: true });
updateQuery.set('ExpiryDate', ExpiryDate);
updateQuery.set('OriginIp', ip);
const AutoReminder = request?.object?.get('AutomaticReminders') || false;
if (AutoReminder) {
const RemindOnceInEvery = request?.object?.get('RemindOnceInEvery') || 5;
const ReminderDate = new Date(createdAt);
ReminderDate.setDate(ReminderDate.getDate() + RemindOnceInEvery);
updateQuery.set('NextReminderDate', ReminderDate);
}
await updateQuery.save(null, { useMasterKey: true });
} else if (createdAt && Folder === 'AIDoc') {
const TimeToCompleteDays = request.object.get('TimeToCompleteDays');
const ExpiryDate = new Date(createdAt);
// console.log("ExpiryDate")
// console.log(ExpiryDate)
ExpiryDate.setDate(ExpiryDate.getDate() + TimeToCompleteDays);
// console.log("ExpiryDate date after update")
// console.log(ExpiryDate)
const documentQuery = new Parse.Query('contracts_Document');
const updateQuery = await documentQuery.get(request.object.id, { useMasterKey: true });
updateQuery.set('ExpiryDate', ExpiryDate);
updateQuery.set('OriginIp', ip);
const AutoReminder = request?.object?.get('AutomaticReminders') || false;
if (AutoReminder) {
const RemindOnceInEvery = request?.object?.get('RemindOnceInEvery') || 5;
const ReminderDate = new Date(createdAt);
ReminderDate.setDate(ReminderDate.getDate() + RemindOnceInEvery);
updateQuery.set('NextReminderDate', ReminderDate);
}
await updateQuery.save(null, { useMasterKey: true });
}
@@ -1,4 +1,4 @@
import axios from "axios";
import axios from 'axios';
const serverUrl = process.env.SERVER_URL;
const APPID = process.env.APP_ID;
@@ -18,23 +18,23 @@ 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 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);
userQuery.equalTo('email', userEmail);
const res = await userQuery.first({ useMasterKey: true });
if (res) {
try {
const SignIn = await axios.put(
serverUrl + "/users/" + res.id,
serverUrl + '/users/' + res.id,
{ authData: authData },
{
headers: {
"X-Parse-Application-Id": APPID,
"X-Parse-Master-key": masterKEY,
'X-Parse-Application-Id': APPID,
'X-Parse-Master-key': masterKEY,
},
}
);
@@ -42,22 +42,22 @@ export default async function FacebookSign(request) {
if (SignIn.data) {
// console.log("google Sign in", SignIn);
const sessiontoken = SignIn.data.sessionToken;
console.log("Google sessiontoken", sessiontoken);
console.log('Google sessiontoken', sessiontoken);
return {
email: userEmail,
message: "User Sign In",
message: 'User Sign In',
sessiontoken: sessiontoken,
};
}
} catch (err) {
console.log("err in user google sign in", err);
return { message: "Internal server error" };
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",
serverUrl + '/users',
{
authData: authData,
username: userEmail,
@@ -67,8 +67,8 @@ export default async function FacebookSign(request) {
},
{
headers: {
"X-Parse-Application-Id": APPID,
"X-Parse-Revocable-Session": "1",
'X-Parse-Application-Id': APPID,
'X-Parse-Revocable-Session': '1',
},
}
);
@@ -79,14 +79,14 @@ export default async function FacebookSign(request) {
const sessiontoken = SignUp.data.sessionToken;
const payload = {
email: userEmail,
message: "User Sign Up",
message: 'User Sign Up',
sessiontoken: sessiontoken,
};
return payload;
}
} catch (err) {
console.log("err in user google sign up", err);
return { message: "Internal server err" };
console.log('err in user google sign up', err);
return { message: 'Internal server err' };
}
}
}
@@ -0,0 +1,32 @@
async function GetPublicTemplate(request) {
try {
const username = request.params.username;
if (username) {
const extUserQuery = new Parse.Query('contracts_Users');
extUserQuery.equalTo('UserName', username);
const res = await extUserQuery.first({ useMasterKey: true });
const userId = res.get('UserId').id;
if (userId) {
const templatQuery = new Parse.Query('contracts_Template');
templatQuery.equalTo('CreatedBy', {
__type: 'Pointer',
className: '_User',
objectId: userId,
});
templatQuery.equalTo('IsPublic', true);
const getTemplate = await templatQuery.find({ useMasterKey: true });
return getTemplate;
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Template not found');
}
} 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;
@@ -0,0 +1,23 @@
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;
@@ -26,7 +26,7 @@ export default async function GetTemplate(request) {
if (res) {
// console.log("res ",res)
const acl = res.getACL();
console.log("acl", acl.getReadAccess(userId))
// console.log("acl", acl.getReadAccess(userId))
if (acl && acl.getReadAccess(userId)) {
return res;
} else {
@@ -1,4 +1,4 @@
import axios from "axios";
import axios from 'axios';
const serverUrl = process.env.SERVER_URL;
const APPID = process.env.APP_ID;
@@ -9,8 +9,8 @@ const masterKEY = process.env.MASTER_KEY;
* @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
* @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}
*/
@@ -18,21 +18,21 @@ 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 phone = request.params?.Phone || '';
const name = request.params.Name;
const authData = { google: { id: userGoogleId, id_token: userTokenId } };
const userQuery = new Parse.Query(Parse.User);
userQuery.equalTo("email", userEmail);
userQuery.equalTo('email', userEmail);
const res = await userQuery.first({ useMasterKey: true });
if (res) {
try {
const SignIn = await axios.put(
serverUrl + "/users/" + res.id,
serverUrl + '/users/' + res.id,
{ authData: authData },
{
headers: {
"X-Parse-Application-Id": APPID,
"X-Parse-Master-key": masterKEY,
'X-Parse-Application-Id': APPID,
'X-Parse-Master-key': masterKEY,
},
}
);
@@ -40,22 +40,22 @@ export default async function GoogleSign(request) {
if (SignIn.data) {
// console.log("google Sign in", SignIn);
const sessiontoken = SignIn.data.sessionToken;
console.log("Google sessiontoken", sessiontoken);
console.log('Google sessiontoken', sessiontoken);
return {
email: userEmail,
message: "User Sign In",
message: 'User Sign In',
sessiontoken: sessiontoken,
};
}
} catch (err) {
console.log("err in user google sign in", err);
return { message: "Internal server error" };
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",
serverUrl + '/users',
{
authData: authData,
username: userEmail,
@@ -65,8 +65,8 @@ export default async function GoogleSign(request) {
},
{
headers: {
"X-Parse-Application-Id": APPID,
"X-Parse-Revocable-Session": "1",
'X-Parse-Application-Id': APPID,
'X-Parse-Revocable-Session': '1',
},
}
);
@@ -77,14 +77,14 @@ export default async function GoogleSign(request) {
const sessiontoken = SignUp.data.sessionToken;
const payload = {
email: userEmail,
message: "User Sign Up",
message: 'User Sign Up',
sessiontoken: sessiontoken,
};
return payload;
}
} catch (err) {
console.log("err in user google sign up", err);
return { message: "Internal server err" };
console.log('err in user google sign up', err);
return { message: 'Internal server err' };
}
}
}
@@ -4,7 +4,14 @@ export default async function TemplateAfterSave(request) {
console.log('new entry is insert in contracts_Template');
// update acl of New Document If There are signers present in array
const signers = request.object.get('Signers');
const AutoReminder = request?.object?.get('AutomaticReminders') || false;
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);
await request.object.save(null, { useMasterKey: true });
}
if (signers && signers.length > 0) {
await updateAclDoc(request.object.id);
} else {
@@ -2,41 +2,41 @@
//-- Export Modules
import dotenv from 'dotenv';
dotenv.config()
import axios from "axios";
dotenv.config();
import axios from 'axios';
export async function getUserGroups(request) {
try {
var appname = request.params.appname;
if (appname == "") {
return Promise.reject("Error:please provide appname");
if (appname == '') {
return Promise.reject('Error:please provide appname');
}
var response = {};
var rolelist = {};
appname = appname + "_";
appname = appname + '_';
//--function to get the userid from session token
function getuserid(request) {
return new Promise(function (resolve, reject) {
const options = {
url: process.env.SERVER_URL + "/users/me",
method: "get",
url: process.env.SERVER_URL + '/users/me',
method: 'get',
headers: {
"X-Parse-Application-Id": process.env.APP_ID,
"X-Parse-Session-Token": request.headers["sessiontoken"],
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Session-Token': request.headers['sessiontoken'],
},
};
axios(options)
.then((x) => {
.then(x => {
const body = x.data;
var error = body == "" ? true : false;
var error = body == '' ? true : false;
if (error) {
reject("result not found!");
reject('result not found!');
} else {
resolve(body);
}
})
.catch((err) => {
.catch(err => {
if (err) {
console.error(err);
return;
@@ -46,7 +46,7 @@ export async function getUserGroups(request) {
}
var userData = await getuserid(request);
var userid = userData.objectId;
console.log("userid " + userid);
// console.log("userid " + userid);
var url =
process.env.SERVER_URL +
'/roles?where={"users":{"__type":"Pointer","className":"_User","objectId":"' +
@@ -60,29 +60,29 @@ export async function getUserGroups(request) {
return new Promise(function (resolve, reject) {
const options = {
url: url,
method: "get",
method: 'get',
headers: {
"X-Parse-Application-Id": process.env.APP_ID,
'X-Parse-Application-Id': process.env.APP_ID,
},
};
axios(options)
.then((x) => {
.then(x => {
const body = x.data;
var roleres = [];
for (var i = 0; i < body["results"].length; i++) {
var rolename = body["results"][i]["name"];
for (var i = 0; i < body['results'].length; i++) {
var rolename = body['results'][i]['name'];
//var roleprefix = rolename.split("_")[0];
roleres.push(rolename);
}
var error = roleres == "" ? true : false;
var error = roleres == '' ? true : false;
if (error) {
reject("result not found!");
reject('result not found!');
} else {
resolve(roleres);
}
})
.catch((err) => {
.catch(err => {
if (err) {
console.error(err);
return;
@@ -92,7 +92,7 @@ export async function getUserGroups(request) {
}
rolelist = await getRoleList(request);
console.log(rolelist);
// console.log(rolelist);
//--check user roles according to appId
var rolesInapp = [];
for (let i = 0; i < rolelist.length; i++) {
@@ -102,11 +102,11 @@ export async function getUserGroups(request) {
rolesInapp.push(rolelist[i]);
}
}
console.log(rolesInapp);
// console.log(rolesInapp);
return rolesInapp;
} catch (err) {
console.log("err in usergroup");
console.log('err in usergroup');
console.log(err);
return Promise.reject("Error:Result not found");
return Promise.reject('Error:Result not found');
}
}
@@ -26,12 +26,15 @@ export default async function ZohoDetails(request) {
// console.log("Access Token:", res.data);
if (res.data.access_token) {
const hostedpages = request.params.hostedpagesId;
const userData = await axios.get('https://billing.zoho.in/api/v1/hostedpages/' + hostedpages, {
headers: {
Authorization: 'Zoho-oauthtoken ' + res.data.access_token,
'X-com-zoho-subscriptions-organizationid': process.env.ZOHO_BILLING_ORG_ID,
},
});
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 || '';
@@ -44,7 +47,7 @@ export default async function ZohoDetails(request) {
userData.data.data.subscription.customer.cd_job_title) ||
'';
const resData = {
phone: userData.data.data.subscription.contactpersons[0].mobile,
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,
@@ -0,0 +1,182 @@
import axios from 'axios';
const serverUrl = process.env.SERVER_URL;
const appId = process.env.APP_ID;
async function sendMail(document, sessionToken) {
const baseUrl = new URL(process.env.PUBLIC_URL);
// console.log("pdfDetails", pdfDetails);
const timeToCompleteDays = document?.TimeToCompleteDays || 15;
const ExpireDate = new Date(document.createdAt);
ExpireDate.setDate(ExpireDate.getDate() + timeToCompleteDays);
const newDate = ExpireDate;
const localExpireDate = newDate.toLocaleDateString('en-US', {
day: 'numeric',
month: 'long',
year: 'numeric',
});
const sender = document.ExtUserPtr.Email;
let signerMail = document.Placeholders;
if (document.SendinOrder) {
signerMail = signerMail.slice();
signerMail.splice(1);
}
for (let i = 0; i < signerMail.length; i++) {
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 objectId = signerMail[i]?.signerObjId;
const hostUrl = baseUrl.origin;
let encodeBase64;
let existSigner = {};
if (objectId) {
existSigner = document?.Signers?.find(user => user.objectId === objectId);
encodeBase64 = btoa(`${document.objectId}/${existSigner?.Email}/${objectId}`);
} else {
encodeBase64 = btoa(`${document.objectId}/${signerMail[i].email}`);
}
let signPdf = `${hostUrl}/login/${encodeBase64}`;
const openSignUrl = 'https://www.opensignlabs.com/';
const orgName = document.ExtUserPtr.Company ? document.ExtUserPtr.Company : '';
const themeBGcolor = '#47a3ad';
let params = {
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 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') {
// console.log('batch login mail sent');
// }
} catch (error) {
console.log('error', error);
}
}
}
export default async function createBatchDocs(request) {
const strDocuments = request.params.Documents;
const sessionToken = request.headers['sessiontoken'];
const Documents = JSON.parse(strDocuments);
const Ip = request?.headers?.['x-real-ip'] || '';
// console.log('Documents ', Documents);
const parseConfig = {
baseURL: serverUrl, //localStorage.getItem('baseUrl'),
headers: {
'X-Parse-Application-Id': appId,
'X-Parse-Session-Token': sessionToken,
'Content-Type': 'application/json',
},
};
try {
const requests = Documents.map(x => {
const Signers = x.Signers;
const allSigner = x?.Placeholders?.map(
item => Signers?.find(e => item?.signerPtr?.objectId === e?.objectId) || item?.signerPtr
).filter(signer => Object.keys(signer).length > 0);
const date = new Date();
const isoDate = date.toISOString();
let Acl = { [x.CreatedBy.objectId]: { read: true, write: true } };
if (allSigner && allSigner.length > 0) {
allSigner.forEach(x => {
const obj = { [x.CreatedBy.objectId]: { read: true, write: true } };
Acl = { ...Acl, ...obj };
});
}
return {
method: 'POST',
path: '/app/classes/contracts_Document',
body: {
Name: x.Name,
URL: x.URL,
Note: x.Note,
Description: x.Description,
CreatedBy: x.CreatedBy,
SendinOrder: x.SendinOrder || true,
ExtUserPtr: {
__type: 'Pointer',
className: x.ExtUserPtr.className,
objectId: x.ExtUserPtr.objectId,
},
Placeholders: x.Placeholders.map(y =>
y?.signerPtr?.objectId
? {
...y,
signerPtr: {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: y.signerPtr.objectId,
},
signerObjId: y.signerObjId,
}
: { ...y, signerPtr: {}, signerObjId: '' }
),
SignedUrl: x.URL || x.SignedUrl,
SentToOthers: true,
Signers: allSigner?.map(y => ({
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: y.objectId,
})),
ACL: Acl,
SentToOthers: true,
RemindOnceInEvery: x.RemindOnceInEvery || 5,
AutomaticReminders: x.AutomaticReminders || false,
TimeToCompleteDays: x.TimeToCompleteDays || 15,
OriginIp: Ip,
DocSentAt: { __type: 'Date', iso: isoDate },
},
};
});
// console.log('requests ', requests);
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,
}));
for (let i = 0; i < updateDocuments.length; i++) {
sendMail(updateDocuments[i], sessionToken);
}
return 'success';
}
// Handle individual responses within response.data.results
} catch (error) {
console.error('Error performing batch query:', error);
}
}
@@ -30,7 +30,7 @@ export default async function getReport(request) {
'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`;
const url = `${serverUrl}/classes/${clsName}?where=${strParams}&keys=${strKeys}&order=${orderBy}&skip=${skip}&limit=${limit}&include=AuditTrail.UserPtr,Placeholders.signerPtr`;
const res = await axios.get(url, { headers: headers });
if (res.data && res.data.results) {
return res.data.results;
@@ -0,0 +1,30 @@
/**
* Checks if a user exists in the extended class 'contracts_Users' based on the provided email.
* @param email - The request contains parameters, such as the user's email.
* @returns {Object} - Returns an object indicating whether the user exists.
*/
export default async function isextenduser(request) {
try {
// Query the 'contracts_Users' class in the database based on the provided email
const userQuery = new Parse.Query('contracts_Users');
userQuery.equalTo('Email', request.params.email);
// Execute the query
const res = await userQuery.first({ useMasterKey: true });
// Check if a user was found
if (res) {
// If user exists, return object with 'isUserExist' set to true
return { isUserExist: true };
} else {
// If user does not exist, return object with 'isUserExist' set to false
return { isUserExist: false };
}
} catch (err) {
// Handle errors
console.log('Error in userexist', err);
const code = err?.code || 400;
const message = err?.message || 'Something went wrong.';
throw new Parse.Error(code, message);
}
}
@@ -0,0 +1,293 @@
// `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');
contactQuery.set('TenantId', 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;
}
};
// `linkContactToDoc` cloud function is used to create contact, add this contact in contracts_Guest role and
// save contact pointer in placeholder, signers and ACL of Document
export default async function linkContactToDoc(req) {
const email = req.params.email;
const docId = req.params.docId;
const name = req.params.name;
const phone = req.params.phone;
try {
if (docId) {
// Execute the query to get the document with the specified 'docId'
const docQuery = new Parse.Query('contracts_Document');
docQuery.include('ExtUserPtr');
const docRes = await docQuery.get(docId, { useMasterKey: true });
// Check if the document was found; if not, throw an error indicating the document was not found
if (!docRes) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Document not found.');
}
const _docRes = JSON.parse(JSON.stringify(docRes));
const Placeholders = _docRes?.Placeholders || [];
const index = Placeholders?.findIndex(x => x.email && x.email === email);
if (index !== -1) {
// `signerObjectId` holds the value of `signerObjId` from the `Placeholders` array at the current index.
// This value is used to check if `signerObjId` is present or not.
const signerObjectId = Placeholders[index]?.signerObjId;
if (signerObjectId) {
return { contactId: signerObjectId };
}
// 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', _docRes.CreatedBy);
contactCls.notEqualTo('IsDeleted', true);
const existContact = await contactCls.first({ useMasterKey: true });
if (existContact) {
//update contact in placeholder, signers and update ACl in provide document
const updateDoc = new Parse.Object('contracts_Document');
updateDoc.id = docId;
const signers = _docRes?.Signers || [];
const signerobj = {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: existContact.id,
};
// The splice method is used to add a signer at the desired index
// index is the variable where the signer needs to be added
// 0 indicates that no elements should be deleted
// signerobj is the reference to the signer object
signers.splice(index, 0, signerobj);
updateDoc.set('Signers', signers);
Placeholders[index] = {
...Placeholders[index],
signerObjId: existContact.id,
signerPtr: {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: existContact.id,
},
};
updateDoc.set('Placeholders', Placeholders);
const Acl = docRes.getACL();
Acl.setReadAccess(existContact.get('UserId').id, true);
Acl.setWriteAccess(existContact.get('UserId').id, true);
updateDoc.setACL(Acl);
// const parseData = JSON.parse(JSON.stringify(res));
const resDoc = await updateDoc.save(null, { useMasterKey: true });
if (resDoc) {
return { contactId: existContact.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: _docRes.CreatedBy,
TenantId: _docRes.ExtUserPtr.TenantId,
};
// if user present on platform create contact on the basis of extended user details
const contactRes = await saveRoleContact(contact);
//update contact in placeholder, signers and update ACl in provide document
const updateDoc = new Parse.Object('contracts_Document');
updateDoc.id = docId;
const signers = _docRes?.Signers || [];
const signerobj = {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: contactRes.id,
};
// The splice method is used to add a signer at the desired index
// index is the variable where the signer needs to be added
// 0 indicates that no elements should be deleted
// signerobj is the reference to the signer object
signers.splice(index, 0, signerobj);
updateDoc.set('Signers', signers);
Placeholders[index] = {
...Placeholders[index],
signerObjId: contactRes.id,
signerPtr: {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: contactRes.id,
},
};
updateDoc.set('Placeholders', Placeholders);
const Acl = docRes.getACL();
Acl.setReadAccess(_extUser.UserId.objectId, true);
Acl.setWriteAccess(_extUser.UserId.objectId, true);
updateDoc.setACL(Acl);
// const parseData = JSON.parse(JSON.stringify(res));
const resDoc = await updateDoc.save(null, { useMasterKey: true });
if (resDoc) {
return { contactId: contactRes.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: _docRes.CreatedBy,
TenantId: _docRes.ExtUserPtr.TenantId,
};
// 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 updateDoc = new Parse.Object('contracts_Document');
updateDoc.id = docId;
const signers = _docRes?.Signers || [];
const signerobj = {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: contactRes.id,
};
// The splice method is used to add a signer at the desired index
// index is the variable where the signer needs to be added
// 0 indicates that no elements should be deleted
// signerobj is the reference to the signer object
signers.splice(index, 0, signerobj);
updateDoc.set('Signers', signers);
Placeholders[index] = {
...Placeholders[index],
signerObjId: contactRes.id,
signerPtr: {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: contactRes.id,
},
};
updateDoc.set('Placeholders', Placeholders);
const Acl = docRes.getACL();
Acl.setReadAccess(userRes.id, true);
Acl.setWriteAccess(userRes.id, true);
updateDoc.setACL(Acl);
// const parseData = JSON.parse(JSON.stringify(res));
const resDoc = await updateDoc.save(null, { useMasterKey: true });
if (resDoc) {
return { contactId: contactRes.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: _docRes.CreatedBy,
TenantId: _docRes.ExtUserPtr.TenantId,
};
// 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 updateDoc = new Parse.Object('contracts_Document');
updateDoc.id = docId;
const signers = _docRes?.Signers || [];
const signerobj = {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: contactRes.id,
};
// The splice method is used to add a signer at the desired index
// index is the variable where the signer needs to be added
// 0 indicates that no elements should be deleted
// signerobj is the reference to the signer object
signers.splice(index, 0, signerobj);
updateDoc.set('Signers', signers);
Placeholders[index] = {
...Placeholders[index],
signerObjId: contactRes.id,
signerPtr: {
__type: 'Pointer',
className: 'contracts_Contactbook',
objectId: contactRes.id,
},
};
updateDoc.set('Placeholders', Placeholders);
const Acl = docRes.getACL();
Acl.setReadAccess(newUserRes.id, true);
Acl.setWriteAccess(newUserRes.id, true);
updateDoc.setACL(Acl);
// const parseData = JSON.parse(JSON.stringify(res));
const resDoc = await updateDoc.save(null, { useMasterKey: true });
if (resDoc) {
return { contactId: contactRes.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.OPERATION_FORBIDDEN, 'unauthorized');
}
} else {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Document not found.');
}
} catch (err) {
console.log('err in linkcontacttodoc', err);
throw err;
}
}
@@ -0,0 +1,473 @@
import fs from 'node:fs';
import axios from 'axios';
import { SignPdf } from '@signpdf/signpdf';
import { P12Signer } from '@signpdf/signer-p12';
import { pdflibAddPlaceholder } from '@signpdf/placeholder-pdf-lib';
import { PDFDocument } from 'pdf-lib';
import { replaceMailVaribles, saveFileUsage } from '../../../Utils.js';
import GenerateCertificate from './GenerateCertificate.js';
const serverUrl = 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) {
try {
const filedata = fs.readFileSync(filepath);
const file = new Parse.File(pdfName, [...filedata], 'application/pdf');
await file.save({ useMasterKey: true });
const fileUrl = file.url();
return { imageUrl: fileUrl };
} catch (err) {
console.log('Err ', err);
// `fs.unlinkSync` is used to remove exported signed pdf file from exports folder
fs.unlinkSync(filepath);
}
}
// `updateDoc` is used to update signedUrl, AuditTrail, Iscompleted in document
async function updateDoc(docId, url, userId, ipAddress, data, className, sign) {
try {
const UserPtr = {
__type: 'Pointer',
className: className,
objectId: userId,
};
const obj = {
UserPtr: UserPtr,
SignedUrl: url,
Activity: 'Signed',
ipAddress: ipAddress,
SignedOn: new Date(),
Signature: sign,
};
let updateAuditTrail;
if (data.AuditTrail && data.AuditTrail.length > 0) {
const AuditTrail = JSON.parse(JSON.stringify(data.AuditTrail));
const existingIndex = AuditTrail.findIndex(
entry => entry.UserPtr.objectId === userId && entry.Activity !== 'Created'
);
existingIndex !== -1
? (AuditTrail[existingIndex] = { ...AuditTrail[existingIndex], ...obj })
: AuditTrail.push(obj);
updateAuditTrail = AuditTrail;
} else {
updateAuditTrail = [obj];
}
const auditTrail = updateAuditTrail.filter(x => x.Activity === 'Signed');
let isCompleted = false;
if (data.Signers && data.Signers.length > 0) {
if (auditTrail.length === data.Placeholders.length) {
isCompleted = true;
}
} else {
isCompleted = true;
}
const body = {
SignedUrl: url,
AuditTrail: updateAuditTrail,
IsCompleted: isCompleted,
};
const signedRes = await axios.put(serverUrl + '/classes/contracts_Document/' + docId, body, {
headers: {
'Content-Type': 'application/json',
'X-Parse-Application-Id': APPID,
'X-Parse-Master-Key': masterKEY,
},
});
return { isCompleted: isCompleted, message: 'success', AuditTrail: updateAuditTrail };
} catch (err) {
console.log('update doc err ', err);
return 'err';
}
}
// `sendCompletedMail` is used to send copy of completed document mail
async function sendCompletedMail(obj) {
const url = obj.url;
const doc = obj.doc;
const sender = obj.doc.ExtUserPtr;
const pdfName = doc.Name;
const mailLogo = 'https://qikinnovation.ams3.digitaloceanspaces.com/logo.png';
const recipient = sender.Email;
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>';
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;
}
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 params = {
extUserId: sender.objectId,
url: url,
from: 'OpenSign™',
recipient: recipient,
subject: subject,
pdfName: pdfName,
html: body,
mailProvider: obj.mailProvider,
};
const res = await axios.post(serverUrl + '/functions/sendmailv3', params, {
headers: {
'Content-Type': 'application/json',
'X-Parse-Application-Id': APPID,
'X-Parse-Master-Key': masterKEY,
},
});
// console.log('Res ', res);
}
// `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 {
// console.log('res ', res);
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);
}
});
// console.log('res ', res.data);
}
}
// `sendMailsaveCertifcate` is used send completion mail and update complete status of document
const sendMailsaveCertifcate = async (doc, P12Buffer, url, isCustomMail, mailProvider, userId) => {
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('./exports/certificate.pdf', signedCertificate);
const file = await uploadFile('certificate.pdf', './exports/certificate.pdf');
const body = { CertificateUrl: file.imageUrl };
await axios.put(serverUrl + '/classes/contracts_Document/' + doc.objectId, body, {
headers: {
'Content-Type': 'application/json',
'X-Parse-Application-Id': APPID,
'X-Parse-Master-Key': masterKEY,
},
});
// used in API only
if (doc.IsSendMail === false) {
console.log("don't send mail");
} else {
const mailObj = {
url: url,
isCustomMail: isCustomMail,
doc: doc,
mailProvider: mailProvider,
};
sendCompletedMail(mailObj);
}
saveFileUsage(CertificateBuffer.length, file.imageUrl, userId);
sendDoctoWebhook(doc, url, 'completed', '', file.imageUrl);
};
/**
*
* @param docId Id of Document in which user is signing
* @param pdfFile base64 of pdfFile which you want sign
* @returns if success {status, data} else {status, message}
*/
async function PDF(req) {
try {
if (!req?.user) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
} else {
const password = '';
const authUser = req?.user?.toJSON();
const docId = req.params.docId;
const reqUserId = req.params.userId;
const isCustomMail = req.params.isCustomCompletionMail || false;
const mailProvider = req.params.mailProvider || '';
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');
docQuery.equalTo('objectId', docId);
const resDoc = await docQuery.first({ useMasterKey: true });
if (!resDoc) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Document not found.');
}
const _resDoc = resDoc?.toJSON();
let signUser;
let className;
// `reqUserId` is send throught pdfrequest signing flow
if (reqUserId) {
// to get contracts_Contactbook details for currentuser from reqUserId
const _contractUser = _resDoc.Signers.find(x => x.objectId === reqUserId);
if (_contractUser) {
signUser = _contractUser;
className = 'contracts_Contactbook';
}
} else {
className = 'contracts_Users';
signUser = _resDoc.ExtUserPtr;
}
const username = signUser.Name;
const userEmail = signUser.Email;
if (req.params.pdfFile) {
// `PdfBuffer` used to create buffer from pdf file
let PdfBuffer = Buffer.from(req.params.pdfFile, 'base64');
// `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 p12Cert = new P12Signer(P12Buffer, { passphrase: process.env.PASS_PHRASE || null });
const UserPtr = {
__type: 'Pointer',
className: className,
objectId: signUser.objectId,
};
const obj = {
UserPtr: UserPtr,
SignedUrl: '',
Activity: 'Signed',
ipAddress: req.headers['x-real-ip'],
};
let updateAuditTrail;
if (_resDoc.AuditTrail && _resDoc.AuditTrail.length > 0) {
updateAuditTrail = [..._resDoc.AuditTrail, obj];
} else {
updateAuditTrail = [obj];
}
const auditTrail = updateAuditTrail.filter(x => x.Activity === 'Signed');
let isCompleted = false;
if (_resDoc.Signers && _resDoc.Signers.length > 0) {
if (auditTrail.length === _resDoc.Signers.length) {
isCompleted = true;
}
} else {
isCompleted = true;
}
const randomNumber = Math.floor(Math.random() * 5000);
const name = `exported_file_${randomNumber}.pdf`;
const pdfName = `./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);
}
//`new signPDF` create new instance of pdfBuffer and p12Buffer
const OBJ = new SignPdf();
// `signedDocs` is used to signpdf digitally
const signedDocs = await OBJ.sign(PdfBuffer, p12Cert);
//`saveUrl` is used to save signed pdf in exports folder
const saveUrl = fs.writeFileSync(pdfName, signedDocs);
pdfSize = signedDocs.length;
} else {
//`saveUrl` is used to save signed pdf in exports folder
const saveUrl = fs.writeFileSync(pdfName, PdfBuffer);
pdfSize = PdfBuffer.length;
}
// `uploadFile` is used to upload pdf to aws s3 and get it's url
const data = await uploadFile(name, pdfName);
if (data && data.imageUrl) {
// `axios` is used to update signed pdf url in contracts_Document classes for given DocId
const updatedDoc = await updateDoc(
req.params.docId, //docId
data.imageUrl, // url
signUser.objectId, // userID
req.headers['x-real-ip'], // client ipAddress,
_resDoc, // auditTrail, signers, etc data
className,
sign
);
sendDoctoWebhook(_resDoc, data.imageUrl, 'signed', signUser);
saveFileUsage(pdfSize, data.imageUrl, authUser.objectId);
if (updatedDoc && updatedDoc.isCompleted) {
const doc = { ..._resDoc, AuditTrail: updatedDoc.AuditTrail };
sendMailsaveCertifcate(
doc,
P12Buffer,
data.imageUrl,
isCustomMail,
mailProvider,
authUser.objectId
);
}
// `fs.unlinkSync` is used to remove exported signed pdf file from exports folder
fs.unlinkSync(pdfName);
console.log(`New Signed PDF created called: ${pdfName}`);
if (updatedDoc.message === 'success') {
return { status: 'success', data: data.imageUrl };
} else {
const error = new Error('Please provide required parameters!');
error.code = 400; // Set the error code (e.g., 400 for bad request)
throw error;
}
}
} else {
const error = new Error('Pdf file not present!');
error.code = 400; // Set the error code (e.g., 400 for bad request)
throw error;
}
}
} catch (err) {
console.log('Err in signpdf', err);
throw err;
}
}
export default PDF;
-326
View File
@@ -1,326 +0,0 @@
import SignPDF from './SignPDF.min.cjs';
import fs from 'node:fs';
import axios from 'axios';
import { pdflibAddPlaceholder } from './customSignPdf/pdflibplaceholder.min.js';
import { PDFDocument } from 'pdf-lib';
import { replaceMailVaribles, saveFileUsage } from '../../../Utils.js';
import GenerateCertificate from './GenerateCertificate.js';
const serverUrl = process.env.SERVER_URL,
APPID = process.env.APP_ID,
masterKEY = process.env.MASTER_KEY;
async function uploadFile(e, t) {
try {
var a = fs.readFileSync(t),
r = new Parse.File(e, [...a], 'application/pdf'),
i = (await r.save({ useMasterKey: !0 }), r.url());
return { imageUrl: i };
} catch (e) {
console.log('Err ', e), fs.unlinkSync(t);
}
}
async function updateDoc(a, r, i, s, o, n, l) {
try {
var c,
d,
p = {
UserPtr: { __type: 'Pointer', className: n, objectId: i },
SignedUrl: r,
Activity: 'Signed',
ipAddress: s,
SignedOn: new Date(),
Signature: l,
};
let e;
var m = (e =
o.AuditTrail && 0 < o.AuditTrail.length
? (-1 !==
(d = (c = JSON.parse(JSON.stringify(o.AuditTrail))).findIndex(
e => e.UserPtr.objectId === i && 'Created' !== e.Activity
))
? (c[d] = { ...c[d], ...p })
: c.push(p),
c)
: [p]).filter(e => 'Signed' === e.Activity);
let t = !1;
!((o.Signers && 0 < o.Signers.length && m.length !== o.Signers.length) || !(t = !0));
var g = { SignedUrl: r, AuditTrail: e, IsCompleted: t };
await axios.put(serverUrl + '/classes/contracts_Document/' + a, g, {
headers: {
'Content-Type': 'application/json',
'X-Parse-Application-Id': APPID,
'X-Parse-Master-Key': masterKEY,
},
});
return { isCompleted: t, message: 'success', AuditTrail: e };
} catch (e) {
return console.log('update doc err ', e), 'err';
}
}
async function sendCompletedMail(e) {
var t = e.url,
a = e.doc,
r = e.doc.ExtUserPtr,
i = a.Name,
s = r.Email;
let o = `Document "${i}" has been signed by all parties`,
n =
"<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=https://qikinnovation.ams3.digitaloceanspaces.com/logo.png 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>"${i}"</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 ' +
r.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 (e?.isCustomMail)
try {
var l,
c,
d,
p,
m,
g = new Parse.Query('partners_Tenant');
g.equalTo('UserId', { __type: 'Pointer', className: '_User', objectId: r.UserId.objectId });
const f = await g.first();
f &&
((l = JSON.parse(JSON.stringify(f)))?.CompletionSubject && (o = l?.CompletionSubject),
l?.CompletionBody && (n = l?.CompletionBody),
(c = a.ExpiryDate.iso),
(d = new Date(c).toLocaleDateString('en-US', {
day: 'numeric',
month: 'long',
year: 'numeric',
})),
(p = {
document_title: i,
sender_name: r.Name,
sender_mail: r.Email,
sender_phone: r.Phone,
receiver_name: r.Name,
receiver_email: r.Email,
receiver_phone: r.Phone,
expiry_date: d,
company_name: r.Company,
}),
(m = replaceMailVaribles(o, n, p)),
(o = m.subject),
(n = m.body));
} catch (e) {
console.log('error in fetch tenant in signpdf', e.message);
}
g = {
extUserId: r.objectId,
url: t,
from: 'OpenSign™',
recipient: s,
subject: o,
pdfName: i,
html: n,
mailProvider: e.mailProvider,
};
await axios.post(serverUrl + '/functions/sendmailv3', g, {
headers: {
'Content-Type': 'application/json',
'X-Parse-Application-Id': APPID,
'X-Parse-Master-Key': masterKEY,
},
});
}
async function sendDoctoWebhook(a, e, t, r, i) {
let s = [];
(s = r
? { name: r?.Name, email: r?.Email, phone: r?.Phone }
: a?.Signers?.map(e => ({ name: e.Name, email: e.Email, phone: e.Phone })) || [
{ name: a?.ExtUserPtr?.Name, email: a?.ExtUserPtr?.Email, phone: a?.ExtUserPtr?.Phone },
]),
a.ExtUserPtr?.Webhook &&
((r =
'signed' === t
? { signer: s, signedAt: new Date() }
: { signers: s, completedAt: new Date() }),
(t = {
event: t,
objectId: a?.objectId,
file: e || '',
...(i ? { certificate: i } : {}),
name: a?.Name,
note: a?.Note || '',
description: a?.Description || '',
...r,
createdAt: a?.createdAt,
}),
axios
.post(a?.ExtUserPtr?.Webhook, t, { headers: { 'Content-Type': 'application/json' } })
.then(e => {
try {
var t = new Parse.Object('contracts_Webhook');
t.set('Log', e?.status),
t.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: a.ExtUserPtr.UserId.objectId,
}),
t.save(null, { useMasterKey: !0 });
} catch (e) {
console.log('err save in contracts_Webhook', e.message);
}
})
.catch(e => {
console.log('Err send data to webhook', e.message);
try {
var t = new Parse.Object('contracts_Webhook');
t.set('Log', e?.status),
t.set('UserId', {
__type: 'Pointer',
className: '_User',
objectId: a.ExtUserPtr.UserId.objectId,
}),
t.save(null, { useMasterKey: !0 });
} catch (e) {
console.log('err save in contracts_Webhook', e.message);
}
}));
}
const sendMailsaveCertifcate = async (e, t, a, r, i, s) => {
var o = await GenerateCertificate(e),
o = await PDFDocument.load(o),
o =
(pdflibAddPlaceholder({
pdfDoc: o,
reason: 'Digitally signed by OpenSign.',
location: 'n/a',
signatureLength: 15e3,
}),
await o.save()),
o = Buffer.from(o),
t = await new SignPDF(o, t).signPDF(),
t =
(fs.writeFileSync('./exports/certificate.pdf', t),
await uploadFile('certificate.pdf', './exports/certificate.pdf')),
n = { CertificateUrl: t.imageUrl };
await axios.put(serverUrl + '/classes/contracts_Document/' + e.objectId, n, {
headers: {
'Content-Type': 'application/json',
'X-Parse-Application-Id': APPID,
'X-Parse-Master-Key': masterKEY,
},
}),
e.IsSendMail && !1 === e.IsSendMail
? console.log("don't send mail")
: sendCompletedMail({ url: a, isCustomMail: r, doc: e, mailProvider: i }),
saveFileUsage(o.length, t.imageUrl, s),
sendDoctoWebhook(e, a, 'completed', '', t.imageUrl);
};
async function PDF(o) {
try {
if (!o?.user)
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
{
var n = o?.user?.toJSON(),
e = o.params.docId;
const F = o.params.userId;
var l = o.params.isCustomCompletionMail || !1,
c = o.params.mailProvider || '',
d = o.params.signature || '',
t = new Parse.Query('contracts_Document'),
a =
(t.include('ExtUserPtr,Signers'),
t.equalTo('objectId', e),
await t.first({ useMasterKey: !0 }));
if (!a) throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Document not found.');
var r,
p = a?.toJSON();
let i, s;
F
? ((r = p.Signers.find(e => e.objectId === F)),
console.log('_contractUser ', r),
r && ((i = r), (s = 'contracts_Contactbook')))
: ((s = 'contracts_Users'), (i = p.ExtUserPtr));
var m,
g = i.Name,
f = i.Email;
if (!o.params.pdfFile) throw (((m = new Error('Pdf file not present!')).code = 400), m);
{
let e = Buffer.from(o.params.pdfFile, 'base64');
var u = process.env.PFX_BASE64,
h = Buffer.from(u, 'base64'),
y = {
UserPtr: { __type: 'Pointer', className: s, objectId: i.objectId },
SignedUrl: '',
Activity: 'Signed',
ipAddress: o.headers['x-real-ip'],
};
let t;
var P = (t = p.AuditTrail && 0 < p.AuditTrail.length ? [...p.AuditTrail, y] : [y]).filter(
e => 'Signed' === e.Activity
);
let a = !1;
!((p.Signers && 0 < p.Signers.length && P.length !== p.Signers.length) || !(a = !0));
var v,
b,
S,
w,
U,
D,
I = `exported_file_${Math.floor(5e3 * Math.random())}.pdf`,
_ = './exports/' + I;
let r = e.length;
r = (
a
? ((v = p.Signers?.map(e => e.Name + ' <' + e.Email + '>')),
(e =
v && 0 < v.length
? ((b = await PDFDocument.load(e)),
pdflibAddPlaceholder({
pdfDoc: b,
reason: 'Digitally signed by OpenSign for ' + v?.join(', '),
location: 'n/a',
signatureLength: 15e3,
}),
(S = await b.save()),
Buffer.from(S))
: ((w = await PDFDocument.load(e)),
pdflibAddPlaceholder({
pdfDoc: w,
reason: 'Digitally signed by OpenSign for ' + g + ' <' + f + '>',
location: 'n/a',
signatureLength: 15e3,
}),
(U = await w.save()),
Buffer.from(U))),
(D = await new SignPDF(e, h).signPDF()),
fs.writeFileSync(_, D),
D)
: (fs.writeFileSync(_, e), e)
).length;
var E = await uploadFile(I, _);
if (E && E.imageUrl) {
var x,
A,
j = await updateDoc(
o.params.docId,
E.imageUrl,
i.objectId,
o.headers['x-real-ip'],
p,
s,
d
);
if (
(sendDoctoWebhook(p, E.imageUrl, 'signed', i),
saveFileUsage(r, E.imageUrl, n.objectId),
j &&
j.isCompleted &&
((x = { ...p, AuditTrail: j.AuditTrail }),
sendMailsaveCertifcate(x, h, E.imageUrl, l, c, n.objectId)),
fs.unlinkSync(_),
console.log('New Signed PDF created called: ' + _),
'success' === j.message)
)
return { status: 'success', data: E.imageUrl };
throw (((A = new Error('Please provide required parameters!')).code = 400), A);
}
}
}
} catch (e) {
throw (console.log('Err in signpdf', e), e);
}
}
export default PDF;
@@ -1 +0,0 @@
const signer=require("node-signpdf").default;class SignPDF{constructor(e,t){this.pdfDoc=e,this.certificate=t}async signPDF(){return signer.sign(this.pdfDoc,this.certificate, process.env.PASS_PHRASE ? {passphrase : process.env.PASS_PHRASE}: null)}static unit8ToBuffer(e){var t=Buffer.alloc(e.byteLength),r=new Uint8Array(e);for(let e=0;e<t.length;++e)t[e]=r[e];return t}}module.exports=SignPDF;
@@ -1 +0,0 @@
class PDFAbstractReference{toString(){throw new Error("Must be implemented by subclasses")}}export default PDFAbstractReference;
@@ -1 +0,0 @@
import PDFAbstractReference from"./PDFAbstractReference.min.js";class PDFKitReferenceMock extends PDFAbstractReference{constructor(e,t=void 0){super(),this.index=e,void 0!==t&&Object.assign(this,t)}toString(){return this.index+" 0 R"}}export default PDFKitReferenceMock;
@@ -1,20 +0,0 @@
const ERROR_TYPE_UNKNOWN = 1,
ERROR_TYPE_INPUT = 2,
ERROR_TYPE_PARSE = 3,
ERROR_VERIFY_SIGNATURE = 4;
class SignPdfError extends Error {
constructor(R, E = ERROR_TYPE_UNKNOWN) {
super(R), (this.type = E);
}
}
(SignPdfError.TYPE_UNKNOWN = ERROR_TYPE_UNKNOWN),
(SignPdfError.TYPE_INPUT = ERROR_TYPE_INPUT),
(SignPdfError.TYPE_PARSE = ERROR_TYPE_PARSE),
(SignPdfError.VERIFY_SIGNATURE = ERROR_VERIFY_SIGNATURE);
export {
ERROR_TYPE_UNKNOWN,
ERROR_TYPE_INPUT,
ERROR_TYPE_PARSE,
ERROR_VERIFY_SIGNATURE,
SignPdfError,
};
@@ -1 +0,0 @@
const DEFAULT_SIGNATURE_LENGTH=8192,DEFAULT_BYTE_RANGE_PLACEHOLDER="**********",SUBFILTER_ADOBE_PKCS7_DETACHED="adbe.pkcs7.detached",SUBFILTER_ADOBE_PKCS7_SHA1="adbe.pkcs7.sha1",SUBFILTER_ADOBE_X509_SHA1="adbe.x509.rsa.sha1",SUBFILTER_ETSI_CADES_DETACHED="ETSI.CAdES.detached";export{DEFAULT_SIGNATURE_LENGTH,DEFAULT_BYTE_RANGE_PLACEHOLDER,SUBFILTER_ADOBE_PKCS7_DETACHED,SUBFILTER_ADOBE_PKCS7_SHA1,SUBFILTER_ADOBE_X509_SHA1,SUBFILTER_ETSI_CADES_DETACHED};
@@ -1,3 +0,0 @@
import findObject from"./findObject.min.js";import getIndexFromRef from"./getIndexFromRef.min.js";const createBufferPageWithAnnotation=(e,t,n,f)=>{e=findObject(e,t.xref,n).toString();let r,o,i;r=e.indexOf("/Annots"),i=-1<r?(o=e.indexOf("]",r),(i=e.substr(r,o+1-r)).substr(0,i.length-1)):(r=e.length,o=e.length,"/Annots [");t=getIndexFromRef(t.xref,n),n=f.toString(),i+=` ${n}]`,f=e.substr(0,r);let m="";return e.length>o&&(m=e.substr(o+1)),Buffer.concat([Buffer.from(t+` 0 obj
`),Buffer.from("<<\n"),Buffer.from(f+i+m+`
`),Buffer.from("\n>>\nendobj\n")])};export default createBufferPageWithAnnotation;
@@ -1,3 +0,0 @@
import getIndexFromRef from"./getIndexFromRef.min.js";const createBufferRootWithAcroform=(r,f,o)=>{var e=getIndexFromRef(f.xref,f.rootRef);return Buffer.concat([Buffer.from(e+` 0 obj
`),Buffer.from("<<\n"),Buffer.from(f.root+`
`),Buffer.from("/AcroForm "+o),Buffer.from("\n>>\nendobj\n")])};export default createBufferRootWithAcroform;
@@ -1,8 +0,0 @@
const createBufferTrailer=(f,r,e)=>{let o=[];return o[0]="0000000000 65535 f ",e.forEach((f,r)=>{f=("0000000000"+f).slice(-10);o[r+1]=r+` 1
${f} 00000 n `}),o=o.filter(f=>void 0!==f),Buffer.concat([Buffer.from("xref\n"),Buffer.from(r.xref.startingIndex+` 1
`),Buffer.from(o.join("\n")),Buffer.from("\ntrailer\n"),Buffer.from("<<\n"),Buffer.from(`/Size ${r.xref.maxIndex+1}
`),Buffer.from(`/Root ${r.rootRef}
`),Buffer.from(r.infoRef?`/Info ${r.infoRef}
`:""),Buffer.from(`/Prev ${r.xRefPosition}
`),Buffer.from(">>\n"),Buffer.from("startxref\n"),Buffer.from(f.length+`
`),Buffer.from("%%EOF")])};export default createBufferTrailer;
@@ -1 +0,0 @@
import getIndexFromRef from"./getIndexFromRef.min.js";const findObject=(e,t,f)=>{f=getIndexFromRef(t,f),t=t.offsets.get(f);let n=e.slice(t);return n=(n=(n=n.slice(0,n.indexOf("endobj","utf8"))).slice(n.indexOf("<<","utf8")+2)).slice(0,n.lastIndexOf(">>","utf8"))};export default findObject;
@@ -1 +0,0 @@
import SignPdfError from"./SignPdfError.min.js";const getIndexFromRef=(r,e)=>{var[o]=e.split(" "),o=parseInt(o);if(r.offsets.has(o))return o;throw new SignPdfError(`Failed to locate object "${e}".`,SignPdfError.TYPE_PARSE)};export default getIndexFromRef;
@@ -1 +0,0 @@
import getPagesDictionaryRef from"./getPagesDictionaryRef.min.js";import findObject from"./findObject.min.js";export default function getPageRef(e,t,i){var n=getPagesDictionaryRef(t),e=findObject(e,t.xref,n),t=e.indexOf("/Kids"),n=e.indexOf("[",t)+1,t=e.indexOf("]",t);const f=e.slice(n,t).toString();for(var r=[],o=0;o<f.length;o++)"R"===f[o]&&r.push(o);let a={},g=0;return r.forEach((e,t)=>{a={...a,[t+1]:f.substring(g+1,e+1)},g=e+1}),i?a[i]:a[1]}
@@ -1 +0,0 @@
import SignPdfError from"./SignPdfError.min.js";export default function getPagesDictionaryRef(r){r=/\/Pages\s+(\d+\s+\d+\s+R)/g.exec(r.root);if(null===r)throw new SignPdfError("Failed to find the pages descriptor. This is probably a problem in node-signpdf.",SignPdfError.TYPE_PARSE);return r[1]}
@@ -1 +0,0 @@
import PDFAbstractReference from"./PDFAbstractReference.min.js";const pad=(e,t)=>(Array(t+1).join("0")+e).slice(-t),escapableRe=/[\n\r\t\b\f()\\]/g,escapable={"\n":"\\n","\r":"\\r","\t":"\\t","\b":"\\b","\f":"\\f","\\":"\\\\","(":"\\(",")":"\\)"},swapBytes=e=>e.swap16();export default class PDFObject{static convert(t,a=null){if("string"==typeof t)return"/"+t;if(t instanceof String){let r=t,n=!1;for(let e=0,t=r.length;e<t;e+=1)if(127<r.charCodeAt(e)){n=!0;break}let e;return e=n?swapBytes(Buffer.from(``+r,"utf16le")):Buffer.from(r,"ascii"),`(${r=(r=(a?a(e):e).toString("binary")).replace(escapableRe,e=>escapable[e])})`}if(Buffer.isBuffer(t))return`<${t.toString("hex")}>`;if(t instanceof PDFAbstractReference)return t.toString();if(t instanceof Date){let e=`D:${pad(t.getUTCFullYear(),4)}${pad(t.getUTCMonth()+1,2)}${pad(t.getUTCDate(),2)}${pad(t.getUTCHours(),2)}${pad(t.getUTCMinutes(),2)}${pad(t.getUTCSeconds(),2)}Z`;return`(${e=a?(e=a(Buffer.from(e,"ascii")).toString("binary")).replace(escapableRe,e=>escapable[e]):e})`}if(Array.isArray(t))return`[${t.map(e=>PDFObject.convert(e,a)).join(" ")}]`;if("[object Object]"!=={}.toString.call(t))return"number"==typeof t?PDFObject.number(t):""+t;{const n=["<<"];return Object.entries(t).forEach(([e,t])=>{let r="";r=t&&-1!==t.toString().indexOf("<<")?t:PDFObject.convert(t,a),n.push(`/${e} `+r)}),n.push(">>"),n.join("\n")}}static number(e){if(-1e21<e&&e<1e21)return Math.round(1e6*e)/1e6;throw new Error("unsupported number: "+e)}}
@@ -1,28 +0,0 @@
import{DEFAULT_BYTE_RANGE_PLACEHOLDER,DEFAULT_SIGNATURE_LENGTH,SUBFILTER_ADOBE_PKCS7_DETACHED}from"./const.min.js";import PDFKitReferenceMock from"./PDFKitReferenceMock.min.js";import PNG from"png-js";import zlib from"zlib";const specialCharacters=["á","Á","é","É","í","Í","ó","Ó","ö","Ö","ő","Ő","ú","Ú","ű","Ű"],MARKERS=[65472,65473,65474,65475,65477,65478,65479,65480,65481,65482,65483,65484,65485,65486,65487],COLOR_SPACE_MAP={1:"DeviceGray",3:"DeviceRGB",4:"DeviceCMYK"};function getImage(e,t){let n;if(255===e[0]&&216===e[1])n=getJpgImage(t,e);else{if(137!==e[0]||"PNG"!==e.toString("ascii",1,4))throw new Error("Unknown image format.");n=getPngImage(t,e)}return n}function getAnnotationApparance(e,t,n){return e.ref({CropBox:[0,10,200,60],Type:"XObject",FormType:1,BBox:[0,10,200,60],Resources:`<</XObject <<
/Img${t.index} ${t.index} 0 R
>>
/Font <<
/f1 ${n.index} 0 R
>>
>>`,MediaBox:[0,10,200,60],Subtype:"Form"},null,getStream(t.index))}const getStream=e=>getConvertedText(`
1.0 1.0 1.0 rg
0.0 0.0 0.0 RG
q
q
200 0 0 50 0 10 cm
/Img${e} Do
Q
0 0 0 rg
BT
0 Tr
/f1 10.0 Tf
1.4 0 0 1 20 45.97412 Tm
ET
BT
0 Tr
/f1 10.0 Tf
1.4 0 0 1 20 33.56006 Tm
ET
Q`),getFont=(e,t)=>e.ref({Type:"Font",BaseFont:t,Encoding:"WinAnsiEncoding",Subtype:"Type1"}),getJpgImage=(e,t)=>{if(65496!==t.readUInt16BE(0))throw"SOI not found in JPEG";let n=2,a;for(;n<t.length&&(a=t.readUInt16BE(n),n+=2,!MARKERS.includes(a));)n+=t.readUInt16BE(n);if(!MARKERS.includes(a))throw"Invalid JPEG.";n+=2;var r=t[n++],o=t.readUInt16BE(n),i=(n+=2,t.readUInt16BE(n)),l=(n+=2,t[n++]),l=COLOR_SPACE_MAP[l],r={Type:"XObject",Subtype:"Image",BitsPerComponent:r,Width:i,Height:o,ColorSpace:l,Filter:"DCTDecode"},i=("DeviceCMYK"===l&&(r.Decode=[1,0,1,0,1,0,1,0]),e.ref(r,null,t));return i},getPngImage=(e,t)=>{var t=new PNG(t),n=t.hasAlphaChannel,a={Type:"XObject",Subtype:"Image",BitsPerComponent:n?8:t.bits,Width:t.width,Height:t.height,Filter:"FlateDecode"};if(n||(r=e.ref({Predictor:15,Colors:t.colors,BitsPerComponent:t.bits,Columns:t.width}),a.DecodeParms=r),0===t.palette.length?a.ColorSpace=t.colorSpace:(r=e.ref({stream:new Buffer(t.palette)}),a.ColorSpace=["Indexed","DeviceRGB",t.palette.length/3-1,r]),null!=t.transparency.grayscale){var r=t.transparency.grayscale;a.Mask=[r,r]}else if(t.transparency.rgb){var o,r=t.transparency["rgb"],i=[];for(o of r)i.push(o,o);a.Mask=i}else t.transparency.indexed?loadIndexedAlphaChannel(t):n&&(splitAlphaChannel(t),r=getSmask(e,t),a.Mask=r);return e.ref(a,null,t.imgData)},loadIndexedAlphaChannel=e=>{const o=e.transparency.indexed;return e.decodePixels(n=>{var a=new Buffer(e.width*e.height);let r=0;for(let e=0,t=n.length;e<t;e++)a[r++]=o[n[e]];e.alphaChannel=zlib.deflateSync(a)})},splitAlphaChannel=d=>{d.decodePixels(t=>{let e,n;var a=d.colors,r=d.width*d.height,o=new Buffer(r*a),i=new Buffer(r);let l=n=e=0;for(var c=t.length,g=16===d.bits?1:0;l<c;){for(let e=0;e<a;e++)o[n++]=t[l++],l+=g;i[e++]=t[l++],l+=g}d.imgData=zlib.deflateSync(o),d.alphaChannel=zlib.deflateSync(i)})},getSmask=(e,t)=>{let n;return console.log("image.hasAlphaChannel ",t.hasAlphaChannel),t.hasAlphaChannel&&(console.log("image.alphaChannel ",t.alphaChannel),n=e.ref({Type:"XObject",Subtype:"Image",Height:t.height,Width:t.width,BitsPerComponent:8,Filter:"FlateDecode",ColorSpace:"DeviceGray",Decode:[0,1],stream:t.alphaChannel})),n},getConvertedText=e=>e.split("").map(e=>specialCharacters.includes(e)?getOctalCodeFromCharacter(e):e).join(""),getOctalCodeFromCharacter=e=>"\\"+e.charCodeAt(0).toString(8),pdfkitAddPlaceholder=({pdf:e,pdfBuffer:a,reason:r,contactInfo:t="emailfromp1289@gmail.com",name:n="Name from p12",location:o="Location from p12",signatureLength:i=DEFAULT_SIGNATURE_LENGTH,byteRangePlaceholder:l=DEFAULT_BYTE_RANGE_PLACEHOLDER,subFilter:c=SUBFILTER_ADOBE_PKCS7_DETACHED,sign:g})=>{var d=getFont(e,"Helvetica"),s=getFont(e,"ZapfDingbats"),p=getFont(e,"Helvetica"),f=g.Base64,f=getAnnotationApparance(e,getImage(Buffer.from(f,"base64"),e),p,g),p=e.ref({Type:"Sig",Filter:"Adobe.PPKLite",SubFilter:c,ByteRange:[0,l,l,l],Contents:Buffer.from(String.fromCharCode(0).repeat(i)),Reason:new String(r),M:new Date,ContactInfo:new String(t),Name:new String(n),Location:new String(o)}),h=a.lastIndexOf("/Type /AcroForm"),c=-1!==h;let m=[],C;if(c){let e=h;var S=h-10;let t="",n=11;for(n;n<22;n+=1){var A=a.slice(h-n,S).toString();if("\n"===A[0])break;t=A,e=h-n}l=a.slice(e),i=l.slice(0,l.indexOf("endobj")).toString(),r=(C=parseInt(t),i.slice(i.indexOf("/Fields [")+9,i.indexOf("]")));m=r.split(" ").filter((e,t)=>t%3==0).map(e=>new PDFKitReferenceMock(e))}t=g.Left,n=g.Bottom,o=t+g.Width,l=n+g.Height,i=e.ref({Type:"Annot",Subtype:"Widget",FT:"Sig",Rect:[t,n,o,l],V:p,T:new String("Signature"+(m.length+1)),F:4,P:e.page.dictionary,AP:`<</N ${f.index} 0 R>>`,DA:new String("/Helvetica 0 Tf 0 g")});e.page.dictionary.data.Annots=[i];let T;return T=c?e.ref({Type:"AcroForm",SigFlags:3,Fields:[...m,i],DR:`<</Font
<</Helvetica ${d.index} 0 R/ZapfDingbats ${s.index} 0 R>>
>>`},C):e.ref({Type:"AcroForm",SigFlags:3,Fields:[...m,i]}),{signature:p,form:e._root.data.AcroForm=T,widget:i}};export default pdfkitAddPlaceholder;
@@ -1,87 +0,0 @@
import {
DEFAULT_BYTE_RANGE_PLACEHOLDER,
DEFAULT_SIGNATURE_LENGTH,
SUBFILTER_ADOBE_PKCS7_DETACHED,
} from './const.min.js';
import { SignPdfError } from './SignPdfError.min.js';
import {
PDFArray,
PDFDict,
PDFHexString,
PDFName,
PDFNumber,
PDFInvalidObject,
PDFString,
} from 'pdf-lib';
const pdflibAddPlaceholder = ({
pdfDoc: e = void 0,
pdfPage: o = void 0,
reason: t,
contactInfo: r = 'emailfromp1289@gmail.com',
name: i = 'Name from p12',
location: n,
signingTime: a = void 0,
signatureLength: P = DEFAULT_SIGNATURE_LENGTH,
byteRangePlaceholder: F = DEFAULT_BYTE_RANGE_PLACEHOLDER,
subFilter: D = SUBFILTER_ADOBE_PKCS7_DETACHED,
widgetRect: f = [0, 0, 0, 0],
appName: g = void 0,
}) => {
if (void 0 === e && void 0 === o)
throw new SignPdfError('PDFDoc or PDFPage must be set.', SignPdfError.TYPE_INPUT);
var e = e ?? o.doc,
o = o ?? e.getPages()[0],
m = PDFArray.withContext(e.context),
F =
(m.push(PDFNumber.of(0)),
m.push(PDFName.of(F)),
m.push(PDFName.of(F)),
m.push(PDFName.of(F)),
PDFHexString.of(String.fromCharCode(0).repeat(P))),
P = g ? { App: { Name: g } } : {},
g = e.context.obj({
Type: 'Sig',
Filter: 'Adobe.PPKLite',
SubFilter: D,
ByteRange: m,
Contents: F,
Reason: PDFString.of(t),
M: PDFString.fromDate(a ?? new Date()),
ContactInfo: PDFString.of(r),
Name: PDFString.of(i),
Location: PDFString.of(n),
Prop_Build: { Filter: { Name: 'Adobe.PPKLite' }, ...P },
}),
D = new Uint8Array(g.sizeInBytes()),
m = (g.copyBytesInto(D, 0), PDFInvalidObject.of(D)),
F = e.context.register(m);
const s = PDFArray.withContext(e.context);
f.forEach(e => s.push(PDFNumber.of(e)));
(t = e.context.formXObject([], { BBox: f, Resources: {} })),
(a = e.context.obj({
Type: 'Annot',
Subtype: 'Widget',
FT: 'Sig',
Rect: s,
V: F,
T: PDFString.of('Signature1'),
F: 4,
P: o.ref,
AP: { N: e.context.register(t) },
})),
(r = e.context.register(a));
let c = o.node.lookupMaybe(PDFName.of('Annots'), PDFArray),
d =
((c = void 0 === c ? e.context.obj([]) : c).push(r),
o.node.set(PDFName.of('Annots'), c),
e.catalog.lookupMaybe(PDFName.of('AcroForm'), PDFDict));
void 0 === d &&
((d = e.context.obj({ Fields: [] })),
(i = e.context.register(d)),
e.catalog.set(PDFName.of('AcroForm'), i));
let N;
N = d.has(PDFName.of('SigFlags')) ? d.get(PDFName.of('SigFlags')) : PDFNumber.of(0);
n = PDFNumber.of(3 | N.asNumber());
d.set(PDFName.of('SigFlags'), n), d.get(PDFName.of('Fields')).push(r);
};
export { pdflibAddPlaceholder };
@@ -1,2 +0,0 @@
import PDFObject from"./pdfObject.min.js";import PDFKitReferenceMock from"./PDFKitReferenceMock.min.js";import removeTrailingNewLine from"./removeTrailingNewLine.min.js";import{DEFAULT_SIGNATURE_LENGTH,SUBFILTER_ADOBE_PKCS7_DETACHED}from"./const.min.js";import pdfkitAddPlaceholder from"./pdfkitAddPlaceholder.min.js";import getIndexFromRef from"./getIndexFromRef.min.js";import readPdf from"./readPdf.min.js";import getPageRef from"./getPageRef.min.js";import createBufferRootWithAcroform from"./createBufferRootWithAcroform.min.js";import createBufferPageWithAnnotation from"./createBufferPageWithAnnotation.min.js";import createBufferTrailer from"./createBufferTrailer.min.js";const isContainBufferRootWithAcroform=e=>{e=/\/AcroForm\s+(\d+\s\d+\sR)/g.exec(e.toString());return null!=e&&null!=e[1]&&""!==e[1]},getAssembledPdf=(e,r,f,o)=>{let t=e;return t=Buffer.concat([t,Buffer.from("\n"),Buffer.from(r+` 0 obj
`),Buffer.from(PDFObject.convert(f))]),o&&(t=Buffer.concat([t,Buffer.from("\nstream\n"),Buffer.from(o),Buffer.from("\nendstream")])),t=Buffer.concat([t,Buffer.from("\nendobj\n")])},plainAddPlaceholder=({pdfBuffer:e,reason:r,contactInfo:f="emailfromp1289@gmail.com",name:o="Name from p12",location:t="Location from p12",signatureLength:n=DEFAULT_SIGNATURE_LENGTH,subFilter:i=SUBFILTER_ADOBE_PKCS7_DETACHED,sign:m})=>{let a=removeTrailingNewLine(e);const c=readPdf(a);var d=getPageRef(a,c,m.Page),u=getIndexFromRef(c.xref,d);const l=new Map;var s={ref:(e,r,f)=>{c.xref.maxIndex+=1;r=null!=r?r:c.xref.maxIndex;return l.set(r,a.length+1),a=getAssembledPdf(a,r,e,f),new PDFKitReferenceMock(c.xref.maxIndex)},page:{dictionary:new PDFKitReferenceMock(u,{data:{Annots:[]}})},_root:{data:{}}},{form:s,widget:e}=pdfkitAddPlaceholder({pdf:s,pdfBuffer:e,reason:r,contactInfo:f,name:o,location:t,signatureLength:n,subFilter:i,sign:m});return isContainBufferRootWithAcroform(a)||(r=getIndexFromRef(c.xref,c.rootRef),l.set(r,a.length+1),a=Buffer.concat([a,Buffer.from("\n"),createBufferRootWithAcroform(a,c,s)])),l.set(u,a.length+1),a=Buffer.concat([a,Buffer.from("\n"),createBufferPageWithAnnotation(a,c,d,e)]),a=Buffer.concat([a,Buffer.from("\n"),createBufferTrailer(a,c,l)])};export default plainAddPlaceholder;
@@ -1 +0,0 @@
import readRefTable from"./readRefTable.min.js";import findObject from"./findObject.min.js";const getValue=(e,t)=>{let r=e.indexOf(t);if(-1!==r)return e=e.slice(r),-1===(r=e.indexOf("/",1))&&(r=e.indexOf(">",1)),e.slice(t.length+1,r).toString().trim()},readPdf=e=>{var t=e.lastIndexOf("trailer"),r=e.slice(t,e.length-6),f=r.slice(r.lastIndexOf("startxref")+10).toString(),f=parseInt(f),i=readRefTable(e),n=getValue(r,"/Root");return{xref:i,rootRef:n,root:findObject(e,i,n).toString(),infoRef:getValue(r,"/Info"),trailerStart:t,previousXrefs:[],xRefPosition:f}};export default readPdf;
@@ -1 +0,0 @@
import SignPdfError from"./SignPdfError.min.js";import xrefToRefMap from"./xrefToRefMap.min.js";const getLastTrailerPosition=e=>{var r=e.lastIndexOf(Buffer.from("trailer","utf8")),r=e.slice(r,e.length-6),e=r.slice(r.lastIndexOf(Buffer.from("startxref","utf8"))+10).toString();return parseInt(e)},getXref=(e,r)=>{let t=e.slice(r);e=t.indexOf(Buffer.from("xref","utf8"));if(-1===e)throw new SignPdfError(`Could not find xref anywhere at or after ${r}.`,SignPdfError.TYPE_PARSE);if(0<e&&""!==t.slice(0,e).toString().replace(/\s*/g,""))throw new SignPdfError(`Expected xref at ${r} but found other content.`,SignPdfError.TYPE_PARSE);r=t.indexOf(Buffer.from("%%EOF","utf8"));if(-1===r)throw new SignPdfError("Expected EOF after xref and trailer but could not find one.",SignPdfError.TYPE_PARSE);if(!(r=(t=(t=(t=t.slice(0,r)).slice(e+4)).slice(t.indexOf("\n")+1)).toString().split("/Size")[1]))throw new SignPdfError("Size not found in xref table.",SignPdfError.TYPE_PARSE);if(null===(r=/^\s*(\d+)/.exec(r)))throw new SignPdfError("Failed to parse size of xref table.",SignPdfError.TYPE_PARSE);var r=parseInt(r[1]),[e,f]=t.toString().split("trailer");let n;null!=f.split("/Prev")[1]&&([,f]=/Prev (\d+)/g.exec(f),n=f);f=xrefToRefMap(e);return{size:r,prev:n,xRefContent:f}},getFullXrefTable=e=>{var r=getLastTrailerPosition(e),t=getXref(e,r);return void 0===t.prev?t.xRefContent:(e=e.slice(0,r),r=getFullXrefTable(e),new Map([...r,...t.xRefContent]))},readRefTable=e=>{e=getFullXrefTable(e);return{startingIndex:0,maxIndex:Math.max(...e.keys()),offsets:e}};export default readRefTable;export{getLastTrailerPosition,getXref,getFullXrefTable};
@@ -1 +0,0 @@
import SignPdfError from"./SignPdfError.min.js";const sliceLastChar=(r,e)=>{return r.slice(r.length-1).toString()===e?r.slice(0,r.length-1):r},removeTrailingNewLine=r=>{if(!(r instanceof Buffer))throw new SignPdfError("PDF expected as Buffer.",SignPdfError.TYPE_INPUT);r=sliceLastChar(r,"\n");if("\n%%EOF"!==(r=sliceLastChar(r,"\r")).slice(r.length-6).toString())throw new SignPdfError("A PDF file must end with an EOF line.",SignPdfError.TYPE_PARSE);return r};export default removeTrailingNewLine;
@@ -1 +0,0 @@
import SignPdfError from"./SignPdfError.min.js";const xrefToRefMap=r=>{r=r.split("\n").filter(r=>""!==r);let n=0,t=0;const f=new Map;return r.forEach(r=>{r=r.split(" ");if(2===r.length)n=parseInt(r[0]),t=parseInt(r[1]);else{if(t<=0)throw new SignPdfError("Too many lines in xref table.",SignPdfError.TYPE_PARSE);--t;var[r,,e]=r;if("f"!==e.trim()){if("n"!==e.trim())throw new SignPdfError(`Unknown in-use flag "${e}". Expected "n" or "f".`,SignPdfError.TYPE_PARSE);if(!/^\d+$/.test(r.trim()))throw new SignPdfError(`Expected integer offset. Got "${r}".`,SignPdfError.TYPE_PARSE);e=parseInt(r.trim());f.set(n,e)}n+=1}}),f};export default xrefToRefMap;
@@ -27,6 +27,7 @@ export default function reportJson(id, userId) {
'Signers.Name',
'Signers.Email',
'Signers.Phone',
'Placeholders',
],
};
@@ -67,6 +68,7 @@ export default function reportJson(id, userId) {
'Signers.Phone',
'Signers.UserId',
'AuditTrail',
'Placeholders',
],
};
// In progess report
@@ -75,7 +77,7 @@ export default function reportJson(id, userId) {
reportName: 'In-progress documents',
params: {
Type: { $ne: 'Folder' },
Signers: { $exists: true, $ne: [] },
SignedUrl: { $ne: null },
Placeholders: { $ne: null },
IsCompleted: { $ne: true },
IsDeclined: { $ne: true },
@@ -101,6 +103,8 @@ export default function reportJson(id, userId) {
'AuditTrail',
'AuditTrail.UserPtr',
'ExpiryDate',
'SendMail',
'Placeholders',
],
};
// completed documents report
@@ -129,6 +133,7 @@ export default function reportJson(id, userId) {
'Signers.Email',
'Signers.Phone',
'TimeToCompleteDays',
'Placeholders',
],
};
@@ -156,6 +161,7 @@ export default function reportJson(id, userId) {
'Signers.Name',
'Signers.Email',
'Signers.Phone',
'Placeholders',
],
};
// Expired Documents report
@@ -186,6 +192,7 @@ export default function reportJson(id, userId) {
'Signers.Name',
'Signers.Email',
'Signers.Phone',
'Placeholders',
],
};
// Recently sent for signatures report show on dashboard
@@ -194,7 +201,7 @@ export default function reportJson(id, userId) {
reportName: 'Recently sent for signatures',
params: {
Type: { $ne: 'Folder' },
Signers: { $exists: true, $ne: [] },
SignedUrl: { $ne: null },
Placeholders: { $ne: null },
IsCompleted: { $ne: true },
IsDeclined: { $ne: true },
@@ -216,6 +223,10 @@ export default function reportJson(id, userId) {
'Signers.Name',
'Signers.Email',
'Signers.Phone',
'AuditTrail',
'AuditTrail.UserPtr',
'ExpiryDate',
'Placeholders',
],
};
// Recent signature requests report show on dashboard
@@ -253,6 +264,7 @@ export default function reportJson(id, userId) {
'AuditTrail',
'Signers.Email',
'Signers.Phone',
'Placeholders',
],
};
// Drafts report show on dashboard
@@ -280,6 +292,7 @@ export default function reportJson(id, userId) {
'Signers.Name',
'Signers.Email',
'Signers.Phone',
'Placeholders',
],
};
// contact book report
@@ -320,6 +333,8 @@ export default function reportJson(id, userId) {
'Signers.Name',
'Signers.Email',
'Signers.Phone',
'Placeholders',
'IsPublic',
],
};
default:
@@ -0,0 +1,132 @@
import axios from 'axios';
const serverUrl = process.env.SERVER_URL;
const APPID = process.env.APP_ID;
const masterKEY = process.env.MASTER_KEY;
const clientUrl = process.env.PUBLIC_URL;
const ssoApiUrl = process.env.SSO_API_URL || 'https://sso.opensignlabs.com/api'; //'https://osl-jacksonv2.vercel.app/api';
/**
* ssoSign is function which is used to sign up/sign in with SSO
* @param code It is code return by jackson using authorize endpoint
* @param email It is user's email with user sign in/sign up
* @returns if success {email, name, phone message, sessiontoken} else on reject error {code, message}
*/
export default async function ssoSignin(request) {
const code = request.params.code;
const userEmail = request.params.email;
try {
const headers = { 'content-type': 'application/x-www-form-urlencoded' };
const axiosRes = await axios.post(
ssoApiUrl + '/oauth/token',
{
grant_type: 'authorization_code',
client_id: 'dummy',
tenant: 'Okta-dev-nxglabs-in',
product: 'OpenSign',
client_secret: 'dummy',
redirect_uri: clientUrl + '/sso',
code: code,
},
{ headers: headers }
);
const ssoAccessToken = axiosRes.data && axiosRes.data.access_token;
const authData = { sso: { id: userEmail, access_token: ssoAccessToken } };
const userQuery = new Parse.Query(Parse.User);
userQuery.equalTo('username', 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) {
const response = await axios.get(ssoApiUrl + '/oauth/userinfo', {
headers: {
Authorization: `Bearer ${ssoAccessToken}`,
},
});
const sessiontoken = SignIn.data.sessionToken;
// console.log('sso sessiontoken', sessiontoken);
const payload = {
email: userEmail,
name: response.data?.firstName + ' ' + response.data?.lastName,
phone: response?.data?.phone || '',
message: 'User Sign In',
sessiontoken: sessiontoken,
};
return payload;
}
} catch (err) {
const errCode = err?.response?.data?.code || err?.response?.status || err?.code || 400;
const message =
err?.response?.data?.error ||
err?.response?.data ||
err?.message ||
'Internal server error.';
console.log('err in user sso sign in', errCode, message);
throw new Parse.Error(errCode, message);
}
} else {
// console.log("in sign up condition");
const response = await axios.get(ssoApiUrl + '/oauth/userinfo', {
headers: {
Authorization: `Bearer ${ssoAccessToken}`,
},
});
if (response.data && response.data.id) {
try {
const SignUp = await axios.post(
serverUrl + '/users',
{
authData: authData,
username: response.data.email,
email: response.data.email,
phone: response.data?.phone,
name: response.data?.firstName + ' ' + response.data?.lastName,
},
{
headers: {
'X-Parse-Application-Id': APPID,
'X-Parse-Revocable-Session': '1',
},
}
);
if (SignUp.data) {
const sessiontoken = SignUp.data.sessionToken;
const payload = {
email: userEmail,
name: SignUp?.data?.name,
phone: SignUp?.data?.phone || '',
message: 'User Sign Up',
sessiontoken: sessiontoken,
};
return payload;
}
} catch (err) {
const errCode = err?.response?.data?.code || err?.response?.status || err?.code || 400;
const message =
err?.response?.data?.error ||
err?.response?.data ||
err?.message ||
'Internal server error.';
console.log('err in user sso sign up', errCode, message);
throw new Parse.Error(errCode, message);
}
}
}
} catch (err) {
const errCode = err?.response?.status || err?.code || 400;
const message = err?.response?.data || err?.message || 'Internal server error.';
console.log('err in ssoSign', errCode, message);
throw new Parse.Error(errCode, message);
}
}
@@ -30,7 +30,9 @@ async function saveUser(userDetails) {
user.set('username', userDetails.email);
user.set('password', userDetails.password);
user.set('email', userDetails.email);
user.set('phone', userDetails.phone);
if (userDetails?.phone) {
user.set('phone', userDetails.phone);
}
user.set('name', userDetails.name);
const res = await user.signUp();
@@ -77,7 +79,9 @@ export default async function usersignup(request) {
objectId: user.id,
});
partnerQuery.set('ContactNumber', userDetails.phone);
if (userDetails?.phone) {
partnerQuery.set('ContactNumber', userDetails.phone);
}
partnerQuery.set('TenantName', userDetails.name);
partnerQuery.set('EmailAddress', userDetails.email);
partnerQuery.set('IsActive', true);
@@ -113,7 +117,9 @@ export default async function usersignup(request) {
newObj.set('UserRole', userDetails.role);
newObj.set('Email', userDetails.email);
newObj.set('Name', userDetails.name);
newObj.set('Phone', userDetails.phone);
if (userDetails?.phone) {
newObj.set('Phone', userDetails?.phone);
}
newObj.set('TenantId', {
__type: 'Pointer',
className: 'partners_Tenant',
@@ -126,7 +132,9 @@ export default async function usersignup(request) {
newObj.set('JobTitle', userDetails.jobTitle);
}
const extRes = await newObj.save(null, { useMasterKey: true });
await saveSubscription(extRes.id, user.id, tenantRes.id, subscription);
if (subscription) {
await saveSubscription(extRes.id, user.id, tenantRes.id, subscription);
}
return { message: 'User sign up', sessionToken: user.sessionToken };
}
} catch (err) {