feat: enable custom file storage for document siging

This commit is contained in:
prafull-opensignlabs
2024-10-04 12:59:10 +05:30
parent 356ab4e7cb
commit 00fcd17285
26 changed files with 1856 additions and 1451 deletions
@@ -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;