mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-08-20 06:35:54 +02:00
feat: enable custom file storage for document siging
This commit is contained in:
@@ -9,14 +9,23 @@ async function DocumentAfterFind(request) {
|
||||
const SignedUrl = obj?.get('SignedUrl') && obj?.get('SignedUrl');
|
||||
const Url = obj?.get('URL') && obj?.get('URL');
|
||||
const certificateUrl = obj.get('CertificateUrl') && obj.get('CertificateUrl');
|
||||
const IsFileAdapter = obj?.get('IsFileAdapter') || false;
|
||||
let fileAdapter = {};
|
||||
if (IsFileAdapter) {
|
||||
const tenantId = obj?.get('ExtUserPtr')?.get('TenantId');
|
||||
if (tenantId) {
|
||||
const _tenantId = JSON.parse(JSON.stringify(obj?.get('ExtUserPtr')?.get('TenantId')));
|
||||
fileAdapter = _tenantId?.FileAdapter || {};
|
||||
}
|
||||
}
|
||||
if (SignedUrl) {
|
||||
obj.set('SignedUrl', getPresignedUrl(SignedUrl));
|
||||
obj.set('SignedUrl', getPresignedUrl(SignedUrl, fileAdapter));
|
||||
}
|
||||
if (Url) {
|
||||
obj.set('URL', getPresignedUrl(Url));
|
||||
obj.set('URL', getPresignedUrl(Url, fileAdapter));
|
||||
}
|
||||
if (certificateUrl) {
|
||||
obj.set('CertificateUrl', getPresignedUrl(certificateUrl));
|
||||
obj.set('CertificateUrl', getPresignedUrl(certificateUrl, fileAdapter));
|
||||
}
|
||||
return [obj];
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ export default async function GetTemplate(request) {
|
||||
template.include('ExtUserPtr');
|
||||
template.include('Signers');
|
||||
template.include('CreatedBy');
|
||||
|
||||
template.include('ExtUserPtr.TenantId');
|
||||
const extUserQuery = new Parse.Query('contracts_Users');
|
||||
extUserQuery.equalTo('Email', userRes.data.email);
|
||||
extUserQuery.include('TeamIds');
|
||||
@@ -48,12 +48,14 @@ export default async function GetTemplate(request) {
|
||||
template.include('ExtUserPtr');
|
||||
template.include('Signers');
|
||||
template.include('CreatedBy');
|
||||
template.include('ExtUserPtr.TenantId');
|
||||
}
|
||||
}
|
||||
const res = await template.first({ useMasterKey: true });
|
||||
if (res) {
|
||||
// console.log("res ",res)
|
||||
return res;
|
||||
const templateRes = JSON.parse(JSON.stringify(res));
|
||||
delete templateRes?.ExtUserPtr?.TenantId?.FileAdapter;
|
||||
return templateRes;
|
||||
} else {
|
||||
return { error: "You don't have access of this document!" };
|
||||
}
|
||||
@@ -69,10 +71,13 @@ export default async function GetTemplate(request) {
|
||||
template.include('ExtUserPtr');
|
||||
template.include('Signers');
|
||||
template.include('CreatedBy');
|
||||
template.include('ExtUserPtr.TenantId');
|
||||
const res = await template.first({ useMasterKey: true });
|
||||
// console.log("res ", res)
|
||||
if (res) {
|
||||
return res;
|
||||
const templateRes = JSON.parse(JSON.stringify(res));
|
||||
delete templateRes?.ExtUserPtr?.TenantId?.FileAdapter;
|
||||
return templateRes;
|
||||
} else {
|
||||
return { error: "You don't have access of this document!" };
|
||||
}
|
||||
|
||||
@@ -9,14 +9,23 @@ async function TemplateAfterFind(request) {
|
||||
const SignedUrl = obj?.get('SignedUrl') && obj?.get('SignedUrl');
|
||||
const Url = obj?.get('URL') && obj?.get('URL');
|
||||
const certificateUrl = obj.get('CertificateUrl') && obj.get('CertificateUrl');
|
||||
const IsFileAdapter = obj?.get('IsFileAdapter') || false;
|
||||
let fileAdapter = {};
|
||||
if (IsFileAdapter) {
|
||||
const tenantId = obj?.get('ExtUserPtr')?.get('TenantId');
|
||||
if (tenantId) {
|
||||
const _tenantId = JSON.parse(JSON.stringify(obj?.get('ExtUserPtr')?.get('TenantId')));
|
||||
fileAdapter = _tenantId?.FileAdapter || {};
|
||||
}
|
||||
}
|
||||
if (SignedUrl) {
|
||||
obj.set('SignedUrl', getPresignedUrl(SignedUrl));
|
||||
obj.set('SignedUrl', getPresignedUrl(SignedUrl, fileAdapter));
|
||||
}
|
||||
if (Url) {
|
||||
obj.set('URL', getPresignedUrl(Url));
|
||||
obj.set('URL', getPresignedUrl(Url, fileAdapter));
|
||||
}
|
||||
if (certificateUrl) {
|
||||
obj.set('CertificateUrl', getPresignedUrl(certificateUrl));
|
||||
obj.set('CertificateUrl', getPresignedUrl(certificateUrl, fileAdapter));
|
||||
}
|
||||
|
||||
return [obj];
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
export default async function addFileAdapter(request) {
|
||||
if (!request?.user) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
|
||||
}
|
||||
const bucketName = request.params.bucketName;
|
||||
const region = request.params.region;
|
||||
const endpoint = request.params.endpoint;
|
||||
const baseUrl = request.params.baseUrl;
|
||||
const accessKeyId = request.params.accessKeyId;
|
||||
const secretAccessKey = request.params.secretAccessKey;
|
||||
const adapter = request.params.fileAdapter;
|
||||
|
||||
if (bucketName && region && endpoint && accessKeyId && secretAccessKey && adapter) {
|
||||
try {
|
||||
const extUserCls = new Parse.Query('contracts_Users');
|
||||
extUserCls.equalTo('UserId', request.user);
|
||||
const extUser = await extUserCls.first({ useMasterKey: true });
|
||||
if (extUser) {
|
||||
const tenantCls = new Parse.Object('partners_Tenant');
|
||||
tenantCls.id = extUser?.get('TenantId')?.id;
|
||||
const adapterConfig = {
|
||||
bucketName: bucketName,
|
||||
region: region,
|
||||
endpoint: endpoint,
|
||||
baseUrl: baseUrl,
|
||||
accessKeyId: accessKeyId,
|
||||
secretAccessKey: secretAccessKey,
|
||||
};
|
||||
tenantCls.set('FileAdapter', adapterConfig);
|
||||
tenantCls.set('ActiveFileAdapter', adapter);
|
||||
const updateTenant = await tenantCls.save(null, { useMasterKey: true });
|
||||
const tenantSchema = new Parse.Schema('partners_Tenant');
|
||||
tenantSchema.setCLP({
|
||||
get: { '*': true },
|
||||
find: { '*': true },
|
||||
count: { '*': true },
|
||||
create: { '*': true },
|
||||
update: { '*': true },
|
||||
delete: { '*': true },
|
||||
addField: { '*': true },
|
||||
protectedFields: { '*': ['FileAdapter'] },
|
||||
});
|
||||
await tenantSchema.update();
|
||||
return updateTenant.updatedAt;
|
||||
}
|
||||
return extUser;
|
||||
} catch (err) {
|
||||
console.log('Err in add custom file adapter', err);
|
||||
const code = err.code || 400;
|
||||
const msg = err.message || 'Something went wrong.';
|
||||
throw new Parse.Error(code, msg);
|
||||
}
|
||||
} else {
|
||||
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide all parameters.');
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ export default async function getDocument(request) {
|
||||
const query = new Parse.Query('contracts_Document');
|
||||
query.equalTo('objectId', docId);
|
||||
query.include('ExtUserPtr');
|
||||
query.include('ExtUserPtr.TenantId');
|
||||
query.include('CreatedBy');
|
||||
query.include('Signers');
|
||||
query.include('AuditTrail.UserPtr');
|
||||
@@ -20,8 +21,10 @@ export default async function getDocument(request) {
|
||||
const res = await query.first({ useMasterKey: true });
|
||||
if (res) {
|
||||
const IsEnableOTP = res?.get('IsEnableOTP') || false;
|
||||
const document = JSON.parse(JSON.stringify(res));
|
||||
delete document.ExtUserPtr.TenantId.FileAdapter;
|
||||
if (!IsEnableOTP) {
|
||||
return res;
|
||||
return document;
|
||||
} else {
|
||||
if (request?.headers?.['sessiontoken']) {
|
||||
try {
|
||||
@@ -34,7 +37,7 @@ export default async function getDocument(request) {
|
||||
const userId = userRes.data && userRes.data?.objectId;
|
||||
const acl = res.getACL();
|
||||
if (userId && acl && acl.getReadAccess(userId)) {
|
||||
return res;
|
||||
return document;
|
||||
} else {
|
||||
return { error: "You don't have access of this document!" };
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
export default async function getFileAdapter(request) {
|
||||
if (!request?.user) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
|
||||
}
|
||||
try {
|
||||
const extUserCls = new Parse.Query('contracts_Users');
|
||||
extUserCls.equalTo('UserId', request.user);
|
||||
extUserCls.include('TenantId');
|
||||
const extUser = await extUserCls.first({ useMasterKey: true });
|
||||
if (extUser) {
|
||||
const _extUser = JSON.parse(JSON.stringify(extUser));
|
||||
if (_extUser?.TenantId?.ActiveFileAdapter && _extUser?.TenantId?.FileAdapter?.bucketName) {
|
||||
const adapterConfig = {
|
||||
bucketName: _extUser?.TenantId?.FileAdapter?.bucketName,
|
||||
region: _extUser?.TenantId?.FileAdapter?.region,
|
||||
endpoint: _extUser?.TenantId?.FileAdapter?.endpoint,
|
||||
baseUrl: _extUser?.TenantId?.FileAdapter?.baseUrl,
|
||||
accessKeyId: _extUser?.TenantId?.FileAdapter?.accessKeyId,
|
||||
secretAccessKey: _extUser?.TenantId?.FileAdapter?.secretAccessKey,
|
||||
fileAdapter: _extUser?.TenantId?.ActiveFileAdapter,
|
||||
};
|
||||
return adapterConfig;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
} else {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Err in add custom file adapter', err);
|
||||
const code = err.code || 400;
|
||||
const msg = err.message || 'Something went wrong.';
|
||||
throw new Parse.Error(code, msg);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
import AWS from 'aws-sdk';
|
||||
import { useLocal } from '../../Utils.js';
|
||||
export default function getPresignedUrl(url) {
|
||||
export default function getPresignedUrl(url, adapter) {
|
||||
const credentials = {
|
||||
accessKeyId: process.env.DO_ACCESS_KEY_ID,
|
||||
secretAccessKey: process.env.DO_SECRET_ACCESS_KEY,
|
||||
accessKeyId: adapter?.accessKeyId || process.env.DO_ACCESS_KEY_ID,
|
||||
secretAccessKey: adapter?.secretAccessKey || process.env.DO_SECRET_ACCESS_KEY,
|
||||
};
|
||||
AWS.config.update({ credentials: credentials, region: process.env.DO_REGION });
|
||||
const spacesEndpoint = new AWS.Endpoint(process.env.DO_ENDPOINT);
|
||||
AWS.config.update({ credentials: credentials, region: adapter?.region || process.env.DO_REGION });
|
||||
const spacesEndpoint = adapter?.endpoint || new AWS.Endpoint(process.env.DO_ENDPOINT);
|
||||
|
||||
const s3 = new AWS.S3({ endpoint: spacesEndpoint });
|
||||
|
||||
@@ -19,7 +19,7 @@ export default function getPresignedUrl(url) {
|
||||
|
||||
// presignedGETURL return presignedUrl with expires time
|
||||
const presignedGETURL = s3.getSignedUrl('getObject', {
|
||||
Bucket: process.env.DO_SPACE,
|
||||
Bucket: adapter?.bucketName || process.env.DO_SPACE,
|
||||
Key: filename, //filename
|
||||
Expires: 160, //time to expire in seconds
|
||||
});
|
||||
@@ -29,26 +29,51 @@ export default function getPresignedUrl(url) {
|
||||
export async function getSignedUrl(request) {
|
||||
try {
|
||||
const docId = request.params.docId || '';
|
||||
const templateId = request.params.templateId || '';
|
||||
const url = request.params.url;
|
||||
if (docId) {
|
||||
const IsFileAdapter = request.params.isFileAdapter || '';
|
||||
if (docId || templateId) {
|
||||
try {
|
||||
const query = new Parse.Query('contracts_Document');
|
||||
query.equalTo('objectId', docId);
|
||||
query.notEqualTo('IsEnableOTP', true);
|
||||
query.include('CreatedBy');
|
||||
query.include('Signers');
|
||||
query.include('AuditTrail.UserPtr');
|
||||
query.include('Placeholders');
|
||||
query.include('DeclineBy');
|
||||
query.notEqualTo('IsArchive', true);
|
||||
const res = await query.first({ useMasterKey: true });
|
||||
if (res) {
|
||||
if (useLocal !== 'true') {
|
||||
const presignedUrl = getPresignedUrl(url);
|
||||
return presignedUrl;
|
||||
} else {
|
||||
return url;
|
||||
if (useLocal !== 'true') {
|
||||
const query = new Parse.Query(docId ? 'contracts_Document' : 'contracts_Template');
|
||||
query.equalTo('objectId', docId ? docId : templateId);
|
||||
query.include('ExtUserPtr.TenantId');
|
||||
query.notEqualTo('IsArchive', true);
|
||||
const res = await query.first({ useMasterKey: true });
|
||||
if (res) {
|
||||
const _resDoc = JSON.parse(JSON.stringify(res));
|
||||
if (_resDoc?.IsEnableOTP) {
|
||||
if (!request?.user) {
|
||||
throw new Parse.Error(
|
||||
Parse.Error.INVALID_SESSION_TOKEN,
|
||||
'User is not authenticated.'
|
||||
);
|
||||
} else {
|
||||
let adapterConfig = {};
|
||||
if (IsFileAdapter) {
|
||||
// `FileAdapter` && `ActiveFileAdapter` is used to save file in user's fileAdapter
|
||||
const FileAdapter = _resDoc?.ExtUserPtr?.TenantId?.FileAdapter;
|
||||
const ActiveFileAdapter = _resDoc?.ExtUserPtr?.TenantId?.ActiveFileAdapter;
|
||||
if (FileAdapter && ActiveFileAdapter) {
|
||||
adapterConfig = { ActiveFileAdapter: ActiveFileAdapter, ...FileAdapter };
|
||||
}
|
||||
}
|
||||
const presignedUrl = getPresignedUrl(url, adapterConfig);
|
||||
return presignedUrl;
|
||||
}
|
||||
} else {
|
||||
const FileAdapter = _resDoc?.ExtUserPtr?.TenantId?.FileAdapter;
|
||||
const ActiveFileAdapter = _resDoc?.ExtUserPtr?.TenantId?.ActiveFileAdapter;
|
||||
let adapterConfig = {};
|
||||
if (FileAdapter && ActiveFileAdapter) {
|
||||
adapterConfig = { ActiveFileAdapter: ActiveFileAdapter, ...FileAdapter };
|
||||
}
|
||||
const presignedUrl = getPresignedUrl(url, adapterConfig);
|
||||
return presignedUrl;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return url;
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('Err in presigned url', err);
|
||||
|
||||
@@ -14,6 +14,7 @@ async function getUserDetails(request) {
|
||||
userQuery.include('UserId');
|
||||
userQuery.include('CreatedBy');
|
||||
userQuery.exclude('CreatedBy.authData');
|
||||
userQuery.exclude('TenantId.FileAdapter');
|
||||
userQuery.exclude('google_refresh_token');
|
||||
if (userId) {
|
||||
userQuery.equalTo('CreatedBy', { __type: 'Pointer', className: '_User', objectId: userId });
|
||||
|
||||
@@ -6,18 +6,35 @@ import { pdflibAddPlaceholder } from '@signpdf/placeholder-pdf-lib';
|
||||
import { PDFDocument } from 'pdf-lib';
|
||||
import { cloudServerUrl, replaceMailVaribles, saveFileUsage } from '../../../Utils.js';
|
||||
import GenerateCertificate from './GenerateCertificate.js';
|
||||
import uploadFileToS3 from '../uploadFiletoS3.js';
|
||||
const serverUrl = cloudServerUrl; // process.env.SERVER_URL;
|
||||
const APPID = process.env.APP_ID;
|
||||
const masterKEY = process.env.MASTER_KEY;
|
||||
const eSignName = 'opensign';
|
||||
const eSigncontact = 'hello@opensignlabs.com';
|
||||
// `updateDoc` is used to create url in from pdfFile
|
||||
async function uploadFile(pdfName, filepath) {
|
||||
async function uploadFile(pdfName, filepath, adapter) {
|
||||
try {
|
||||
const filedata = fs.readFileSync(filepath);
|
||||
const file = new Parse.File(pdfName, [...filedata], 'application/pdf');
|
||||
await file.save({ useMasterKey: true });
|
||||
const fileUrl = file.url();
|
||||
let fileUrl;
|
||||
if (adapter?.bucketName) {
|
||||
const adapterConfig = {
|
||||
fileAdapter: adapter?.ActiveFileAdapter,
|
||||
bucketName: adapter?.bucketName,
|
||||
region: adapter?.region,
|
||||
endpoint: adapter?.endpoint,
|
||||
accessKeyId: adapter?.accessKeyId,
|
||||
secretAccessKey: adapter?.secretAccessKey,
|
||||
baseUrl: adapter?.baseUrl,
|
||||
};
|
||||
// `uploadFileToS3` is used to save document in user's file storage
|
||||
fileUrl = await uploadFileToS3(filedata, pdfName, 'application/pdf', adapterConfig);
|
||||
} else {
|
||||
const file = new Parse.File(pdfName, [...filedata], 'application/pdf');
|
||||
await file.save({ useMasterKey: true });
|
||||
fileUrl = file.url();
|
||||
}
|
||||
|
||||
return { imageUrl: fileUrl };
|
||||
} catch (err) {
|
||||
console.log('Err ', err);
|
||||
@@ -79,7 +96,7 @@ async function updateDoc(docId, url, userId, ipAddress, data, className, sign) {
|
||||
|
||||
// `sendCompletedMail` is used to send copy of completed document mail
|
||||
async function sendCompletedMail(obj) {
|
||||
const url = obj.url;
|
||||
const url = obj.doc?.SignedUrl;
|
||||
const doc = obj.doc;
|
||||
const sender = obj.doc.ExtUserPtr;
|
||||
const pdfName = doc.Name;
|
||||
@@ -223,7 +240,7 @@ async function sendDoctoWebhook(doc, Url, event, signUser, certificateUrl) {
|
||||
}
|
||||
|
||||
// `sendMailsaveCertifcate` is used send completion mail and update complete status of document
|
||||
const sendMailsaveCertifcate = async (doc, P12Buffer, url, isCustomMail, mailProvider, userId) => {
|
||||
async function sendMailsaveCertifcate(doc, P12Buffer, isCustomMail, mailProvider, adapterConfig) {
|
||||
const certificate = await GenerateCertificate(doc);
|
||||
const certificatePdf = await PDFDocument.load(certificate);
|
||||
const p12 = new P12Signer(P12Buffer, { passphrase: process.env.PASS_PHRASE || null });
|
||||
@@ -245,7 +262,7 @@ const sendMailsaveCertifcate = async (doc, P12Buffer, url, isCustomMail, mailPro
|
||||
|
||||
//below is used to save signed certificate in exports folder
|
||||
fs.writeFileSync('./exports/certificate.pdf', signedCertificate);
|
||||
const file = await uploadFile('certificate.pdf', './exports/certificate.pdf');
|
||||
const file = await uploadFile('certificate.pdf', './exports/certificate.pdf', adapterConfig);
|
||||
const body = { CertificateUrl: file.imageUrl };
|
||||
await axios.put(serverUrl + '/classes/contracts_Document/' + doc.objectId, body, {
|
||||
headers: {
|
||||
@@ -258,12 +275,11 @@ const sendMailsaveCertifcate = async (doc, P12Buffer, url, isCustomMail, mailPro
|
||||
if (doc.IsSendMail === false) {
|
||||
console.log("don't send mail");
|
||||
} else {
|
||||
const mailObj = { url: url, isCustomMail: isCustomMail, doc: doc, mailProvider: mailProvider };
|
||||
sendCompletedMail(mailObj);
|
||||
sendCompletedMail({ isCustomMail, doc, mailProvider });
|
||||
}
|
||||
saveFileUsage(CertificateBuffer.length, file.imageUrl, userId);
|
||||
sendDoctoWebhook(doc, url, 'completed', '', file.imageUrl);
|
||||
};
|
||||
saveFileUsage(CertificateBuffer.length, file.imageUrl, doc?.CreatedBy?.objectId);
|
||||
sendDoctoWebhook(doc, doc?.SignedUrl, 'completed', '', file.imageUrl);
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param docId Id of Document in which user is signing
|
||||
@@ -280,7 +296,7 @@ async function PDF(req) {
|
||||
const sign = req.params.signature || '';
|
||||
// below bode is used to get info of docId
|
||||
const docQuery = new Parse.Query('contracts_Document');
|
||||
docQuery.include('ExtUserPtr,Signers');
|
||||
docQuery.include('ExtUserPtr,Signers,ExtUserPtr.TenantId');
|
||||
docQuery.equalTo('objectId', docId);
|
||||
const resDoc = await docQuery.first({ useMasterKey: true });
|
||||
if (!resDoc) {
|
||||
@@ -294,6 +310,13 @@ async function PDF(req) {
|
||||
}
|
||||
}
|
||||
const _resDoc = resDoc?.toJSON();
|
||||
// `FileAdapter` && `ActiveFileAdapter` is used to save file in user's fileAdapter
|
||||
const FileAdapter = _resDoc?.ExtUserPtr?.TenantId?.FileAdapter;
|
||||
const ActiveFileAdapter = _resDoc?.ExtUserPtr?.TenantId?.ActiveFileAdapter;
|
||||
let adapterConfig = {};
|
||||
if (FileAdapter && ActiveFileAdapter) {
|
||||
adapterConfig = { ActiveFileAdapter: ActiveFileAdapter, ...FileAdapter };
|
||||
}
|
||||
let signUser;
|
||||
let className;
|
||||
// `reqUserId` is send throught pdfrequest signing flow
|
||||
@@ -339,7 +362,7 @@ async function PDF(req) {
|
||||
}
|
||||
const randomNumber = Math.floor(Math.random() * 5000);
|
||||
const name = `exported_file_${randomNumber}.pdf`;
|
||||
const pdfName = `./exports/${name}`;
|
||||
const filePath = `./exports/${name}`;
|
||||
let pdfSize = PdfBuffer.length;
|
||||
if (isCompleted) {
|
||||
const signersName = _resDoc.Signers?.map(x => x.Name + ' <' + x.Email + '>');
|
||||
@@ -376,22 +399,22 @@ async function PDF(req) {
|
||||
const signedDocs = await OBJ.sign(PdfBuffer, p12Cert);
|
||||
|
||||
//`saveUrl` is used to save signed pdf in exports folder
|
||||
const saveUrl = fs.writeFileSync(pdfName, signedDocs);
|
||||
fs.writeFileSync(filePath, signedDocs);
|
||||
pdfSize = signedDocs.length;
|
||||
} else {
|
||||
//`saveUrl` is used to save signed pdf in exports folder
|
||||
const saveUrl = fs.writeFileSync(pdfName, PdfBuffer);
|
||||
fs.writeFileSync(filePath, PdfBuffer);
|
||||
pdfSize = PdfBuffer.length;
|
||||
}
|
||||
|
||||
// `uploadFile` is used to upload pdf to aws s3 and get it's url
|
||||
const data = await uploadFile(name, pdfName);
|
||||
const data = await uploadFile(name, filePath, adapterConfig);
|
||||
|
||||
if (data && data.imageUrl) {
|
||||
// `axios` is used to update signed pdf url in contracts_Document classes for given DocId
|
||||
const updatedDoc = await updateDoc(
|
||||
req.params.docId, //docId
|
||||
data.imageUrl, // url
|
||||
data.imageUrl, // SignedUrl
|
||||
signUser.objectId, // userID
|
||||
userIP, // client ipAddress,
|
||||
_resDoc, // auditTrail, signers, etc data
|
||||
@@ -401,19 +424,12 @@ async function PDF(req) {
|
||||
sendDoctoWebhook(_resDoc, data.imageUrl, 'signed', signUser);
|
||||
saveFileUsage(pdfSize, data.imageUrl, _resDoc?.CreatedBy?.objectId);
|
||||
if (updatedDoc && updatedDoc.isCompleted) {
|
||||
const doc = { ..._resDoc, AuditTrail: updatedDoc.AuditTrail };
|
||||
sendMailsaveCertifcate(
|
||||
doc,
|
||||
P12Buffer,
|
||||
data.imageUrl,
|
||||
isCustomMail,
|
||||
mailProvider,
|
||||
_resDoc?.CreatedBy?.objectId
|
||||
);
|
||||
const doc = { ..._resDoc, AuditTrail: updatedDoc.AuditTrail, SignedUrl: data.imageUrl };
|
||||
sendMailsaveCertifcate(doc, P12Buffer, isCustomMail, mailProvider, adapterConfig);
|
||||
}
|
||||
// `fs.unlinkSync` is used to remove exported signed pdf file from exports folder
|
||||
fs.unlinkSync(pdfName);
|
||||
console.log(`New Signed PDF created called: ${pdfName}`);
|
||||
fs.unlinkSync(filePath);
|
||||
console.log(`New Signed PDF created called: ${filePath}`);
|
||||
if (updatedDoc.message === 'success') {
|
||||
return { status: 'success', data: data.imageUrl };
|
||||
} else {
|
||||
|
||||
@@ -29,6 +29,7 @@ export default function reportJson(id, userId) {
|
||||
'Signers.Phone',
|
||||
'Placeholders',
|
||||
'IsSignyourself',
|
||||
'IsFileAdapter',
|
||||
],
|
||||
};
|
||||
|
||||
@@ -74,6 +75,7 @@ export default function reportJson(id, userId) {
|
||||
'AuditTrail',
|
||||
'Placeholders',
|
||||
'SignedUrl',
|
||||
'IsFileAdapter',
|
||||
],
|
||||
};
|
||||
// In progess report
|
||||
@@ -113,6 +115,7 @@ export default function reportJson(id, userId) {
|
||||
'SendMail',
|
||||
'Placeholders',
|
||||
'SignedUrl',
|
||||
'IsFileAdapter',
|
||||
],
|
||||
};
|
||||
// completed documents report
|
||||
@@ -144,6 +147,7 @@ export default function reportJson(id, userId) {
|
||||
'Placeholders',
|
||||
'IsSignyourself',
|
||||
'IsCompleted',
|
||||
'IsFileAdapter',
|
||||
],
|
||||
};
|
||||
// declined documents report
|
||||
@@ -173,6 +177,7 @@ export default function reportJson(id, userId) {
|
||||
'Placeholders',
|
||||
'DeclineReason',
|
||||
'SignedUrl',
|
||||
'IsFileAdapter',
|
||||
],
|
||||
};
|
||||
// Expired Documents report
|
||||
@@ -205,6 +210,7 @@ export default function reportJson(id, userId) {
|
||||
'Signers.Phone',
|
||||
'Placeholders',
|
||||
'SignedUrl',
|
||||
'IsFileAdapter',
|
||||
],
|
||||
};
|
||||
// Recently sent for signatures report show on dashboard
|
||||
@@ -242,6 +248,7 @@ export default function reportJson(id, userId) {
|
||||
'ExpiryDate',
|
||||
'Placeholders',
|
||||
'SignedUrl',
|
||||
'IsFileAdapter',
|
||||
],
|
||||
};
|
||||
// Recent signature requests report show on dashboard
|
||||
@@ -284,6 +291,7 @@ export default function reportJson(id, userId) {
|
||||
'Signers.Phone',
|
||||
'Placeholders',
|
||||
'SignedUrl',
|
||||
'IsFileAdapter',
|
||||
],
|
||||
};
|
||||
// Drafts report show on dashboard
|
||||
@@ -312,6 +320,7 @@ export default function reportJson(id, userId) {
|
||||
'Signers.Email',
|
||||
'Signers.Phone',
|
||||
'Placeholders',
|
||||
'IsFileAdapter',
|
||||
],
|
||||
};
|
||||
// contact book report
|
||||
@@ -352,6 +361,7 @@ export default function reportJson(id, userId) {
|
||||
'IsPublic',
|
||||
'SharedWith.Name',
|
||||
'SendinOrder',
|
||||
'IsFileAdapter',
|
||||
],
|
||||
};
|
||||
default:
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import uploadFileToS3 from './uploadFiletoS3.js';
|
||||
|
||||
export default async function saveToFileAdapter(request) {
|
||||
if (!request?.user) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'User is not authenticated.');
|
||||
}
|
||||
|
||||
if (!request.params.fileBase64) {
|
||||
throw new Parse.Error(Parse.Error.INVALID_QUERY, 'Please provide file.');
|
||||
}
|
||||
const fileBase64 = request.params.fileBase64;
|
||||
try {
|
||||
const extCls = new Parse.Query('contracts_Users');
|
||||
extCls.equalTo('UserId', request.user);
|
||||
extCls.include('TenantId');
|
||||
const resExt = await extCls.first({ useMasterKey: true });
|
||||
if (resExt) {
|
||||
const _resExt = JSON.parse(JSON.stringify(resExt));
|
||||
|
||||
if (_resExt?.TenantId.FileAdapter?.accessKeyId) {
|
||||
const adapterConfig = {
|
||||
fileAdapter: _resExt?.TenantId?.ActiveFileAdapter,
|
||||
bucketName: _resExt?.TenantId?.FileAdapter?.bucketName,
|
||||
region: _resExt?.TenantId?.FileAdapter?.region,
|
||||
endpoint: _resExt?.TenantId?.FileAdapter?.endpoint,
|
||||
accessKeyId: _resExt?.TenantId?.FileAdapter?.accessKeyId,
|
||||
secretAccessKey: _resExt?.TenantId?.FileAdapter?.secretAccessKey,
|
||||
baseUrl: _resExt?.TenantId?.FileAdapter?.baseUrl,
|
||||
};
|
||||
const buffer = Buffer.from(fileBase64, 'base64');
|
||||
const fileName = request.params.fileName;
|
||||
const ext = request.params.fileName?.split('.')?.pop();
|
||||
let mimeType;
|
||||
if (ext === 'pdf') {
|
||||
mimeType = 'application/pdf';
|
||||
} else if (ext === 'png' || ext === 'jpeg' || ext === 'jpg') {
|
||||
mimeType = `image/${ext}`;
|
||||
}
|
||||
try {
|
||||
const presignedUrl = await uploadFileToS3(buffer, fileName, mimeType, adapterConfig);
|
||||
return { url: presignedUrl };
|
||||
} catch (err) {
|
||||
console.error('Error generate presigned url:', err);
|
||||
throw new Parse.Error(400, 'Something went wrong.');
|
||||
}
|
||||
} else {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'S3 credentials not found.');
|
||||
}
|
||||
} else {
|
||||
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'User not found.');
|
||||
}
|
||||
} catch (err) {
|
||||
console.log('err in savetoS3', err);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { S3Client, PutObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3';
|
||||
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
||||
import * as crypto from 'node:crypto';
|
||||
async function uploadFileToS3(buffer, fileName, mimeType, adapter) {
|
||||
const bucketName = adapter?.bucketName;
|
||||
let client;
|
||||
if (adapter?.fileAdapter === 'digitalocean') {
|
||||
client = new S3Client({
|
||||
endpoint: adapter?.endpoint,
|
||||
region: adapter?.region,
|
||||
credentials: { accessKeyId: adapter?.accessKeyId, secretAccessKey: adapter?.secretAccessKey },
|
||||
});
|
||||
} else {
|
||||
client = new S3Client({
|
||||
region: adapter?.region,
|
||||
credentials: { accessKeyId: adapter?.accessKeyId, secretAccessKey: adapter?.secretAccessKey },
|
||||
});
|
||||
}
|
||||
const prefixId = crypto.randomBytes(16).toString('hex');
|
||||
const fileKey = `${prefixId}_${fileName}`;
|
||||
const uploadParams = { Bucket: bucketName, Key: fileKey, Body: buffer, ContentType: mimeType };
|
||||
|
||||
try {
|
||||
// Upload the buffer to the Space
|
||||
const command = new PutObjectCommand(uploadParams);
|
||||
await client.send(command);
|
||||
const getCommand = new GetObjectCommand({ Bucket: bucketName, Key: fileKey });
|
||||
|
||||
// Generate a presigned URL for the uploaded file
|
||||
const presignedUrl = await getSignedUrl(client, getCommand, { expiresIn: 900 }); // URL expiration time in seconds (e.g., 15 min)
|
||||
return presignedUrl;
|
||||
} catch (error) {
|
||||
console.error('Error uploading file to aws:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export default uploadFileToS3;
|
||||
Reference in New Issue
Block a user