mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-08-22 23:52:30 +02:00
initial commit
This commit is contained in:
+262
@@ -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';
|
||||
+40
@@ -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;
|
||||
+15
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user