initial commit

This commit is contained in:
nxglabs
2023-10-22 01:37:25 +05:30
parent 80327652c3
commit 97fd28697a
182 changed files with 55033 additions and 122 deletions
+262
View File
@@ -0,0 +1,262 @@
/* --Description :cloud function to add or attach user in given role */
//-- Export Modules
import 'dotenv/config.js';
import axios from 'axios';
export async function addUserToGroups(request) {
try {
var roleName = request.params.roleName;
if (roleName == undefined) {
return Promise.reject('Error:roleName not found!');
}
var appName = request.params.appName;
if (appName == undefined) {
return Promise.reject('Error:appName not found!');
}
var chkappName = appName + '_';
//console.log("roleName " + roleName);
var userId = request.params.userId;
//console.log("userId " + userId);
console.log('addUserToGroups');
var response = {};
var rolelist = {};
var user = {
users: {
__op: 'AddRelation',
objects: [{ __type: 'Pointer', className: '_User', objectId: userId }],
},
};
var Role = roleName;
var chkappnam = Role.split('_')[0];
chkappnam = chkappnam + '_';
// if (!chkappnam == chkappName) {
// return Promise.reject("Error:Please check role it should belong to current appllication");
// }
function getAccessType(request) {
return new Promise(function (resolve, reject) {
const options = {
url: process.env.SERVER_URL + '/classes/w_appinfo?where={"appname":"' + appName + '"}',
method: 'get',
headers: {
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Master-Key': process.env.MASTER_KEY,
},
};
axios(options)
.then(x => {
const body = x.data;
var accessType;
if (body['results'].length !== 0) {
accessType = body['results'][0]['accessType'];
} else {
reject('Error:app not found!');
}
var error = accessType == '' ? true : false;
if (error) {
reject('result not found!');
} else {
resolve(accessType);
}
})
.catch(err => {
if (err) {
console.error(err);
return;
}
});
});
}
var role = appName + 'appeditor';
var appaccessType = await getAccessType(request);
//console.log("appaccessType");
//console.log(appaccessType);
if (appaccessType == 'public') {
adduserToRole();
} else {
//--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',
headers: {
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Session-Token': request.headers['sessiontoken'],
},
};
axios(options)
.then(x => {
const body = x.data;
var error = body == '' ? true : false;
if (error) {
reject('result not found!');
} else {
resolve(body);
}
})
.catch(err => {
if (err) {
console.error(err);
return;
}
});
});
}
var userData = await getuserid(request);
if (userData.objectId == undefined) {
return Promise.reject('Error:user not found!');
}
var chkuserid = userData.objectId;
//console.log("chkuserid "+chkuserid);
var url =
process.env.SERVER_URL +
'/roles?where={"users":{"__type":"Pointer","className":"_User","objectId":"' +
chkuserid +
'"},"name": {"$regex": "' +
chkappName +
'"}}';
//-- check user role
function getRoleList(chkuserid) {
return new Promise(function (resolve, reject) {
const options = {
url: url,
method: 'get',
headers: {
'X-Parse-Application-Id': process.env.APP_ID,
},
};
axios(options)
.then(x => {
const body = x.data;
if (body['results'].length == 0) {
reject('Error:user not found');
}
var error = body == '' ? true : false;
if (error) {
reject('result not found!');
} else {
resolve(body);
}
})
.catch(err => {
if (err) {
console.error(err);
return;
}
});
});
}
rolelist = await getRoleList(chkuserid);
//console.log("rolelist");
// console.log(rolelist);
var roleres = [];
var result;
for (var i = 0; i < rolelist['results'].length; i++) {
var rolenum = rolelist['results'][i]['name'];
var appnam = rolenum.split('_')[0];
appnam = appnam + '_';
if (appnam == chkappName) {
result = true;
}
}
if (result == true) {
adduserToRole();
} else {
return Promise.reject('Error:user of this app can only add user to Role');
}
}
//--after validation call adduserToRole function
async function adduserToRole() {
var roleNam = roleName;
var roleid = await getroleobjId(roleNam);
console.log('roleid');
console.log(roleid);
var response = await adduserid(roleid);
/*process.stdin.resume();
// listen to the event
process.on('SIGTERM', () => {
process.emit('cleanup');
})*/
return response;
}
//--function to get the role objId
function getroleobjId(roleNam) {
return new Promise(function (resolve, reject) {
const options = {
url: process.env.SERVER_URL + '/roles?where={"name":"' + roleNam + '"}',
method: 'get',
headers: {
'X-Parse-Application-Id': process.env.APP_ID,
},
};
axios(options)
.then(x => {
const body = x.data;
var roleid;
if (body['results'].length !== 0) {
roleid = body['results'][0]['objectId'];
} else {
reject('Error:Role not found!');
}
var error = roleid == '' ? true : false;
if (error) {
reject('result not found!');
} else {
resolve(roleid);
}
})
.catch(err => {
if (err) {
console.error(err);
return;
}
});
});
}
//--function to add the userid to role
function adduserid(roleid) {
return new Promise(function (resolve, reject) {
const options = {
url: process.env.SERVER_URL + '/roles/' + roleid,
method: 'PUT',
headers: {
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Master-Key': process.env.MASTER_KEY,
'Content-Type': 'application/json',
},
data: user,
};
axios(options)
.then(x => {
const body = x.data;
var error = body == '' ? true : false;
if (error) {
reject('result not found!');
} else {
console.log('user added to role');
resolve(body);
}
})
.catch(err => {
if (err) {
console.error(err);
return;
}
});
});
}
} catch (err) {
console.log('err in AddUserToRole');
console.log(err);
return Promise.reject('Error:exception in query,Result not Found');
}
}
@@ -0,0 +1,94 @@
async function AuthLoginAsMail(request) {
try {
//function for login user using user objectId without touching user's password
let otpN = request.params.otp;
let otp = parseInt(otpN);
let email = request.params.email;
let message;
//checking otp is correct or not which already save in defaultdata_Otp class
const checkOtp = new Parse.Query('defaultdata_Otp');
checkOtp.equalTo('Email', email);
const res = await checkOtp.first({ useMasterKey: true });
if (res !== undefined) {
let resOtp = res.get('OTP');
if (resOtp === otp) {
var result = await getToken(request);
return result;
async function getToken(request) {
return new Promise(function (resolve, reject) {
var query = new Parse.Query(Parse.User);
query.equalTo('email', email);
query
.first({ useMasterKey: true })
.then(user => {
//call loginAs function to use login method passing user objectId as a userId
const url = `${serverUrl}/loginAs`;
axios({
method: 'POST',
url: url,
headers: {
'Content-Type': 'application/json;charset=utf-8',
'X-Parse-Application-Id': process.env.APP_ID,
'X-Parse-Master-Key': process.env.MASTER_KEY,
},
params: {
userId: user.id,
},
}).then(
function (httpResponse) {
// console.log("httpResponse")
// console.log(httpResponse.data)
resolve(httpResponse.data);
},
function (httpResponse) {
console.error('User is not found' + httpResponse.status);
reject('User is not found!');
}
);
// user couldn't find lets sign up!
})
.catch(() => {
let user = new Parse.User();
user.set('username', email);
user.set('email', email);
user.set('password', pass);
user
.save()
.then(token => {
var error = token == '' ? true : false;
if (error) {
reject('result not found!');
} else {
resolve(token);
}
})
.catch(e => {
console.log('error in auth');
console.log(e);
return Promise.reject(e);
});
});
});
}
} else {
message = `Invalid Otp`;
return message;
}
} else {
message = 'user not found!';
return message;
}
} catch (err) {
console.log('err in Auth');
console.log(err);
return Promise.reject('Result not found', err);
}
}
export default AuthLoginAsMail;
@@ -0,0 +1,79 @@
/* --Description :cloud function called to get installed applist from client db */
//-- Export Modules
import mongoose from 'mongoose';
import { w_appinfoSchema } from '../models/appInfoclass.js';
import { orgAppsSchema } from '../models/orgApps.js';
import axios from 'axios';
export default async function CheckInstalledApp(request) {
const organization = request.params.orgName;
const options = { useNewUrlParser: true };
const baseDb = process.env.MONGODB_URI;
const serverUrl = process.env.SERVER_URL;
const appId = process.env.APP_ID;
// const sess = request.params._SessionToken;
//--function to get the userid from session token
try {
const sess = request.headers['sessiontoken'];
const user = await axios.get(serverUrl + '/users/me', {
headers: {
'X-Parse-Application-Id': appId,
'X-Parse-Session-Token': sess,
},
});
if (user.data && user.data.objectId) {
// get client db from baseDb
try {
const getDbUrl = async orgName => {
try {
const db = await mongoose.createConnection(baseDb, options).asPromise();
const orgAppsModel = db.model('orgApps', orgAppsSchema);
const res = await orgAppsModel.findOne({ appName: orgName });
console.log('dburl ', res.parseServer.databaseURI);
return res.parseServer.databaseURI;
} catch (err) {
console.log('result not found in orgApps ', err);
return 'result not found!';
}
};
const dbUrl = await getDbUrl(organization);
// targetConString = client db
// var targetConString = `mongodb+srv://doadmin:k0Nn4Q8L96vq715s@qik-server-prod-db-5054d37b.mongo.ondigitalocean.com/${dbName}?authSource=admin&replicaSet=qik-server-prod-db&tls=true`;
const targetConString = dbUrl;
async function getAppInfo() {
console.log('getAppInfo ');
try {
const db = await mongoose.createConnection(targetConString, options).asPromise();
const w_appinfomodel = db.model('w_appinfo', w_appinfoSchema);
const res = await w_appinfomodel.find({});
// console.log('getAppInfo ', res);
if (res.length > 0) {
const ress = JSON.stringify(res);
const result = { count: res.length, appList: ress };
return result;
} else {
const result = { count: res.length, appList: [] };
return result;
}
// console.log(appId);
} catch (err) {
console.log('err in getAppData', err);
}
}
const appInfo = await getAppInfo();
return appInfo;
} catch (err) {
console.log('err ', err);
const res = { error: 'Internal Server err ' };
return res;
}
}
} catch (err) {
return Promise.reject('Invalid session token!');
}
}
@@ -0,0 +1,26 @@
async function ContactbookAftersave(request) {
/* In beforesave or aftersave if you want to check if an object is being inserted or updated
you can check as follows */
if (!request.original) {
const user = request.user;
const object = request.object;
// Retrieve the current ACL
const acl = new Parse.ACL();
// Ensure the current user has read access
if (acl) {
acl.setReadAccess(user, true);
acl.setWriteAccess(user, true);
acl.setReadAccess(object.get('UserId'), true);
acl.setWriteAccess(object.get('UserId'), true);
object.setACL(acl);
// Continue saving the object
return object.save(null, { useMasterKey: true });
}
} else {
console.log('Object being update');
}
}
export default ContactbookAftersave;
@@ -0,0 +1,38 @@
async function ContractUsersAftersave(request) {
console.log('In contracts_Users aftersave');
if (!request.original) {
const shareWithTeam = request.object.get('ShareWithTeam');
const newACL = new Parse.ACL();
const contactbook = new Parse.Object('contracts_Contactbook');
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'));
contactbook.set('ExtUserPtr', {
__type: 'Pointer',
className: 'contracts_Users',
objectId: request.object.id,
});
contactbook.set('UserId', request.object.get('UserId'));
// newACL.setPublicReadAccess(false);
// newACL.setPublicWriteAccess(false);
const tenant_Id = request.object.get('TenantId');
if (shareWithTeam) {
if (tenant_Id) {
newACL.setReadAccess('tenant_' + tenant_Id.id, true);
newACL.setWriteAccess('tenant_' + tenant_Id.id, true);
}
newACL.setReadAccess(request.user, true);
newACL.setWriteAccess(request.user, true);
} else {
newACL.setReadAccess(request.user, true);
newACL.setWriteAccess(request.user, true);
}
contactbook.setACL(newACL);
contactbook.save(null, { useMasterkey: true });
}
}
export default ContractUsersAftersave;
@@ -0,0 +1,120 @@
async function DocumentAftersave(request) {
try {
if (!request.original) {
console.log('new entry is insert in contracts_Document');
const createdAt = request.object.get('createdAt');
const Folder = request.object.get('Type');
// console.log("createdAt")
// console.log(createdAt)
// console.log("Folder")
// console.log(Folder)
// console.log("before If condition")
if (createdAt && Folder === undefined) {
// console.log("IN If condition")
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);
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);
await updateQuery.save(null, { useMasterKey: true });
}
const signers = request.object.get('Signers');
// console.log("Signers")
// console.log(signers.length)
// update acl of New Document If There are signers present in array
if (signers && signers.length > 0) {
await updateAclDoc(request.object.id);
} else {
await updateSelfDoc(request.object.id);
}
} else {
if (request.user) {
const signers = request.object.get('Signers');
if (signers && signers.length > 0) {
await updateAclDoc(request.object.id);
} else {
await updateSelfDoc(request.object.id);
}
}
}
} catch (err) {
console.log('err in aftersave of contracts_Document');
console.log(err);
}
async function updateAclDoc(objId) {
// console.log("In side updateAclDoc func")
// console.log(objId)
const Query = new Parse.Query('contracts_Document');
Query.include('Signers');
const updateACL = await Query.get(objId, { useMasterKey: true });
const res = JSON.parse(JSON.stringify(updateACL));
// console.log("res");
// console.log(JSON.stringify(res));
const UsersPtr = res.Signers.map(item => item.UserId);
if (res.Signers[0].ExtUserPtr) {
const ExtUserSigners = res.Signers.map(item => {
return {
__type: 'Pointer',
className: 'contracts_Users',
objectId: item.ExtUserPtr.objectId,
};
});
updateACL.set('Signers', ExtUserSigners);
}
// console.log("UsersPtr")
// console.log(JSON.stringify(UsersPtr))
const newACL = new Parse.ACL();
newACL.setPublicReadAccess(false);
newACL.setPublicWriteAccess(false);
newACL.setReadAccess(request.user, true);
newACL.setWriteAccess(request.user, true);
UsersPtr.forEach(x => {
newACL.setReadAccess(x.objectId, true);
newACL.setWriteAccess(x.objectId, true);
});
updateACL.setACL(newACL);
updateACL.save(null, { useMasterKey: true });
}
async function updateSelfDoc(objId) {
// console.log("In side updateSelfDoc func")
// console.log(objId)
const Query = new Parse.Query('contracts_Document');
const updateACL = await Query.get(objId, { useMasterKey: true });
const res = JSON.parse(JSON.stringify(updateACL));
// console.log("res");
// console.log(JSON.stringify(res));
const newACL = new Parse.ACL();
newACL.setPublicReadAccess(false);
newACL.setPublicWriteAccess(false);
newACL.setReadAccess(request.user, true);
newACL.setWriteAccess(request.user, true);
updateACL.setACL(newACL);
updateACL.save(null, { useMasterKey: true });
}
}
export default DocumentAftersave;
@@ -0,0 +1,92 @@
import axios from "axios";
const serverUrl = 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" };
}
}
}
@@ -0,0 +1,90 @@
import axios from "axios";
const serverUrl = 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 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) {
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" };
}
}
}
@@ -0,0 +1,418 @@
/* --Description :cloud function called to copy app from default db */
//-- Export Modules
import mongoose from 'mongoose';
import { _SCHEMASchema } from '../models/schemaClass.js';
import { _RoleSchema } from '../models/RoleClass.js';
import { _RolejoinSchema } from '../models/RoleJoin.js';
import { _UserjoinSchema } from '../models/UserJoin.js';
import { w_appinfoSchema } from '../models/appInfoclass.js';
import { w_formV3Schema } from '../models/w_formv3class.js';
import { w_menuSchema } from '../models/w_menuclass.js';
import { w_reportSchema } from '../models/w_reportclass.js';
import { DashboardSchema } from '../models/w_dashboardclass.js';
import { DBFunctionSchema } from '../models/DBFunctionClass.js';
import { orgAppsSchema } from '../models/orgApps.js';
import axios from 'axios';
export async function InstallApp(request) {
try {
// console.log("sess ", request)
//--function to get the userid from session token
const sess = request.headers['sessiontoken'];
const serverUrl = process.env.SERVER_URL;
const appId = process.env.APP_ID;
const user = await axios.get(serverUrl + '/users/me', {
headers: {
'X-Parse-Application-Id': appId,
'X-Parse-Session-Token': sess,
},
});
if (user.data && user.data.objectId) {
var originalappname = request.params.appname;
var appname = originalappname + '_';
//externalInstallation should be true for migrate from server2 to server3 data
//externalInstallation should be false for migrate from default db to client db data
const externalInstallation = request.params.externalSrc;
// var chkbaseurl = request.params.baseurl;
// console.log('baseurl ' + chkbaseurl);
var organization = request.params.organization;
// var dbName = request.params.dbName;
var UserId = request.params.UserId;
//--check if value of connection string is blank or variable
if (organization == '') {
return Promise.reject('Error:please provide organization');
}
if (originalappname == '') {
return Promise.reject('Error:please provide appname');
}
const options = { useNewUrlParser: true };
const baseDb = process.env.MONGODB_URI;
// get client db from baseDb
const getDbUrl = async orgName => {
try {
const db = await mongoose.createConnection(baseDb, options).asPromise();
const orgAppsModel = db.model('orgApps', orgAppsSchema);
const res = await orgAppsModel.findOne({ appName: orgName });
console.log('dburl ', res.parseServer.databaseURI);
return res.parseServer.databaseURI;
} catch (err) {
console.log('result not found in getroleanduser ', err);
return 'result not found!';
}
};
const dbUrl = await getDbUrl(organization);
// targetConString = client db
// var targetConString = `mongodb+srv://doadmin:k0Nn4Q8L96vq715s@qik-server-prod-db-5054d37b.mongo.ondigitalocean.com/${dbName}?authSource=admin&replicaSet=qik-server-prod-db&tls=true`;
const targetConString = dbUrl;
// sourceConString = default db
var sourceConString;
if (externalInstallation) {
sourceConString =
'mongodb+srv://doadmin:k0Nn4Q8L96vq715s@qik-server-prod-db-5054d37b.mongo.ondigitalocean.com/heroku_vcjcwn64?authSource=admin&replicaSet=qik-server-prod-db&tls=true';
} else {
sourceConString =
'mongodb+srv://doadmin:k0Nn4Q8L96vq715s@qik-server-prod-db-5054d37b.mongo.ondigitalocean.com/defaultDJIC?authSource=admin&replicaSet=qik-server-prod-db&tls=true';
}
if (targetConString == sourceConString) {
return Promise.reject("Error:Soure and target database server can't be same");
}
const randomId = function (length) {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
};
const appRoleId = randomId(10);
try {
let Trg_SCHEMASmodel;
let Src_SCHEMASmodel;
let appId;
let defaultRoles;
let Trg__RoleModel;
let msg;
let status;
let trg_w_appinfomodel;
try {
const db = await mongoose.createConnection(targetConString, options).asPromise();
trg_w_appinfomodel = db.model('w_appinfo', w_appinfoSchema);
const res = await trg_w_appinfomodel.find({
appname: originalappname,
});
if (res.length > 0) {
msg = 'App is already exists';
status = 137;
} else {
await getAppInfo();
}
} catch (err) {
console.log('err is checking app exists', err);
return Promise.reject('err is checking app exists');
}
// try {
// const db = await mongoose.createConnection(targetConString, options).asPromise();
// Trg_SCHEMASmodel = db.model('_SCHEMA', _SCHEMASchema);
// const res = await Trg_SCHEMASmodel.find({ _id: { $regex: '.*' + appname + '.*' } });
// if (res.length > 0) {
// msg = 'App is already exists';
// status = 137;
// } else {
// await getAppInfo();
// }
// } catch (err) {
// console.log('err is checking app exists', err);
// return Promise.reject('err is checking app exists');
// }
async function getAppInfo() {
console.log('getAppInfo ');
try {
const db = await mongoose.createConnection(sourceConString, options).asPromise();
const w_appinfomodel = db.model('w_appinfo', w_appinfoSchema);
const res = await w_appinfomodel.find({
appname: originalappname,
});
// console.log('getAppInfo ', res);
appId = res[0]['_id'];
const parseRes = JSON.parse(JSON.stringify(res));
if (parseRes[0].settings && parseRes[0].settings.length > 0) {
defaultRoles = parseRes[0].settings.map(x => {
return {
_id: randomId(10),
name: x.role,
_wperm: ['*'],
_rperm: ['*'],
_acl: { '*': { r: true, w: true } },
_created_at: new Date(),
_updated_at: new Date(),
};
});
// console.log('defaultRoles ', defaultRoles);
}
// console.log(appId);
await saveAppInfo(res);
} catch (err) {
console.log('err in getAppData', err);
}
}
async function saveAppInfo(res) {
console.log('saveAppInfo');
try {
const db = await mongoose.createConnection(targetConString, options).asPromise();
// trg_w_appinfomodel = db.model('w_appinfo', w_appinfoSchema);
const appInfoRes = await trg_w_appinfomodel.collection.insertMany(res);
// console.log('saveAppInfo ', appInfoRes);
console.log('app info inserted');
await getAppFunctions();
} catch (err) {
console.log('err in saveAppData', err);
}
}
async function getAppFunctions() {
console.log('getAppFunctions');
try {
var appIdpointer = 'w_appinfo$' + appId;
const db = await mongoose.createConnection(sourceConString, options).asPromise();
const DBFunctionModel = db.model('w_DBFunctions', DBFunctionSchema);
const dbFnRes = await DBFunctionModel.find({ _p_appId: appIdpointer });
await saveAppFunctions(dbFnRes);
} catch (err) {
console.log('err is get app function', err);
return Promise.reject('err is get app functions');
}
}
async function saveAppFunctions(res) {
console.log('saveAppFunctions');
if (res.length > 0) {
try {
const db = await mongoose.createConnection(targetConString, options).asPromise();
const DBFunctionModel = db.model('w_DBFunctions', DBFunctionSchema);
const src_res = await DBFunctionModel.collection.insertMany(res);
console.log('app functions inserted');
} catch (err) {
console.log('err in save app functions', err);
}
}
await getAppDashboard();
}
async function getAppDashboard() {
try {
var appIdpointer = 'w_appinfo$' + appId;
const db = await mongoose.createConnection(sourceConString, options).asPromise();
const w_Dashboardmodel = db.model('w_dashboard', DashboardSchema);
const dashboardRes = await w_Dashboardmodel.find({ _p_appId: appIdpointer });
await saveAppDashboard(dashboardRes);
} catch (err) {
console.log('err is get app dashboards', err);
}
}
async function saveAppDashboard(res) {
if (res.length > 0) {
try {
const db = await mongoose.createConnection(targetConString, options).asPromise();
const w_Dashboardmodel = db.model('w_dashboard', DashboardSchema);
const src_res = await w_Dashboardmodel.collection.insertMany(res);
console.log('app dashboard inserted');
} catch (err) {
console.log('err in save app dashboards', err);
}
}
await getAppForms();
}
async function getAppForms() {
try {
var appIdpointer = 'w_appinfo$' + appId;
const db = await mongoose.createConnection(sourceConString, options).asPromise();
const w_formV3model = db.model('w_formV3', w_formV3Schema);
const formsRes = await w_formV3model.find({ _p_appId: appIdpointer });
await saveAppForms(formsRes);
} catch (err) {
console.log('err is get app forms', err);
}
}
async function saveAppForms(res) {
if (res.length > 0) {
try {
const db = await mongoose.createConnection(targetConString, options).asPromise();
const w_formV3model = db.model('w_formV3', w_formV3Schema);
const src_res = await w_formV3model.collection.insertMany(res);
console.log('app forms inserted');
} catch (err) {
console.log('err in save app forms', err);
}
}
await getAppReports();
}
async function getAppReports() {
try {
var appIdpointer = 'w_appinfo$' + appId;
const db = await mongoose.createConnection(sourceConString, options).asPromise();
const w_reportmodel = db.model('w_Filter', w_reportSchema);
const reportsRes = await w_reportmodel.find({ _p_appId: appIdpointer });
await saveAppReports(reportsRes);
} catch (err) {
console.log('err is get app reports', err);
}
}
async function saveAppReports(res) {
if (res.length > 0) {
try {
const db = await mongoose.createConnection(targetConString, options).asPromise();
const w_reportmodel = db.model('w_Filter', w_reportSchema);
const src_res = await w_reportmodel.collection.insertMany(res);
console.log('app reports inserted');
} catch (err) {
console.log('err in save app reports', err);
}
}
await getAppMenu();
}
async function getAppMenu() {
try {
var appIdpointer = 'w_appinfo$' + appId;
const db = await mongoose.createConnection(sourceConString, options).asPromise();
const w_menumodel = db.model('w_menu', w_menuSchema);
const menuRes = await w_menumodel.find({ _p_appId: appIdpointer });
await saveAppMenu(menuRes);
} catch (err) {
console.log('err is get app menus', err);
}
}
async function saveAppMenu(res) {
if (res.length > 0) {
try {
const db = await mongoose.createConnection(targetConString, options).asPromise();
const w_menumodel = db.model('w_menu', w_menuSchema);
const src_res = await w_menumodel.collection.insertMany(res);
console.log('app menus inserted');
} catch (err) {
console.log('err in save app menus', err);
}
}
await saveAppRole();
}
async function saveAppRole(res) {
const date = new Date();
const appRole = appname + 'appeditor';
const role = [
{
_id: appRoleId,
name: appRole,
_wperm: [`${UserId}`],
_rperm: ['*', `${UserId}`],
_acl: { UserId: { w: true, r: true }, '*': { r: true } },
_created_at: date,
_updated_at: date,
},
...defaultRoles,
];
try {
const db = await mongoose.createConnection(targetConString, options).asPromise();
Trg__RoleModel = db.model('_Role', _RoleSchema);
const src_res = await Trg__RoleModel.collection.insertMany(role);
console.log('app editor role inserted');
await saveJoinAppRole();
} catch (err) {
console.log('err in save app editor role', err);
}
}
async function saveJoinAppRole() {
const OrgRole = organization + '_org';
let OrgRoleId;
try {
const db = await mongoose.createConnection(targetConString, options).asPromise();
// const _RoleModel = db.model('_Role', _RoleSchema);
const src_res = await Trg__RoleModel.find({ name: OrgRole });
// console.log('src_res ', src_res);
OrgRoleId = src_res[0]._id;
} catch (err) {
console.log('err in get org Role Id', err);
}
const userJoinRole = [{ owningId: appRoleId, relatedId: UserId }];
try {
const db = await mongoose.createConnection(targetConString, options).asPromise();
const _UserJoinRolemodel = db.model('_Join:users:_Role', _UserjoinSchema);
const userJoinRes = await _UserJoinRolemodel.collection.insertMany(userJoinRole);
console.log('User Join role inserted');
} catch (err) {
console.log('err in User Join role', err);
}
const roleJoinRole = [{ owningId: OrgRoleId, relatedId: appRoleId }];
try {
const db = await mongoose.createConnection(targetConString, options).asPromise();
const _RoleJoinRolemodel = db.model('_Join:roles:_Role', _RolejoinSchema);
const roleJoinRes = await _RoleJoinRolemodel.collection.insertMany(roleJoinRole);
console.log('Role Join role inserted');
await getAppSchemas();
} catch (err) {
console.log('err in Role Join role', err);
}
}
async function getAppSchemas() {
console.log('getAppSchemas');
try {
const db = await mongoose.createConnection(sourceConString, options).asPromise();
Src_SCHEMASmodel = db.model('_SCHEMA', _SCHEMASchema);
const schemaRes = await Src_SCHEMASmodel.find({
_id: { $regex: '.*' + appname + '.*' },
});
await saveAppSchemas(schemaRes);
} catch (err) {
console.log('err in get app schema', err);
}
}
async function saveAppSchemas(res) {
if (res.length > 0) {
console.log('saveAppSchemas');
try {
const db = await mongoose.createConnection(targetConString, options).asPromise();
Trg_SCHEMASmodel = db.model('_SCHEMA', _SCHEMASchema);
const src_res = await Trg_SCHEMASmodel.collection.insertMany(res);
console.log('app schema inserted');
msg = 'app installed successfully';
status = 200;
} catch (err) {
console.log('err in save app schemas', err);
}
}
msg = 'app installed successfully';
status = 200;
}
const message = { message: msg, status: status };
return message;
} catch (err) {
console.log('Exeption in query ', err);
console.log(err);
return 'Error:Exeption in query';
}
}
} catch (err) {
return Promise.reject('Invalid session token!');
}
}
@@ -0,0 +1,69 @@
async function sendMailOTPv1(request) {
try {
//--for elearning app side
let code = Math.floor(1000 + Math.random() * 9000);
let getMessage = code + ' This is your verification code';
let email = request.params.email;
var Hashcode;
var TenantId = request.params.TenantId ? request.params.TenantId : undefined;
// console.log("In tempSendOTPv2");
console.log(JSON.stringify(request));
var encoded = encodeURIComponent(Hashcode);
var otp = '<%23> You OTP is:' + code + ' ' + encoded + ' -QikEln';
otp = otp.toString();
if (email) {
axios({
method: 'POST',
url: serverUrl + '/functions/sendmail',
headers: {
'Content-Type': 'application/json',
'X-Parse-Application-Id': process.env.APP_ID,
},
params: {
otp: code,
email: email,
TenantId: TenantId,
},
}).then(
function (httpResponse) {},
function (httpResponse) {
console.error('sms Request failed with response code ' + httpResponse.status);
return Promise.reject('sms Request failed with response code ' + httpResponse.status);
}
);
const tempOtp = new Parse.Query('defaultdata_Otp');
tempOtp.equalTo('Email', email);
const resultOTP = await tempOtp.first({ useMasterKey: true });
console.log('resultOTP', resultOTP);
if (resultOTP !== undefined) {
const updateOtpQuery = new Parse.Query('defaultdata_Otp');
const updateOtp = await updateOtpQuery.get(resultOTP.id, {
useMasterKey: true,
});
updateOtp.set('OTP', code);
const updateRes = updateOtp.save(null, { useMasterKey: true });
// console.log("update otp Res in tempSendOtp ", updateRes);
} else {
const otpClass = Parse.Object.extend('defaultdata_Otp');
const newOtpQuery = new otpClass();
newOtpQuery.set('OTP', code);
newOtpQuery.set('Email', email);
newOtpQuery.set('TenantId', TenantId);
const newRes = await newOtpQuery.save(null, { useMasterKey: true });
// console.log("new otp Res in tempSendOtp ", newRes);
}
return 'Otp send';
} else {
return Promise.reject('Please Enter valid email');
}
} catch (err) {
console.log('err in sendMailOTPv1');
console.log(err);
return Promise.reject(err);
}
}
export default sendMailOTPv1;
@@ -0,0 +1,27 @@
async function SendMailv1(request) {
console.log('in SendMailv1');
try {
const recipient = request.params.email;
const otp = request.params.otp;
const res = await Parse.Cloud.sendEmail({
from: 'Test user' + ' <' + process.env.MAILGUN_SENDER + '>',
recipient: recipient,
subject: 'your otp',
text: 'This email is a test.',
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',>OTP Verification</p></div><div style='padding:20px'><p style='font-family:system-ui;font-size:14px'>Your OTP for LegaDaft verification .</p><p style=' text-decoration: none; font-weight: bolder; color:blue;font-size:45px;margin:20px'>" +
otp +
'</p></div> </div> </div></body></html>',
// "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body style='text-align: center;'><div style='display:flex;flex-direction:column;justify-content:center;align-item:center;margin:40px'> <p style='font-weight: bolder; font-size: large;'>Hello,</p> <span>Your OTP for LegaDaft verification . </span> <p style=' text-decoration: none; font-weight: bolder; color:blue;font-size:45px'>76984</p><span>Thank You!</span> </div> </body></html>"
});
console.log('Res');
console.log(res);
return otp;
} catch (err) {
console.log('err in SendMailv1');
console.log(err);
return err;
}
}
export default SendMailv1;
@@ -0,0 +1,112 @@
/* --Description :cloud function called to add mater key in update query */
//-- Export Modules
import dotenv from 'dotenv';
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");
}
var response = {};
var rolelist = {};
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",
headers: {
"X-Parse-Application-Id": process.env.APP_ID,
"X-Parse-Session-Token": request.headers["sessiontoken"],
},
};
axios(options)
.then((x) => {
const body = x.data;
var error = body == "" ? true : false;
if (error) {
reject("result not found!");
} else {
resolve(body);
}
})
.catch((err) => {
if (err) {
console.error(err);
return;
}
});
});
}
var userData = await getuserid(request);
var userid = userData.objectId;
console.log("userid " + userid);
var url =
process.env.SERVER_URL +
'/roles?where={"users":{"__type":"Pointer","className":"_User","objectId":"' +
userid +
'"},"name": {"$regex": "' +
appname +
'"}}';
//-- check user role
function getRoleList(userid) {
return new Promise(function (resolve, reject) {
const options = {
url: url,
method: "get",
headers: {
"X-Parse-Application-Id": process.env.APP_ID,
},
};
axios(options)
.then((x) => {
const body = x.data;
var roleres = [];
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;
if (error) {
reject("result not found!");
} else {
resolve(roleres);
}
})
.catch((err) => {
if (err) {
console.error(err);
return;
}
});
});
}
rolelist = await getRoleList(request);
console.log(rolelist);
//--check user roles according to appId
var rolesInapp = [];
for (let i = 0; i < rolelist.length; i++) {
var str = JSON.stringify(rolelist[i]);
var result = str.includes(appname);
if (result == true) {
rolesInapp.push(rolelist[i]);
}
}
console.log(rolesInapp);
return rolesInapp;
} catch (err) {
console.log("err in usergroup");
console.log(err);
return Promise.reject("Error:Result not found");
}
}
@@ -0,0 +1,61 @@
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://billing.zoho.in/api/v1/hostedpages/' + hostedpages, {
headers: {
Authorization: 'Zoho-oauthtoken ' + res.data.access_token,
'X-com-zoho-billing-organizationid': process.env.ZOHO_BILLING_ORG_ID,
},
});
// console.log(
// "userData.data.data.subscription.contactpersons ",
// userData.data.data.subscription.contactpersons
// );
// console.log("userData.data.expiring_time ", userData.data.expiring_time);
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 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.expiring_time,
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,
};
return resData;
}
}
@@ -0,0 +1,330 @@
import SignPDF from './SignPDF.cjs';
import fs from 'node:fs';
import axios from 'axios';
import FormData from 'form-data';
import plainplaceholder from './customSignPdf/plainplaceholder.js';
import { plainAddPlaceholder } from 'node-signpdf/dist/helpers/index.js';
const serverUrl = process.env.SERVER_URL;
const APPID = process.env.APP_ID;
const masterKEY = process.env.MASTER_KEY;
// `uploadFile` is used to upload signed pdf on aws s3 and get file url
async function uploadFile(pdfFile) {
try {
const formData = new FormData();
formData.append('file', fs.createReadStream(pdfFile));
const headers = {
'content-type': 'multipart/form-data',
'X-Parse-Application-Id': process.env.APP_ID,
};
const serverURL = process.env.SERVER_URL;
// const split = serverURL.split('/');
// const desiredUrl = serverURL.includes('/app')
// ? serverURL.replace('/app', '')
// : serverURL.replace('/' + split[3], '');
const desiredUrl = serverURL.slice(0, -4);
const url = desiredUrl + '/file_upload'; //process.env.SERVER_URL
const res = await axios.post(url, formData, { headers: headers });
// console.log("res ", res.data);
return res.data;
} catch (err) {
console.log('err ', err);
// `fs.unlinkSync` is used to remove exported signed pdf file from exports folder
fs.unlinkSync(pdfFile);
}
}
async function updateDoc(docId, url, userId, ipAddress, data, className) {
try {
const UserPtr = {
__type: 'Pointer',
className: className,
objectId: userId,
};
const obj = {
UserPtr: UserPtr,
SignedUrl: url,
Activity: 'Signed',
ipAddress: ipAddress,
};
let updateAuditTrail;
if (data.AuditTrail && data.AuditTrail.length > 0) {
updateAuditTrail = [...data.AuditTrail, obj];
} 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.Signers.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' };
} catch (err) {
console.log('update doc err ', err);
return 'err';
}
}
// `sendMail` is used to send copy signed mail
async function sendMail(obj) {
const url = obj.url;
const sender = obj.sender;
const pdfName = obj.pdfName;
const mailLogo = 'https://qikinnovation.ams3.digitaloceanspaces.com/logo.png';
const recipient = obj.receiver;
const subject = `${sender.Name} has signed the doc - ${pdfName}`;
const params = {
url: url,
from: 'Open sign',
recipient: recipient,
subject: subject,
pdfName: pdfName,
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><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 Copy</p></div><div><p style='padding:20px;font-family:system-ui;font-size:14px'>A copy of the document " +
pdfName +
' Standard is attached to this email. Kindly download the document from the attachment.</p></div> </div><div><p>This is an automated email from Open Sign. For any queries regarding this email, please contact the sender ' +
sender.Mail +
' directly. If you think this email is inappropriate or spam, you may file a complaint with Open Sign here.</p></div></div></body></html>',
};
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);
}
// `sendMail` is used to send copy signed mail
async function sendCompletedMail(obj) {
const url = obj.url;
const sender = obj.sender;
const pdfName = obj.pdfName;
const mailLogo = 'https://qikinnovation.ams3.digitaloceanspaces.com/logo.png';
const recipient = obj.receiver;
const subject = `Document ${pdfName} has beeen signed by all parties`;
const params = {
url: url,
from: 'Open sign',
recipient: recipient,
subject: subject,
pdfName: pdfName,
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><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 sign successfully</p></div><div><p style='padding:20px;font-family:system-ui;font-size:14px'>All parties have successfully signed the document" +
pdfName +
'. Kindly download the document from the attachment.</p></div> </div><div><p>This is an automated email from Open Sign. For any queries regarding this email, please contact the sender ' +
sender.Mail +
' directly. If you think this email is inappropriate or spam, you may file a complaint with Open Sign here.</p></div></div></body></html>',
};
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);
}
/**
*
* @param sign base64 sign of user
* @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, res) {
try {
const sign = req.params.sign;
const data = '?include=ExtUserPtr';
const docId = req.params.docId;
// below bode is used to get info of docId
const resDoc = await axios.get(serverUrl + '/classes/contracts_Document/' + docId + data, {
headers: {
'Content-Type': 'application/json',
'X-Parse-Application-Id': APPID,
'X-Parse-Session-Token': req.headers['sessiontoken'],
},
});
// to get user from session token
const user = await axios.get(serverUrl + '/users/me', {
headers: {
'X-Parse-Application-Id': APPID,
'X-Parse-Session-Token': req.headers['sessiontoken'],
},
});
if (user.data && user.data.objectId) {
const userPtr = JSON.stringify({
UserId: {
__type: 'Pointer',
className: '_User',
objectId: user.data.objectId,
},
});
let signUser;
let className;
// to get contracts_Users Id from _User ptr
const contractUser = await axios.get(
serverUrl + '/classes/contracts_Users?where=' + userPtr,
{
headers: {
'X-Parse-Application-Id': APPID,
'X-Parse-Session-Token': req.headers['sessiontoken'],
},
}
);
if (contractUser.data && contractUser.data.results.length > 0) {
signUser = contractUser;
className = 'contracts_Users';
} else {
// to get contracts_Contactbook Id from _User ptr
signUser = await axios.get(serverUrl + '/classes/contracts_Contactbook?where=' + userPtr, {
headers: {
'X-Parse-Application-Id': APPID,
'X-Parse-Session-Token': req.headers['sessiontoken'],
},
});
className = 'contracts_Contactbook';
}
// console.log("signUser ", signUser.data.results[0].objectId);
// console.log("resDoc ", resDoc);
const username = signUser.data.results[0].Name; // resDoc.data.ExtUserPtr.Name;
const userEmail = signUser.data.results[0].Email; //resDoc.data.ExtUserPtr.Email;
if (req.params.pdfFile) {
// `PdfBuffer` used to create buffer from pdf file
let PdfBuffer = Buffer.from(req.params.pdfFile, 'base64');
// let PdfBuffer = fs.readFileSync("exports/exported_file_688.pdf");
// let PdfBuffer = fs.readFileSync("exports/simple.pdf");
// `P12Buffer` used to create buffer from p12 certificate
const P12Buffer = fs.readFileSync(`pdfFile/emudhra-test-class2.pfx`);
if (sign) {
// `plainAddPlaceholder` is used to add code of digitial sign in pdf file
PdfBuffer = plainplaceholder({
pdfBuffer: PdfBuffer,
reason: 'Digitally signed by Open sign for ' + username + ' <' + userEmail + '>',
location: 'test location',
signatureLength: 10000,
sign: sign,
});
} else {
// `plainAddPlaceholder` is used to add code of only digitial sign without widget
PdfBuffer = plainAddPlaceholder({
pdfBuffer: PdfBuffer,
reason: 'Digitally signed by Open sign for ' + username + ' <' + userEmail + '>',
location: 'test location',
signatureLength: 10000,
});
}
// console.log("PdfBuffer ", PdfBuffer);
// const clientIP = req.headers["x-real-ip"];
// console.log("req.ip", clientIP);
//`new signPDF` create new instance of pdfBuffer and p12Buffer
const OBJ = new SignPDF(PdfBuffer, P12Buffer);
// `signedDocs` is used to signpdf digitally
const signedDocs = await OBJ.signPDF();
const randomNumber = Math.floor(Math.random() * 5000);
const pdfName = `./exports/exported_file_${randomNumber}.pdf`;
//`saveUrl` is used to save signed pdf in exports folder
const saveUrl = fs.writeFileSync(pdfName, signedDocs);
// `uploadFile` is used to upload pdf to aws s3 and get it's url
const data = await uploadFile(pdfName);
if (data && data.imageUrl) {
// `axios` is used to update signed pdf url in contracts_Document classes for given DocId
const res = await updateDoc(
req.params.docId, //docId
data.imageUrl, // url
signUser.data.results[0].objectId, // userID
req.headers['x-real-ip'], // client ipAddress,
resDoc.data, // auditTrail, signers, etc data
className
);
const obj = {
url: data.imageUrl,
sender: {
Mail: resDoc.data.ExtUserPtr.Email,
Name: resDoc.data.ExtUserPtr.Name,
},
pdfName: resDoc.data.Name,
receiver: userEmail,
};
sendMail(obj);
// console.log("res ", res);
if (res && res.isCompleted) {
// console.log("res.IsCompleted ", res.isCompleted);
const mailObj = {
url: data.imageUrl,
sender: {
Mail: resDoc.data.ExtUserPtr.Email,
Name: 'Open sign',
},
pdfName: resDoc.data.Name,
receiver: resDoc.data.ExtUserPtr.Email,
};
sendCompletedMail(mailObj);
}
// `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 (res.message === 'success') {
return { status: 'success', data: data.imageUrl };
} else {
return {
status: 'error',
message: 'please provide required parameters!',
};
}
}
} else {
return { status: 'error', message: 'pdf file not present!' };
}
} else {
return { status: 'error', message: 'this user not allowed!' };
}
} catch (err) {
console.log('Err ', err);
if (err.code === 'ERR_BAD_REQUEST') {
return {
status: 'error',
message: 'Invalid session token!',
};
} else {
return {
status: 'error',
message: 'Encrypted files are currently not supported!',
};
}
}
}
export default PDF;
@@ -0,0 +1,31 @@
const signer = require("node-signpdf").default;
class SignPDF {
constructor(pdfBuffer, certBuffer) {
this.pdfDoc = pdfBuffer;
this.certificate = certBuffer;
}
/**
* @return Promise<Buffer>
*/
async signPDF() {
let newPDF = signer.sign(this.pdfDoc, this.certificate, {
passphrase: "emudhra",
});
return newPDF;
}
/**
* @param {Uint8Array} unit8
*/
static unit8ToBuffer(unit8) {
let buf = Buffer.alloc(unit8.byteLength);
const view = new Uint8Array(unit8);
for (let i = 0; i < buf.length; ++i) {
buf[i] = view[i];
}
return buf;
}
}
module.exports = SignPDF;
@@ -0,0 +1,18 @@
/*
PDFAbstractReference by Devon Govett used below.
The class is part of pdfkit. See https://github.com/foliojs/pdfkit
LICENSE: MIT. Included in this folder.
Modifications may have been applied for the purposes of node-signpdf.
*/
/*
PDFAbstractReference - abstract class for PDF reference
*/
class PDFAbstractReference {
toString() {
throw new Error('Must be implemented by subclasses');
}
}
export default PDFAbstractReference;
@@ -0,0 +1,17 @@
import PDFAbstractReference from './PDFAbstractReference.js';
class PDFKitReferenceMock extends PDFAbstractReference {
constructor(index, additionalData = undefined) {
super();
this.index = index;
if (typeof additionalData !== 'undefined') {
Object.assign(this, additionalData);
}
}
toString() {
return `${this.index} 0 R`;
}
}
export default PDFKitReferenceMock;
@@ -0,0 +1,19 @@
export const ERROR_TYPE_UNKNOWN = 1;
export const ERROR_TYPE_INPUT = 2;
export const ERROR_TYPE_PARSE = 3;
export const ERROR_VERIFY_SIGNATURE = 4;
class SignPdfError extends Error {
constructor(msg, type = ERROR_TYPE_UNKNOWN) {
super(msg);
this.type = type;
}
}
// Shorthand
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 default SignPdfError;
@@ -0,0 +1,6 @@
export const DEFAULT_SIGNATURE_LENGTH = 8192;
export const DEFAULT_BYTE_RANGE_PLACEHOLDER = '**********';
export const SUBFILTER_ADOBE_PKCS7_DETACHED = 'adbe.pkcs7.detached';
export const SUBFILTER_ADOBE_PKCS7_SHA1 = 'adbe.pkcs7.sha1';
export const SUBFILTER_ADOBE_X509_SHA1 = 'adbe.x509.rsa.sha1';
export const SUBFILTER_ETSI_CADES_DETACHED = 'ETSI.CAdES.detached';
@@ -0,0 +1,40 @@
import findObject from './findObject.js';
import getIndexFromRef from './getIndexFromRef.js';
const createBufferPageWithAnnotation = (pdf, info, pagesRef, widget) => {
const pagesDictionary = findObject(pdf, info.xref, pagesRef).toString();
// Extend page dictionary with newly created annotations
let annotsStart; let annotsEnd; let
annots;
annotsStart = pagesDictionary.indexOf('/Annots');
if (annotsStart > -1) {
annotsEnd = pagesDictionary.indexOf(']', annotsStart);
annots = pagesDictionary.substr(annotsStart, annotsEnd + 1 - annotsStart);
annots = annots.substr(0, annots.length - 1); // remove the trailing ]
} else {
annotsStart = pagesDictionary.length;
annotsEnd = pagesDictionary.length;
annots = '/Annots [';
}
const pagesDictionaryIndex = getIndexFromRef(info.xref, pagesRef);
const widgetValue = widget.toString();
annots = `${annots} ${widgetValue}]`; // add the trailing ] back
const preAnnots = pagesDictionary.substr(0, annotsStart);
let postAnnots = '';
if (pagesDictionary.length > annotsEnd) {
postAnnots = pagesDictionary.substr(annotsEnd + 1);
}
return Buffer.concat([
Buffer.from(`${pagesDictionaryIndex} 0 obj\n`),
Buffer.from('<<\n'),
Buffer.from(`${preAnnots + annots + postAnnots}\n`),
Buffer.from('\n>>\nendobj\n'),
]);
};
export default createBufferPageWithAnnotation;
@@ -0,0 +1,15 @@
import getIndexFromRef from './getIndexFromRef.js';
const createBufferRootWithAcroform = (pdf, info, form) => {
const rootIndex = getIndexFromRef(info.xref, info.rootRef);
return Buffer.concat([
Buffer.from(`${rootIndex} 0 obj\n`),
Buffer.from('<<\n'),
Buffer.from(`${info.root}\n`),
Buffer.from(`/AcroForm ${form}`),
Buffer.from('\n>>\nendobj\n'),
]);
};
export default createBufferRootWithAcroform;
@@ -0,0 +1,28 @@
const createBufferTrailer = (pdf, info, addedReferences) => {
let rows = [];
rows[0] = '0000000000 65535 f '; // info.xref.tableRows[0];
addedReferences.forEach((offset, index) => {
const paddedOffset = (`0000000000${offset}`).slice(-10);
rows[index + 1] = `${index} 1\n${paddedOffset} 00000 n `;
});
rows = rows.filter((row) => row !== undefined);
return Buffer.concat([
Buffer.from('xref\n'),
Buffer.from(`${info.xref.startingIndex} 1\n`),
Buffer.from(rows.join('\n')),
Buffer.from('\ntrailer\n'),
Buffer.from('<<\n'),
Buffer.from(`/Size ${info.xref.maxIndex + 1}\n`),
Buffer.from(`/Root ${info.rootRef}\n`),
Buffer.from(info.infoRef ? `/Info ${info.infoRef}\n` : ''),
Buffer.from(`/Prev ${info.xRefPosition}\n`),
Buffer.from('>>\n'),
Buffer.from('startxref\n'),
Buffer.from(`${pdf.length}\n`),
Buffer.from('%%EOF'),
]);
};
export default createBufferTrailer;
@@ -0,0 +1,21 @@
import getIndexFromRef from './getIndexFromRef.js';
/**
* @param {Buffer} pdf
* @param {Map} refTable
* @returns {object}
*/
const findObject = (pdf, refTable, ref) => {
const index = getIndexFromRef(refTable, ref);
const offset = refTable.offsets.get(index);
let slice = pdf.slice(offset);
slice = slice.slice(0, slice.indexOf('endobj', 'utf8'));
// FIXME: What if it is a stream?
slice = slice.slice(slice.indexOf('<<', 'utf8') + 2);
slice = slice.slice(0, slice.lastIndexOf('>>', 'utf8'));
return slice;
};
export default findObject;
@@ -0,0 +1,21 @@
import SignPdfError from './SignPdfError.js';
/**
* @param {object} refTable
* @param {string} ref
* @returns {number}
*/
const getIndexFromRef = (refTable, ref) => {
let [index] = ref.split(' ');
index = parseInt(index);
if (!refTable.offsets.has(index)) {
throw new SignPdfError(
`Failed to locate object "${ref}".`,
SignPdfError.TYPE_PARSE,
);
}
return index;
};
export default getIndexFromRef;
@@ -0,0 +1,41 @@
import getPagesDictionaryRef from "./getPagesDictionaryRef.js";
import findObject from "./findObject.js";
/**
* Finds the reference to a page.
*
* @param {Buffer} pdfBuffer
* @param {Object} info As extracted from readRef()
* @param {Number} Page is pageNo on which we want widget/sign
*/
export default function getPageRef(pdfBuffer, info, Page) {
const pagesRef = getPagesDictionaryRef(info);
const pagesDictionary = findObject(pdfBuffer, info.xref, pagesRef);
const kidsPosition = pagesDictionary.indexOf("/Kids");
const kidsStart = pagesDictionary.indexOf("[", kidsPosition) + 1;
const kidsEnd = pagesDictionary.indexOf("]", kidsPosition);
const pages = pagesDictionary.slice(kidsStart, kidsEnd).toString();
// console.log("pages ", pages);
// const split = pages.trim().split(" ", 3);
// return `${split[0]} ${split[1]} ${split[2]}`;
// below code is used to get pageIndex (10 0 R) from pageNo to add sign widget on it
let indices = [];
for (var i = 0; i < pages.length; i++) {
if (pages[i] === "R") indices.push(i);
}
let pageIndex = {};
let startPosition = 0;
indices.forEach((x, index) => {
pageIndex = {
...pageIndex,
[index + 1]: pages.substring(startPosition + 1, x + 1),
};
startPosition = x + 1;
});
// console.log("pageIndex ", pageIndex);
const pageStr = Page ? pageIndex[Page] : pageIndex[1];
// console.log("pageStr ", pageStr);
return pageStr;
}
@@ -0,0 +1,18 @@
import SignPdfError from './SignPdfError.js';
/**
* @param {Object} info As extracted from readRef()
*/
export default function getPagesDictionaryRef(info) {
const pagesRefRegex = /\/Pages\s+(\d+\s+\d+\s+R)/g;
const match = pagesRefRegex.exec(info.root);
if (match === null) {
throw new SignPdfError(
'Failed to find the pages descriptor. This is probably a problem in node-signpdf.',
SignPdfError.TYPE_PARSE,
);
}
return match[1];
}
@@ -0,0 +1,131 @@
/*
PDFObject by Devon Govett used below.
The class is part of pdfkit. See https://github.com/foliojs/pdfkit
LICENSE: MIT. Included in this folder.
Modifications may have been applied for the purposes of node-signpdf.
*/
import PDFAbstractReference from './PDFAbstractReference.js';
/*
PDFObject - converts JavaScript types into their corresponding PDF types.
By Devon Govett
*/
const pad = (str, length) => (Array(length + 1).join('0') + str).slice(-length);
const escapableRe = /[\n\r\t\b\f()\\]/g;
const escapable = {
'\n': '\\n',
'\r': '\\r',
'\t': '\\t',
'\b': '\\b',
'\f': '\\f',
'\\': '\\\\',
'(': '\\(',
')': '\\)',
};
// Convert little endian UTF-16 to big endian
const swapBytes = (buff) => buff.swap16();
export default class PDFObject {
static convert(object, encryptFn = null) {
// String literals are converted to the PDF name type
if (typeof object === 'string') {
return `/${object}`;
// String objects are converted to PDF strings (UTF-16)
} if (object instanceof String) {
let string = object;
// Detect if this is a unicode string
let isUnicode = false;
for (let i = 0, end = string.length; i < end; i += 1) {
if (string.charCodeAt(i) > 0x7f) {
isUnicode = true;
break;
}
}
// If so, encode it as big endian UTF-16
let stringBuffer;
if (isUnicode) {
stringBuffer = swapBytes(Buffer.from(`\ufeff${string}`, 'utf16le'));
} else {
stringBuffer = Buffer.from(string, 'ascii');
}
// Encrypt the string when necessary
if (encryptFn) {
string = encryptFn(stringBuffer).toString('binary');
} else {
string = stringBuffer.toString('binary');
}
// Escape characters as required by the spec
string = string.replace(escapableRe, (c) => escapable[c]);
return `(${string})`;
// Buffers are converted to PDF hex strings
} if (Buffer.isBuffer(object)) {
return `<${object.toString('hex')}>`;
} if (object instanceof PDFAbstractReference) {
return object.toString();
} if (object instanceof Date) {
let string = `D:${pad(object.getUTCFullYear(), 4)}${pad(object.getUTCMonth() + 1, 2)}${pad(object.getUTCDate(), 2)}${pad(object.getUTCHours(), 2)}${pad(object.getUTCMinutes(), 2)}${pad(object.getUTCSeconds(), 2)}Z`;
// Encrypt the string when necessary
if (encryptFn) {
string = encryptFn(Buffer.from(string, 'ascii')).toString('binary');
// Escape characters as required by the spec
string = string.replace(escapableRe, (c) => escapable[c]);
}
return `(${string})`;
} if (Array.isArray(object)) {
const items = object.map((e) => PDFObject.convert(e, encryptFn)).join(' ');
return `[${items}]`;
}
if ({}.toString.call(object) === '[object Object]') {
const out = ['<<'];
let streamData;
// @todo this can probably be refactored into a reduce
Object.entries(object).forEach(([key, val]) => {
let checkedValue = '';
if (val && val.toString().indexOf('<<') !== -1) {
checkedValue = val;
} else {
checkedValue = PDFObject.convert(val, encryptFn);
}
// // if (key === 'stream') {
// // streamData = `${key}\n${val}\nendstream`;
// // }
// else {
out.push(`/${key} ${checkedValue}`);
// }
});
out.push('>>');
// if (streamData) {
// out.push(streamData);
// }
return out.join('\n');
}
if (typeof object === 'number') {
return PDFObject.number(object);
}
return `${object}`;
}
static number(n) {
if (n > -1e21 && n < 1e21) {
return Math.round(n * 1e6) / 1e6;
}
throw new Error(`unsupported number: ${n}`);
}
}
@@ -0,0 +1,449 @@
import {
DEFAULT_BYTE_RANGE_PLACEHOLDER,
DEFAULT_SIGNATURE_LENGTH,
SUBFILTER_ADOBE_PKCS7_DETACHED,
} from "./const.js";
// eslint-disable-next-line import/no-unresolved
import PDFKitReferenceMock from "./PDFKitReferenceMock.js";
/**
* Adds the objects that are needed for Adobe.PPKLite to read the signature.
* Also includes a placeholder for the actual signature.
* Returns an Object with all the added PDFReferences.
* @param {PDFDocument} pdf
* @param {string} reason
* @returns {object}
*/
// testing
import PNG from "png-js";
import zlib from "zlib";
import fs from "node:fs";
const specialCharacters = [
"á",
"Á",
"é",
"É",
"í",
"Í",
"ó",
"Ó",
"ö",
"Ö",
"ő",
"Ő",
"ú",
"Ú",
"ű",
"Ű",
];
const MARKERS = [
0xffc0, 0xffc1, 0xffc2, 0xffc3, 0xffc5, 0xffc6, 0xffc7, 0xffc8, 0xffc9,
0xffca, 0xffcb, 0xffcc, 0xffcd, 0xffce, 0xffcf,
];
const COLOR_SPACE_MAP = {
1: "DeviceGray",
3: "DeviceRGB",
4: "DeviceCMYK",
};
// testing
function getImage(imagePath, pdf) {
let img;
const data = imagePath; //fs.readFileSync(imagePath);
if (data[0] === 0xff && data[1] === 0xd8) {
img = getJpgImage(pdf, data);
} else if (data[0] === 0x89 && data.toString("ascii", 1, 4) === "PNG") {
img = getPngImage(pdf, data);
} else {
throw new Error("Unknown image format.");
}
return img;
}
function getAnnotationApparance(pdf, IMG, APFONT) {
return pdf.ref(
{
CropBox: [0, 0, 197, 70],
Type: "XObject",
FormType: 1,
BBox: [0, 0, 197.0, 70.0], //[-10, 10, 197.0, 70.0],
Resources: `<</XObject <<\n/Img${IMG.index} ${IMG.index} 0 R\n>>\n/Font <<\n/f1 ${APFONT.index} 0 R\n>>\n>>`,
MediaBox: [0, 0, 197, 70],
Subtype: "Form",
},
null,
getStream(IMG.index)
);
}
const getStream = (imgIndex) => {
// (Aláírta: ${userInformation.commonName}) Tj
// (Aláírta: testing name) Tj
// (${new Date().toISOString().slice(0, 10)}) Tj
return 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${imgIndex} 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`);
};
const getFont = (pdf, baseFont) => {
return pdf.ref({
Type: "Font",
BaseFont: baseFont,
Encoding: "WinAnsiEncoding",
Subtype: "Type1",
});
};
const getJpgImage = (pdf, data) => {
if (data.readUInt16BE(0) !== 0xffd8) {
throw "SOI not found in JPEG";
}
let pos = 2;
let marker;
while (pos < data.length) {
marker = data.readUInt16BE(pos);
pos += 2;
if (MARKERS.includes(marker)) {
break;
}
pos += data.readUInt16BE(pos);
}
if (!MARKERS.includes(marker)) {
throw "Invalid JPEG.";
}
pos += 2;
const bits = data[pos++];
const height = data.readUInt16BE(pos);
pos += 2;
const width = data.readUInt16BE(pos);
pos += 2;
const channels = data[pos++];
const colorSpace = COLOR_SPACE_MAP[channels];
const baseJpgData = {
Type: "XObject",
Subtype: "Image",
BitsPerComponent: bits,
Width: width,
Height: height,
ColorSpace: colorSpace,
Filter: "DCTDecode",
};
if (colorSpace === "DeviceCMYK") {
baseJpgData["Decode"] = [1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0];
}
const image = pdf.ref(baseJpgData, null, data);
return image;
};
const getPngImage = (pdf, data) => {
const image = new PNG(data);
const hasAlphaChannel = image.hasAlphaChannel;
const pngBaseData = {
Type: "XObject",
Subtype: "Image",
BitsPerComponent: hasAlphaChannel ? 8 : image.bits,
Width: image.width,
Height: image.height,
Filter: "FlateDecode",
};
if (!hasAlphaChannel) {
const params = pdf.ref({
Predictor: 15,
Colors: image.colors,
BitsPerComponent: image.bits,
Columns: image.width,
});
pngBaseData["DecodeParms"] = params;
}
if (image.palette.length === 0) {
pngBaseData["ColorSpace"] = image.colorSpace;
} else {
const palette = pdf.ref({
stream: new Buffer(image.palette),
});
pngBaseData["ColorSpace"] = [
"Indexed",
"DeviceRGB",
image.palette.length / 3 - 1,
palette,
];
}
if (image.transparency.grayscale != null) {
const val = image.transparency.grayscale;
pngBaseData["Mask"] = [val, val];
} else if (image.transparency.rgb) {
const { rgb } = image.transparency;
const mask = [];
for (let x of rgb) {
mask.push(x, x);
}
pngBaseData["Mask"] = mask;
} else if (image.transparency.indexed) {
loadIndexedAlphaChannel(image);
} else if (hasAlphaChannel) {
splitAlphaChannel(image);
const sMask = getSmask(pdf, image);
pngBaseData["Mask"] = sMask;
}
const pngImage = pdf.ref(pngBaseData, null, image.imgData);
return pngImage;
};
const loadIndexedAlphaChannel = (image) => {
const transparency = image.transparency.indexed;
return image.decodePixels((pixels) => {
const alphaChannel = new Buffer(image.width * image.height);
let i = 0;
for (let j = 0, end = pixels.length; j < end; j++) {
alphaChannel[i++] = transparency[pixels[j]];
}
image.alphaChannel = zlib.deflateSync(alphaChannel);
});
};
const splitAlphaChannel = (image) => {
image.decodePixels((pixels) => {
let a, p;
const colorCount = image.colors;
const pixelCount = image.width * image.height;
const imgData = new Buffer(pixelCount * colorCount);
const alphaChannel = new Buffer(pixelCount);
let i = (p = a = 0);
const len = pixels.length;
const skipByteCount = image.bits === 16 ? 1 : 0;
while (i < len) {
for (let colorIndex = 0; colorIndex < colorCount; colorIndex++) {
imgData[p++] = pixels[i++];
i += skipByteCount;
}
alphaChannel[a++] = pixels[i++];
i += skipByteCount;
}
image.imgData = zlib.deflateSync(imgData);
image.alphaChannel = zlib.deflateSync(alphaChannel);
});
};
const getSmask = (pdf, image) => {
let sMask;
console.log("image.hasAlphaChannel ", image.hasAlphaChannel);
if (image.hasAlphaChannel) {
console.log("image.alphaChannel ", image.alphaChannel);
sMask = pdf.ref({
Type: "XObject",
Subtype: "Image",
Height: image.height,
Width: image.width,
BitsPerComponent: 8,
Filter: "FlateDecode",
ColorSpace: "DeviceGray",
Decode: [0, 1],
stream: image.alphaChannel,
});
}
return sMask;
};
const getConvertedText = (text) => {
return text
.split("")
.map((character) => {
return specialCharacters.includes(character)
? getOctalCodeFromCharacter(character)
: character;
})
.join("");
};
const getOctalCodeFromCharacter = (character) => {
return "\\" + character.charCodeAt(0).toString(8);
};
//
const pdfkitAddPlaceholder = ({
pdf,
pdfBuffer,
reason,
contactInfo = "emailfromp1289@gmail.com",
name = "Name from p12",
location = "Location from p12",
signatureLength = DEFAULT_SIGNATURE_LENGTH,
byteRangePlaceholder = DEFAULT_BYTE_RANGE_PLACEHOLDER,
subFilter = SUBFILTER_ADOBE_PKCS7_DETACHED,
sign,
}) => {
// testing
const FONT = getFont(pdf, "Helvetica");
const ZAF = getFont(pdf, "ZapfDingbats");
const APFONT = getFont(pdf, "Helvetica");
// const imagePath = "exports/img.jpg";
const signBase64 = sign.Base64;
const imagePath = Buffer.from(signBase64, "base64");
const IMG = getImage(imagePath, pdf);
const AP = getAnnotationApparance(pdf, IMG, APFONT);
//
/* eslint-disable no-underscore-dangle,no-param-reassign */
// Generate the signature placeholder
const signature = pdf.ref({
Type: "Sig",
Filter: "Adobe.PPKLite",
SubFilter: subFilter,
ByteRange: [
0,
byteRangePlaceholder,
byteRangePlaceholder,
byteRangePlaceholder,
],
Contents: Buffer.from(String.fromCharCode(0).repeat(signatureLength)),
Reason: new String(reason), // eslint-disable-line no-new-wrappers
M: new Date(),
ContactInfo: new String(contactInfo), // eslint-disable-line no-new-wrappers
Name: new String(name), // eslint-disable-line no-new-wrappers
Location: new String(location), // eslint-disable-line no-new-wrappers
});
// Check if pdf already contains acroform field
const acroFormPosition = pdfBuffer.lastIndexOf("/Type /AcroForm");
const isAcroFormExists = acroFormPosition !== -1;
let fieldIds = [];
let acroFormId;
if (isAcroFormExists) {
let acroFormStart = acroFormPosition;
// 10 is the distance between "/Type /AcroForm" and AcroFrom ID
const charsUntilIdEnd = 10;
const acroFormIdEnd = acroFormPosition - charsUntilIdEnd;
// Let's find AcroForm ID by trying to find the "\n" before the ID
// 12 is a enough space to find the "\n"
// (generally it's 2 or 3, but I'm giving a big space though)
const maxAcroFormIdLength = 12;
let foundAcroFormId = "";
let index = charsUntilIdEnd + 1;
for (index; index < charsUntilIdEnd + maxAcroFormIdLength; index += 1) {
const acroFormIdString = pdfBuffer
.slice(acroFormPosition - index, acroFormIdEnd)
.toString();
if (acroFormIdString[0] === "\n") {
break;
}
foundAcroFormId = acroFormIdString;
acroFormStart = acroFormPosition - index;
}
const pdfSlice = pdfBuffer.slice(acroFormStart);
const acroForm = pdfSlice.slice(0, pdfSlice.indexOf("endobj")).toString();
acroFormId = parseInt(foundAcroFormId);
const acroFormFields = acroForm.slice(
acroForm.indexOf("/Fields [") + 9,
acroForm.indexOf("]")
);
fieldIds = acroFormFields
.split(" ")
.filter((element, i) => i % 3 === 0)
.map((fieldId) => new PDFKitReferenceMock(fieldId));
}
const signatureName = "Signature";
const signatureLeftOffset = fieldIds.length * 125;
const signatureBottomOffset = 5;
const left = sign.Left; //461.50001525878906 //477.3333282470703//409.5000305175781; // first block 44.33332824707031;
const bottom = sign.Bottom; // 730.1979598999023 - 39 + 5.072906494140625 //720.1979598999023 - 30 + 15.072906494140625; // first block 652.1979598999023 - 60 + 19.072906494140625;
const RightX = left + sign.Width; // 98;
const RightY = bottom + sign.Height; //39;
// Generate signature annotation widget
const widget = pdf.ref({
Type: "Annot",
Subtype: "Widget",
FT: "Sig",
Rect: [left, bottom, RightX, RightY], // [signatureLeftOffset, 0, signatureLeftOffset + 90, signatureBottomOffset + 60, ], // [50, 50, 100, 100], //[25, 25, 100, 300], //
V: signature,
T: new String(signatureName + (fieldIds.length + 1)), // eslint-disable-line no-new-wrappers
F: 4,
P: pdf.page.dictionary, // eslint-disable-line no-underscore-dangle
AP: `<</N ${AP.index} 0 R>>`, // testing
DA: new String("/Helvetica 0 Tf 0 g"), // eslint-disable-line no-new-wrappers // testing
});
pdf.page.dictionary.data.Annots = [widget];
// Include the widget in a page
let form;
if (!isAcroFormExists) {
// Create a form (with the widget) and link in the _root
form = pdf.ref({
Type: "AcroForm",
SigFlags: 3,
Fields: [...fieldIds, widget],
});
} else {
// Use existing acroform and extend the fields with newly created widgets
form = pdf.ref(
{
Type: "AcroForm",
SigFlags: 3,
Fields: [...fieldIds, widget],
DR: `<</Font\n<</Helvetica ${FONT.index} 0 R/ZapfDingbats ${ZAF.index} 0 R>>\n>>`, /// testing
},
acroFormId
);
}
pdf._root.data.AcroForm = form;
return {
signature,
form,
widget,
};
/* eslint-enable no-underscore-dangle,no-param-reassign */
};
export default pdfkitAddPlaceholder;
@@ -0,0 +1,138 @@
import PDFObject from "./pdfObject.js";
import PDFKitReferenceMock from "./PDFKitReferenceMock.js";
import removeTrailingNewLine from "./removeTrailingNewLine.js";
import {
DEFAULT_SIGNATURE_LENGTH,
SUBFILTER_ADOBE_PKCS7_DETACHED,
} from "./const.js";
import pdfkitAddPlaceholder from "./pdfkitAddPlaceholder.js";
import getIndexFromRef from "./getIndexFromRef.js";
import readPdf from "./readPdf.js";
import getPageRef from "./getPageRef.js";
import createBufferRootWithAcroform from "./createBufferRootWithAcroform.js";
import createBufferPageWithAnnotation from "./createBufferPageWithAnnotation.js";
import createBufferTrailer from "./createBufferTrailer.js";
const isContainBufferRootWithAcroform = (pdf) => {
const bufferRootWithAcroformRefRegex = /\/AcroForm\s+(\d+\s\d+\sR)/g;
const match = bufferRootWithAcroformRefRegex.exec(pdf.toString());
return match != null && match[1] != null && match[1] !== "";
};
// testing
const getAssembledPdf = (pdf, index, input, stream) => {
let finalPdf = pdf;
finalPdf = Buffer.concat([
finalPdf,
Buffer.from("\n"),
Buffer.from(`${index} 0 obj\n`),
Buffer.from(PDFObject.convert(input)),
]);
if (stream) {
finalPdf = Buffer.concat([
finalPdf,
Buffer.from("\nstream\n"),
Buffer.from(stream),
Buffer.from("\nendstream"),
]);
}
finalPdf = Buffer.concat([finalPdf, Buffer.from("\nendobj\n")]);
return finalPdf;
};
//
/**
* Adds a signature placeholder to a PDF Buffer.
*
* This contrasts with the default pdfkit-based implementation.
* Parsing is done using simple string operations.
* Adding is done with `Buffer.concat`.
* This allows node-signpdf to be used on any PDF and
* not only on a freshly created through PDFKit one.
*/
const plainAddPlaceholder = ({
pdfBuffer,
reason,
contactInfo = "emailfromp1289@gmail.com",
name = "Name from p12",
location = "Location from p12",
signatureLength = DEFAULT_SIGNATURE_LENGTH,
subFilter = SUBFILTER_ADOBE_PKCS7_DETACHED,
sign,
}) => {
let pdf = removeTrailingNewLine(pdfBuffer);
const info = readPdf(pdf);
const pageRef = getPageRef(pdf, info, sign.Page);
const pageIndex = getIndexFromRef(info.xref, pageRef);
const addedReferences = new Map();
const pdfKitMock = {
ref: (input, additionalIndex, stream) => {
info.xref.maxIndex += 1;
const index =
additionalIndex != null ? additionalIndex : info.xref.maxIndex;
addedReferences.set(index, pdf.length + 1); // + 1 new line
pdf = getAssembledPdf(pdf, index, input, stream);
return new PDFKitReferenceMock(info.xref.maxIndex);
},
page: {
dictionary: new PDFKitReferenceMock(pageIndex, {
data: {
Annots: [],
},
}),
},
_root: {
data: {},
},
};
const { form, widget } = pdfkitAddPlaceholder({
pdf: pdfKitMock,
pdfBuffer,
reason,
contactInfo,
name,
location,
signatureLength,
subFilter,
sign,
});
if (!isContainBufferRootWithAcroform(pdf)) {
const rootIndex = getIndexFromRef(info.xref, info.rootRef);
addedReferences.set(rootIndex, pdf.length + 1);
pdf = Buffer.concat([
pdf,
Buffer.from("\n"),
createBufferRootWithAcroform(pdf, info, form),
]);
}
addedReferences.set(pageIndex, pdf.length + 1);
pdf = Buffer.concat([
pdf,
Buffer.from("\n"),
createBufferPageWithAnnotation(pdf, info, pageRef, widget),
]);
pdf = Buffer.concat([
pdf,
Buffer.from("\n"),
createBufferTrailer(pdf, info, addedReferences),
]);
return pdf;
};
export default plainAddPlaceholder;
@@ -0,0 +1,54 @@
import readRefTable from './readRefTable.js';
import findObject from './findObject.js';
const getValue = (trailer, key) => {
let index = trailer.indexOf(key);
if (index === -1) {
return undefined;
}
const slice = trailer.slice(index);
index = slice.indexOf('/', 1);
if (index === -1) {
index = slice.indexOf('>', 1);
}
return slice.slice(key.length + 1, index).toString().trim(); // key + at least one space
};
/**
* Simplified parsing of a PDF Buffer.
* Extracts reference table, root info and trailer start.
*
* See section 7.5.5 (File Trailer) of the PDF specs.
*
* @param {Buffer} pdfBuffer
*/
const readPdf = (pdfBuffer) => {
// Extract the trailer dictionary.
const trailerStart = pdfBuffer.lastIndexOf('trailer');
// The trailer is followed by xref. Then an EOF. EOF's length is 6 characters.
const trailer = pdfBuffer.slice(trailerStart, pdfBuffer.length - 6);
let xRefPosition = trailer.slice(trailer.lastIndexOf('startxref') + 10).toString();
xRefPosition = parseInt(xRefPosition);
const refTable = readRefTable(pdfBuffer);
const rootRef = getValue(trailer, '/Root');
const root = findObject(pdfBuffer, refTable, rootRef).toString();
const infoRef = getValue(trailer, '/Info');
return {
xref: refTable,
rootRef,
root,
infoRef,
trailerStart,
previousXrefs: [],
xRefPosition,
};
};
export default readPdf;
@@ -0,0 +1,118 @@
import SignPdfError from './SignPdfError.js';
import xrefToRefMap from './xrefToRefMap.js';
export const getLastTrailerPosition = (pdf) => {
const trailerStart = pdf.lastIndexOf(Buffer.from('trailer', 'utf8'));
const trailer = pdf.slice(trailerStart, pdf.length - 6);
const xRefPosition = trailer
.slice(trailer.lastIndexOf(Buffer.from('startxref', 'utf8')) + 10)
.toString();
return parseInt(xRefPosition);
};
export const getXref = (pdf, position) => {
let refTable = pdf.slice(position); // slice starting from where xref starts
const realPosition = refTable.indexOf(Buffer.from('xref', 'utf8'));
if (realPosition === -1) {
throw new SignPdfError(
`Could not find xref anywhere at or after ${position}.`,
SignPdfError.TYPE_PARSE,
);
}
if (realPosition > 0) {
const prefix = refTable.slice(0, realPosition);
if (prefix.toString().replace(/\s*/g, '') !== '') {
throw new SignPdfError(
`Expected xref at ${position} but found other content.`,
SignPdfError.TYPE_PARSE,
);
}
}
const nextEofPosition = refTable.indexOf(Buffer.from('%%EOF', 'utf8'));
if (nextEofPosition === -1) {
throw new SignPdfError(
'Expected EOF after xref and trailer but could not find one.',
SignPdfError.TYPE_PARSE,
);
}
refTable = refTable.slice(0, nextEofPosition);
refTable = refTable.slice(realPosition + 4); // move ahead with the "xref"
refTable = refTable.slice(refTable.indexOf('\n') + 1); // move after the next new line
// extract the size
let size = refTable.toString().split('/Size')[1];
if (!size) {
throw new SignPdfError(
'Size not found in xref table.',
SignPdfError.TYPE_PARSE,
);
}
size = (/^\s*(\d+)/).exec(size);
if (size === null) {
throw new SignPdfError(
'Failed to parse size of xref table.',
SignPdfError.TYPE_PARSE,
);
}
size = parseInt(size[1]);
const [objects, infos] = refTable.toString().split('trailer');
const isContainingPrev = infos.split('/Prev')[1] != null;
let prev;
if (isContainingPrev) {
const pagesRefRegex = /Prev (\d+)/g;
const match = pagesRefRegex.exec(infos);
const [, prevPosition] = match;
prev = prevPosition;
}
const xRefContent = xrefToRefMap(objects);
return {
size,
prev,
xRefContent,
};
};
export const getFullXrefTable = (pdf) => {
const lastTrailerPosition = getLastTrailerPosition(pdf);
const lastXrefTable = getXref(pdf, lastTrailerPosition);
if (lastXrefTable.prev === undefined) {
return lastXrefTable.xRefContent;
}
const pdfWithoutLastTrailer = pdf.slice(0, lastTrailerPosition);
const partOfXrefTable = getFullXrefTable(pdfWithoutLastTrailer);
const mergedXrefTable = new Map([
...partOfXrefTable,
...lastXrefTable.xRefContent,
]);
return mergedXrefTable;
};
/**
* @param {Buffer} pdfBuffer
* @returns {object}
*/
const readRefTable = (pdf) => {
const fullXrefTable = getFullXrefTable(pdf);
const startingIndex = 0;
const maxIndex = Math.max(...fullXrefTable.keys());
return {
startingIndex,
maxIndex,
offsets: fullXrefTable,
};
};
export default readRefTable;
@@ -0,0 +1,42 @@
import SignPdfError from './SignPdfError.js';
const sliceLastChar = (pdf, character) => {
const lastChar = pdf.slice(pdf.length - 1).toString();
if (lastChar === character) {
return pdf.slice(0, pdf.length - 1);
}
return pdf;
};
/**
* Removes a trailing new line if there is such.
*
* Also makes sure the file ends with an EOF line as per spec.
* @param {Buffer} pdf
* @returns {Buffer}
*/
const removeTrailingNewLine = (pdf) => {
if (!(pdf instanceof Buffer)) {
throw new SignPdfError(
'PDF expected as Buffer.',
SignPdfError.TYPE_INPUT,
);
}
let output = pdf;
output = sliceLastChar(output, '\n');
output = sliceLastChar(output, '\r');
const lastLine = output.slice(output.length - 6).toString();
if (lastLine !== '\n%%EOF') {
throw new SignPdfError(
'A PDF file must end with an EOF line.',
SignPdfError.TYPE_PARSE,
);
}
return output;
};
export default removeTrailingNewLine;
@@ -0,0 +1,48 @@
import SignPdfError from './SignPdfError.js';
const xrefToRefMap = (xrefString) => {
const lines = xrefString.split('\n').filter((l) => l !== '');
let index = 0;
let expectedLines = 0;
const xref = new Map();
lines.forEach((line) => {
const split = line.split(' ');
if (split.length === 2) {
index = parseInt(split[0]);
expectedLines = parseInt(split[1]);
return;
}
if (expectedLines <= 0) {
throw new SignPdfError(
'Too many lines in xref table.',
SignPdfError.TYPE_PARSE,
);
}
expectedLines -= 1;
const [offset, , inUse] = split;
if (inUse.trim() === 'f') {
index += 1;
return;
}
if (inUse.trim() !== 'n') {
throw new SignPdfError(
`Unknown in-use flag "${inUse}". Expected "n" or "f".`,
SignPdfError.TYPE_PARSE,
);
}
if (!/^\d+$/.test(offset.trim())) {
throw new SignPdfError(
`Expected integer offset. Got "${offset}".`,
SignPdfError.TYPE_PARSE,
);
}
const storeOffset = parseInt(offset.trim());
xref.set(index, storeOffset);
index += 1;
});
return xref;
};
export default xrefToRefMap;
@@ -0,0 +1,91 @@
import fs from 'node:fs';
import formData from 'form-data';
import Mailgun from 'mailgun.js';
import https from 'https';
const mailgun = new Mailgun(formData);
const mailgunClient = mailgun.client({
username: 'api',
key: process.env.MAILGUN_API_KEY,
});
const mailgunDomain = process.env.MAILGUN_DOMAIN;
async function sendmail(req) {
try {
if (req.params.url) {
let Pdf = fs.createWriteStream('test.pdf');
const writeToLocalDisk = () => {
return new Promise((resolve, reject) => {
https.get(req.params.url, async function (response) {
response.pipe(Pdf);
response.on('end', () => resolve('success'));
});
});
};
// `writeToLocalDisk` is used to create pdf file from doc url
const ress = await writeToLocalDisk();
if (ress) {
function readTolocal() {
return new Promise((resolve, reject) => {
setTimeout(() => {
let PdfBuffer = fs.readFileSync(Pdf.path);
resolve(PdfBuffer);
}, 100);
});
}
// `PdfBuffer` used to create buffer from pdf file
let PdfBuffer = await readTolocal();
const pdfName = req.params.pdfName && `${req.params.pdfName}.pdf`;
const file = {
filename: pdfName || 'exported.pdf',
data: PdfBuffer, //fs.readFileSync('./exports/exported_file_1223.pdf'),
};
// const html = "<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body style='text-align: center;'> <p style='font-weight: bolder; font-size: large;'>Hello!</p> <p>This is a html checking mail</p><p><button style='background-color: lightskyblue; cursor: pointer; border-radius: 5px; padding: 10px; border-style: solid; border-width: 2px; text-decoration: none; font-weight: bolder; color:blue'>Verify email</button></p></body></html>"
const from = req.params.from || '';
const messageParams = {
from: from + ' <' + process.env.MAILGUN_SENDER + '>',
to: req.params.recipient,
subject: req.params.subject,
text: req.params.text || 'mail',
html: req.params.html || '',
attachment: file,
};
const res = await mailgunClient.messages.create(mailgunDomain, messageParams);
console.log('Res ', res);
if (res.status === 200) {
return {
status: 'success',
};
}
}
} else {
const from = req.params.from || '';
const messageParams = {
from: from + ' <' + process.env.MAILGUN_SENDER + '>',
to: req.params.recipient,
subject: req.params.subject,
text: req.params.text || 'mail',
html: req.params.html || '',
};
const res = await mailgunClient.messages.create(mailgunDomain, messageParams);
console.log('Res ', res);
if (res.status === 200) {
return {
status: 'success',
};
}
}
} catch (err) {
console.log('err ', err);
if (err) {
return { status: 'error' };
}
}
}
export default sendmail;
@@ -0,0 +1,140 @@
import axios from "axios";
const serverUrl = process.env.SERVER_URL;
const APPID = process.env.APP_ID;
const masterKEY = process.env.MASTER_KEY;
async function saveUser(userDetails) {
const userQuery = new Parse.Query(Parse.User);
userQuery.equalTo("username", userDetails.email);
const userRes = await userQuery.first({ useMasterKey: true });
if (userRes) {
const url = `${serverUrl}/loginAs`;
const axiosRes = await axios({
method: "POST",
url: url,
headers: {
"Content-Type": "application/json;charset=utf-8",
"X-Parse-Application-Id": APPID,
"X-Parse-Master-Key": masterKEY,
},
params: {
userId: userRes.id,
},
});
const login = await axiosRes.data;
// console.log("login ", login);
return { id: login.objectId, sessionToken: login.sessionToken };
} else {
const user = new Parse.User();
user.set("username", userDetails.email);
user.set("password", userDetails.password);
user.set("email", userDetails.email);
user.set("phone", userDetails.phone);
user.set("name", userDetails.name);
const res = await user.signUp();
// console.log("res ", res);
return { id: res.id, sessionToken: res.getSessionToken() };
}
}
export default async function (request) {
const userDetails = request.params.userDetails;
const planDetails = request.params.planDetails;
const user = await saveUser(userDetails);
try {
const extClass = userDetails.role.split("_")[0];
const extQuery = new Parse.Query(extClass + "_Users");
extQuery.equalTo("UserId", {
__type: "Pointer",
className: "_User",
objectId: user.id,
});
const extUser = await extQuery.first({ useMasterKey: true });
if (extUser) {
return { message: "User already exist" };
} else {
const roleurl = `${serverUrl}/functions/AddUserToRole`;
const headers = {
"Content-Type": "application/json",
"X-Parse-Application-Id": APPID,
sessionToken: user.sessionToken, //localStorage.getItem("accesstoken"),
};
let body = {
appName: extClass, //props.appInfo.appname,
roleName: userDetails.role, //props.appInfo.defaultRole,
userId: user.id,
};
let role = await axios.post(roleurl, body, { headers: headers });
// console.log("role ", role);
// props.appInfo.defaultRole
const partnerCls = Parse.Object.extend("partners_Tenant");
const partnerQuery = new partnerCls();
partnerQuery.set("UserId", {
__type: "Pointer",
className: "_User",
objectId: user.id,
});
partnerQuery.set("ContactNumber", userDetails.phone);
partnerQuery.set("TenantName", userDetails.name);
partnerQuery.set("EmailAddress", userDetails.email);
partnerQuery.set("IsActive", true);
partnerQuery.set("CreatedBy", {
__type: "Pointer",
className: "_User",
objectId: user.id,
});
if (userDetails && userDetails.pincode) {
partnerQuery.set("PinCode", userDetails.pincode);
}
if (userDetails && userDetails.country) {
partnerQuery.set("Country", userDetails.country);
}
if (userDetails && userDetails.state) {
partnerQuery.set("State", userDetails.state);
}
if (userDetails && userDetails.city) {
partnerQuery.set("City", userDetails.city);
}
if (userDetails && userDetails.address) {
partnerQuery.set("Address", userDetails.address);
}
const tenantRes = await partnerQuery.save(null, { useMasterKey: true });
// console.log("tenantRes ", tenantRes);
const extCls = Parse.Object.extend(extClass + "_Users");
const newObj = new extCls();
newObj.set("UserId", {
__type: "Pointer",
className: "_User",
objectId: user.id,
});
newObj.set("UserRole", userDetails.role); // props.appInfo.defaultRole
newObj.set("Email", userDetails.email);
newObj.set("Name", userDetails.name);
newObj.set("Phone", userDetails.phone);
newObj.set("TenantId", {
__type: "Pointer",
className: "partners_Tenant",
objectId: tenantRes.id,
});
if (userDetails && userDetails.company) {
newObj.set("Company", userDetails.company);
}
if (planDetails && planDetails.customer_id) {
newObj.set("Next_billing_date", new Date(planDetails.nextBillingDate));
newObj.set("Plan", planDetails.plan);
newObj.set("Customer_id", planDetails.customer_id);
newObj.set("Subscription_id", planDetails.subscription_id);
}
const extRes = await newObj.save(null, { useMasterKey: true });
return { message: "User sign up", sessionToken: user.sessionToken };
}
} catch (err) {
console.log("Err ", err);
}
}