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
+553 -558
View File
File diff suppressed because it is too large Load Diff
+8 -8
View File
@@ -6,14 +6,14 @@
"@formkit/auto-animate": "^0.8.2",
"@lottiefiles/dotlottie-react": "^0.9.0",
"@pdf-lib/fontkit": "^1.1.1",
"@radix-ui/themes": "^3.1.3",
"@radix-ui/themes": "^3.1.4",
"@react-pdf/renderer": "^4.0.0",
"@reduxjs/toolkit": "^2.2.7",
"axios": "^1.7.7",
"file-saver": "^2.0.5",
"i18next": "^23.15.1",
"i18next-browser-languagedetector": "^8.0.0",
"i18next-http-backend": "^2.6.1",
"i18next-http-backend": "^2.6.2",
"jszip": "^3.10.1",
"jwt-decode": "^4.0.0",
"moment": "^2.30.1",
@@ -95,22 +95,22 @@
}
},
"devDependencies": {
"@babel/core": "^7.25.2",
"@babel/preset-env": "^7.25.4",
"@babel/preset-react": "^7.24.7",
"@babel/runtime-corejs2": "^7.25.6",
"@babel/core": "^7.25.7",
"@babel/preset-env": "^7.25.7",
"@babel/preset-react": "^7.25.7",
"@babel/runtime-corejs2": "^7.25.7",
"autoprefixer": "^10.4.20",
"babel-loader": "^9.2.1",
"clean-webpack-plugin": "^4.0.0",
"commitizen": "^4.3.1",
"concurrently": "^9.0.1",
"css-loader": "^7.1.2",
"daisyui": "^4.12.10",
"daisyui": "^4.12.11",
"dotenv": "^16.4.5",
"dotenv-webpack": "^8.1.0",
"eslint": "^9.11.1",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-react": "^7.37.0",
"eslint-plugin-react": "^7.37.1",
"lint-staged": "^15.2.10",
"mini-css-extract-plugin": "^2.9.1",
"postcss": "^8.4.47",
@@ -30,16 +30,15 @@ function EmailComponent({
const pdfName = pdfDetails[0]?.Name;
setIsLoading(true);
let sendMail;
const docId = !pdfDetails?.[0]?.IsEnableOTP
? pdfDetails?.[0]?.objectId
: "";
const docId = pdfDetails?.[0]?.objectId || "";
const isFileAdapter = pdfDetails?.[0]?.IsFileAdapter
? pdfDetails?.[0]?.IsFileAdapter
: false;
let presignedUrl = pdfUrl;
try {
// const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl });
const axiosRes = await axios.post(
`${localStorage.getItem("baseUrl")}/functions/getsignedurl`,
{ url: pdfUrl, docId: docId },
{ url: pdfUrl, docId: docId, isFileAdapter: isFileAdapter },
{
headers: {
"content-type": "Application/json",
@@ -41,9 +41,9 @@ const SuggestionInput = (props) => {
const handleInputChange = async (e) => {
const value = e.target.value;
setInputValue(value?.toLowerCase());
setInputValue(value?.toLowerCase()?.replace(/\s/g, ""));
if (props.onChange) {
props.onChange(value);
props.onChange(value?.toLowerCase()?.replace(/\s/g, ""));
}
if (value.trim() === "") {
+22 -6
View File
@@ -2048,11 +2048,21 @@ export const fetchUrl = async (url, pdfName) => {
console.error("Error downloading the file:", error);
}
};
export const getSignedUrl = async (pdfUrl, docId) => {
export const getSignedUrl = async (
pdfUrl,
docId,
isFileAdapter,
templateId
) => {
//use only axios here due to public template sign
const axiosRes = await axios.post(
`${localStorage.getItem("baseUrl")}/functions/getsignedurl`,
{ url: pdfUrl, docId: docId || "" },
{
url: pdfUrl,
docId: docId || "",
isFileAdapter: isFileAdapter,
templateId: templateId || ""
},
{
headers: {
"content-type": "Application/json",
@@ -2069,9 +2079,12 @@ export const handleDownloadPdf = async (pdfDetails, setIsDownloading) => {
const pdfName = pdfDetails[0] && pdfDetails[0]?.Name;
const pdfUrl = pdfDetails?.[0]?.SignedUrl || pdfDetails?.[0]?.URL;
setIsDownloading && setIsDownloading("pdf");
const docId = !pdfDetails?.[0]?.IsEnableOTP ? pdfDetails?.[0]?.objectId : "";
const docId = pdfDetails?.[0]?.objectId || "";
const isFileAdapter = pdfDetails?.[0]?.IsFileAdapter
? pdfDetails?.[0]?.IsFileAdapter
: false;
try {
const url = await getSignedUrl(pdfUrl, docId);
const url = await getSignedUrl(pdfUrl, docId, isFileAdapter);
await fetchUrl(url, pdfName);
setIsDownloading && setIsDownloading("");
} catch (err) {
@@ -2094,14 +2107,17 @@ export const handleToPrint = async (
) => {
event.preventDefault();
setIsDownloading("pdf");
const docId = !pdfDetails?.[0]?.IsEnableOTP ? pdfDetails?.[0]?.objectId : "";
const docId = pdfDetails?.[0]?.objectId || "";
const isFileAdapter = pdfDetails?.[0]?.IsFileAdapter
? pdfDetails?.[0]?.IsFileAdapter
: false;
try {
// const url = await Parse.Cloud.run("getsignedurl", { url: pdfUrl });
//`localStorage.getItem("baseUrl")` is also use in public-profile flow for public-sign
//if we give this `appInfo.baseUrl` as a base url then in public-profile it will create base url of it's window.location.origin ex- opensign.me which is not base url
const axiosRes = await axios.post(
`${localStorage.getItem("baseUrl")}/functions/getsignedurl`,
{ url: pdfUrl, docId: docId },
{ url: pdfUrl, docId: docId, isFileAdapter: isFileAdapter },
{
headers: {
"content-type": "Application/json",
+266 -129
View File
@@ -85,6 +85,9 @@ const Forms = (props) => {
setIsCorrectPass(true);
setFormData({ ...formData, [e.target.name]: e.target.value });
};
const extUserData =
localStorage.getItem("Extand_Class") &&
JSON.parse(localStorage.getItem("Extand_Class"))?.[0];
useEffect(() => {
handleReset();
return () => abortController.abort();
@@ -153,10 +156,7 @@ const Forms = (props) => {
formData.append("file", files[0]);
formData.append("password", "");
const config = {
headers: {
"content-type": "multipart/form-data"
// sessiontoken: Parse.User.current().getSessionToken()
},
headers: { "content-type": "multipart/form-data" },
responseType: "blob"
};
const response = await axios.post(url, formData, config);
@@ -168,35 +168,58 @@ const Forms = (props) => {
});
setIsDecrypting(false);
setfileload(true);
// Upload the file to Parse Server
const parseFile = new Parse.File(
name,
pdfFile,
"application/pdf"
);
await parseFile.save({
progress: (progressValue, loaded, total, { type }) => {
if (type === "upload" && progressValue !== null) {
const percentCompleted = Math.round(
(loaded * 100) / total
);
setpercentage(percentCompleted);
}
if (extUserData?.TenantId?.ActiveFileAdapter) {
const base64 = await toDataUrl(pdfFile);
const fileBase64 = base64.split(",").pop();
const ext = files?.[0]?.name?.split(".").pop();
const fileRes = await saveToCustomFile(
fileBase64,
`${name}.${ext}`
);
if (fileRes.url) {
setFileUpload(fileRes.url);
setfileload(false);
setpercentage(0);
const title = generateTitleFromFilename(files?.[0]?.name);
setFormData((obj) => ({ ...obj, Name: title }));
const tenantId = localStorage.getItem("TenantId");
SaveFileSize(size, fileRes.url, tenantId);
return fileRes.url;
} else {
setfileload(false);
setpercentage(0);
setFileUpload("");
e.target.value = "";
}
});
} else {
// Upload the file to Parse Server
const parseFile = new Parse.File(
name,
pdfFile,
"application/pdf"
);
// Retrieve the URL of the uploaded file
if (parseFile.url()) {
setFileUpload(parseFile.url());
setfileload(false);
const title = generateTitleFromFilename(files?.[0]?.name);
setFormData((obj) => ({ ...obj, Name: title }));
SaveFileSize(size, response.url(), tenantId);
const tenantId = localStorage.getItem("TenantId");
SaveFileSize(size, parseFile.url(), tenantId);
return parseFile.url();
await parseFile.save({
progress: (progressValue, loaded, total, { type }) => {
if (type === "upload" && progressValue !== null) {
const percentCompleted = Math.round(
(loaded * 100) / total
);
setpercentage(percentCompleted);
}
}
});
// Retrieve the URL of the uploaded file
if (parseFile.url()) {
setFileUpload(parseFile.url());
setfileload(false);
const title = generateTitleFromFilename(files?.[0]?.name);
setFormData((obj) => ({ ...obj, Name: title }));
const tenantId = localStorage.getItem("TenantId");
SaveFileSize(size, parseFile.url(), tenantId);
return parseFile.url();
}
}
} catch (err) {
setfileload(false);
@@ -237,46 +260,73 @@ const Forms = (props) => {
width: imageWidth,
height: imageHeight
});
const getFile = await pdfDoc.save({
useObjectStreams: false
});
setfileload(true);
const size = files[0].size;
const size = files?.[0]?.size;
const name = generatePdfName(16);
const pdfName = `${name?.split(".")[0]}.pdf`;
const parseFile = new Parse.File(
pdfName,
[...getFile],
"application/pdf"
);
try {
const response = await parseFile.save({
progress: (progressValue, loaded, total, { type }) => {
if (type === "upload" && progressValue !== null) {
const percentCompleted = Math.round(
(loaded * 100) / total
);
setpercentage(percentCompleted);
}
}
if (extUserData?.TenantId?.ActiveFileAdapter) {
const base64 = await pdfDoc.saveAsBase64({
useObjectStreams: false
});
// The response object will contain information about the uploaded file
// You can access the URL of the uploaded file using response.url()
setFileUpload(response.url());
setfileload(false);
if (response.url()) {
const tenantId = localStorage.getItem("TenantId");
setfileload(true);
const fileBase64 = base64.split(",").pop();
const ext = files?.[0]?.name?.split(".").pop();
const fileRes = await saveToCustomFile(
fileBase64,
`${name}.${ext}`
);
if (fileRes.url) {
setFileUpload(fileRes.url);
setfileload(false);
setpercentage(0);
const title = generateTitleFromFilename(files?.[0]?.name);
setFormData((obj) => ({ ...obj, Name: title }));
SaveFileSize(size, response.url(), tenantId);
return response.url();
const tenantId = localStorage.getItem("TenantId");
SaveFileSize(size, fileRes.url, tenantId);
return fileRes.url;
} else {
setfileload(false);
setpercentage(0);
e.target.value = "";
}
} else {
const getFile = await pdfDoc.save({
useObjectStreams: false
});
setfileload(true);
const pdfName = `${name?.split(".")[0]}.pdf`;
const parseFile = new Parse.File(
pdfName,
[...getFile],
"application/pdf"
);
try {
const response = await parseFile.save({
progress: (progressValue, loaded, total, { type }) => {
if (type === "upload" && progressValue !== null) {
const percentCompleted = Math.round(
(loaded * 100) / total
);
setpercentage(percentCompleted);
}
}
});
// The response object will contain information about the uploaded file
// You can access the URL of the uploaded file using response.url()
setFileUpload(response.url());
setfileload(false);
if (response.url()) {
const tenantId = localStorage.getItem("TenantId");
const title = generateTitleFromFilename(files?.[0]?.name);
setFormData((obj) => ({ ...obj, Name: title }));
SaveFileSize(size, response.url(), tenantId);
return response.url();
}
} catch (error) {
e.target.value = "";
setfileload(false);
setpercentage(0);
console.error("Error uploading file:", error);
}
} catch (error) {
e.target.value = "";
setfileload(false);
setpercentage(0);
console.error("Error uploading file:", error);
}
} else {
if (isEnableSubscription) {
@@ -322,41 +372,78 @@ const Forms = (props) => {
}
};
const saveToCustomFile = async (base64, filname) => {
try {
const savetos3 = await Parse.Cloud.run("savetofileadapter", {
fileBase64: base64,
fileName: filname
});
return { url: savetos3.url };
} catch (err) {
console.log("err in save to customfile", err);
return { url: "" };
}
};
const handleFileUpload = async (file) => {
setfileload(true);
const size = file.size;
const name = generatePdfName(16);
const pdfFile = file;
const parseFile = new Parse.File(name, pdfFile);
try {
const response = await parseFile.save({
progress: (progressValue, loaded, total, { type }) => {
if (type === "upload" && progressValue !== null) {
const percentCompleted = Math.round((loaded * 100) / total);
setpercentage(percentCompleted);
}
}
});
// The response object will contain information about the uploaded file
// You can access the URL of the uploaded file using response.url()
setFileUpload(response.url());
setfileload(false);
if (response.url()) {
if (extUserData?.TenantId?.ActiveFileAdapter) {
const base64 = await toDataUrl(file);
const fileBase64 = base64.split(",").pop();
const ext = file?.name?.split(".").pop();
const fileRes = await saveToCustomFile(fileBase64, `${name}.${ext}`);
if (fileRes.url) {
setFileUpload(fileRes?.url);
const tenantId = localStorage.getItem("TenantId");
const title = generateTitleFromFilename(file.name);
setFormData((obj) => ({ ...obj, Name: title }));
SaveFileSize(size, response.url(), tenantId);
return response.url();
SaveFileSize(size, fileRes.url, tenantId);
}
} catch (error) {
setfileload(false);
setpercentage(0);
console.error("Error uploading file:", error);
} else {
const parseFile = new Parse.File(name, pdfFile);
try {
const response = await parseFile.save({
progress: (progressValue, loaded, total, { type }) => {
if (type === "upload" && progressValue !== null) {
const percentCompleted = Math.round((loaded * 100) / total);
setpercentage(percentCompleted);
}
}
});
// The response object will contain information about the uploaded file
// You can access the URL of the uploaded file using response.url()
setFileUpload(response.url());
setfileload(false);
if (response.url()) {
const tenantId = localStorage.getItem("TenantId");
const title = generateTitleFromFilename(file.name);
setFormData((obj) => ({ ...obj, Name: title }));
SaveFileSize(size, response.url(), tenantId);
return response.url();
}
} catch (error) {
setfileload(false);
setpercentage(0);
console.error("Error uploading file:", error);
}
}
};
async function uriToBase64(uri) {
const response = await fetch(uri);
const blob = await response.blob();
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsDataURL(blob); // Convert to Base64
});
}
const dropboxSuccess = async (files) => {
setfileload(true);
const file = files[0];
@@ -371,30 +458,49 @@ const Forms = (props) => {
return;
} else {
const name = generatePdfName(16);
const parseFile = new Parse.File(name, { uri: url });
try {
const response = await parseFile.save({
progress: (progressValue, loaded, total, { type }) => {
if (type === "upload" && progressValue !== null) {
const percentCompleted = Math.round((loaded * 100) / total);
setpercentage(percentCompleted);
}
}
});
setFileUpload(response.url());
setfileload(false);
if (response.url()) {
const tenantId = localStorage.getItem("TenantId");
const title = generateTitleFromFilename(file.name);
if (extUserData?.TenantId?.ActiveFileAdapter) {
const base64 = await uriToBase64(url);
const fileBase64 = base64.split(",").pop();
const ext = file?.name?.split(".").pop();
const fileRes = await saveToCustomFile(fileBase64, `${name}.${ext}`);
if (fileRes.url) {
setFileUpload(fileRes.url);
setfileload(false);
setpercentage(0);
const title = generateTitleFromFilename(files?.[0]?.name);
setFormData((obj) => ({ ...obj, Name: title }));
SaveFileSize(size, response.url(), tenantId);
return response.url();
const tenantId = localStorage.getItem("TenantId");
SaveFileSize(size, fileRes.url, tenantId);
return fileRes.url;
} else {
setfileload(false);
setpercentage(0);
}
} else {
const parseFile = new Parse.File(name, { uri: url });
try {
const response = await parseFile.save({
progress: (progressValue, loaded, total, { type }) => {
if (type === "upload" && progressValue !== null) {
const percentCompleted = Math.round((loaded * 100) / total);
setpercentage(percentCompleted);
}
}
});
setFileUpload(response.url());
setfileload(false);
if (response.url()) {
const tenantId = localStorage.getItem("TenantId");
const title = generateTitleFromFilename(file.name);
setFormData((obj) => ({ ...obj, Name: title }));
SaveFileSize(size, response.url(), tenantId);
return response.url();
}
} catch (error) {
setfileload(false);
setpercentage(0);
console.error("Error uploading file:", error);
}
} catch (error) {
setfileload(false);
setpercentage(0);
console.error("Error uploading file:", error);
}
}
};
@@ -447,6 +553,9 @@ const Forms = (props) => {
className: "contracts_Users",
objectId: ExtCls[0].objectId
});
if (extUserData?.TenantId?.ActiveFileAdapter) {
object.set("IsFileAdapter", true);
}
const res = await object.save();
if (res) {
setSigners([]);
@@ -550,27 +659,55 @@ const Forms = (props) => {
});
setIsDecrypting(false);
// Upload the file to Parse Server
const parseFile = new Parse.File(name, pdfFile, "application/pdf");
await parseFile.save({
progress: (progressValue, loaded, total, { type }) => {
if (type === "upload" && progressValue !== null) {
const percentCompleted = Math.round((loaded * 100) / total);
setpercentage(percentCompleted);
if (extUserData?.TenantId?.ActiveFileAdapter) {
const base64 = await toDataUrl(pdfFile);
const fileBase64 = base64.split(",").pop();
const ext = formData?.file?.name?.split(".").pop();
const fileRes = await saveToCustomFile(fileBase64, `${name}.${ext}`);
if (fileRes.url) {
setFormData((prev) => ({ ...prev, password: "" }));
setFileUpload(fileRes?.url);
setfileload(false);
setpercentage(0);
const title = generateTitleFromFilename(formData?.file?.name);
setFormData((obj) => ({ ...obj, Name: title }));
const tenantId = localStorage.getItem("TenantId");
SaveFileSize(size, fileRes.url, tenantId);
return fileRes.url;
} else {
setfileload(false);
setpercentage(0);
}
} else {
const parseFile = new Parse.File(name, pdfFile, "application/pdf");
await parseFile.save({
progress: (progressValue, loaded, total, { type }) => {
if (type === "upload" && progressValue !== null) {
const percentCompleted = Math.round((loaded * 100) / total);
setpercentage(percentCompleted);
}
}
});
// Retrieve the URL of the uploaded file
if (parseFile.url()) {
setFormData((prev) => ({ ...prev, password: "" }));
setFileUpload(parseFile.url());
setfileload(false);
setpercentage(0);
const title = generateTitleFromFilename(formData?.file?.name);
setFormData((obj) => ({ ...obj, Name: title }));
const tenantId = localStorage.getItem("TenantId");
SaveFileSize(size, parseFile.url(), tenantId);
return parseFile.url();
} else {
setfileload(false);
setpercentage(0);
setFormData((prev) => ({ ...prev, password: "" }));
setIsDecrypting(false);
if (inputFileRef.current) {
inputFileRef.current.value = ""; // Set file input value to empty string
}
}
});
// Retrieve the URL of the uploaded file
if (parseFile.url()) {
setFormData((prev) => ({ ...prev, password: "" }));
setFileUpload(parseFile.url());
setfileload(false);
const title = generateTitleFromFilename(formData?.file?.name);
setFormData((obj) => ({ ...obj, Name: title }));
const tenantId = localStorage.getItem("TenantId");
SaveFileSize(size, parseFile.url(), tenantId);
return parseFile.url();
}
} catch (err) {
setfileload(false);
+1 -1
View File
@@ -901,7 +901,7 @@ function PlaceHolderSign() {
useEffect(() => {
const timer = setTimeout(() => {
if (signerPos?.length > 0) {
if (signerPos?.length > 0 && !pdfDetails?.[0]?.IsCompleted) {
autosavedetails();
}
}, 2000);
+1 -1
View File
@@ -558,7 +558,7 @@ function SignYourSelf() {
};
useEffect(() => {
const timer = setTimeout(() => {
if (xyPostion?.length > 0) {
if (xyPostion?.length > 0 && !pdfDetails?.[0]?.IsCompleted) {
autosavedetails();
}
}, 2000);
@@ -34,11 +34,13 @@ function DownloadPdfZip(props) {
try {
// Fetch the first PDF (Signed Document)
const docId =
props.isDocId && !pdfDetails?.[0]?.IsEnableOTP
? pdfDetails?.[0]?.objectId
: "";
const signedUrl = await getSignedUrl(pdfUrl, docId);
const docId = pdfDetails?.[0]?.objectId || "";
const isFileAdapter = pdfDetails?.[0]?.IsFileAdapter
? pdfDetails?.[0]?.IsFileAdapter
: false;
console.log("pdfDetails?.[0] ", pdfDetails?.[0]);
const signedUrl = await getSignedUrl(pdfUrl, docId, isFileAdapter);
console.log("signedUrl ", signedUrl);
const pdf1Response = await fetch(signedUrl);
if (!pdf1Response.ok) {
throw new Error(`Failed to fetch PDF: ${signedUrl}`);
@@ -671,12 +671,21 @@ const ReportTable = (props) => {
const url = item?.SignedUrl || item?.URL || "";
const pdfName = item?.Name || "exported_file";
const isCompleted = item?.IsCompleted || false;
const templateId = props?.ReportName === "Templates" && item.objectId;
const docId = props?.ReportName !== "Templates" && item.objectId;
const isFileAdapter = item?.IsFileAdapter ? item?.IsFileAdapter : false;
if (url) {
try {
if (isCompleted) {
setIsDownloadModal({ [item.objectId]: true });
} else {
const signedUrl = await getSignedUrl(url);
const signedUrl = await getSignedUrl(
url,
docId,
isFileAdapter,
templateId
);
await fetchUrl(signedUrl, pdfName);
}
setActLoader({});
+6
View File
@@ -70,6 +70,9 @@ import updateContactTour from './parsefunction/updateContactTour.js';
import declinedocument from './parsefunction/declinedocument.js';
import addcustomsmtp from './parsefunction/addcustomsmtp.js';
import deactivateMailAdapter from './parsefunction/deactivateMailAdapter.js';
import addFileAdapter from './parsefunction/addFileAdapter.js';
import saveToFileAdapter from './parsefunction/saveToFileAdapter.js';
import getFileAdapter from './parsefunction/getFileAdapter.js';
// This afterSave function triggers after an object is added or updated in the specified class, allowing for post-processing logic.
Parse.Cloud.afterSave('contracts_Document', DocumentAftersave);
@@ -152,3 +155,6 @@ Parse.Cloud.define('updatecontacttour', updateContactTour);
Parse.Cloud.define('declinedoc', declinedocument);
Parse.Cloud.define('addsmtp', addcustomsmtp);
Parse.Cloud.define('deactivatemailadapter', deactivateMailAdapter);
Parse.Cloud.define('savetofileadapter', saveToFileAdapter);
Parse.Cloud.define('addfileadapter', addFileAdapter);
Parse.Cloud.define('getfileadapter', getFileAdapter);
@@ -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;
@@ -0,0 +1,21 @@
/**
*
* @param {Parse} Parse
*/
exports.up = async Parse => {
const className = 'contracts_Document';
const schema = new Parse.Schema(className);
schema.addBoolean('IsFileAdapter');
return schema.update();
};
/**
*
* @param {Parse} Parse
*/
exports.down = async Parse => {
const className = 'contracts_Document';
const schema = new Parse.Schema(className);
schema.deleteField('IsFileAdapter');
return schema.update();
};
+621 -665
View File
File diff suppressed because it is too large Load Diff
+6 -5
View File
@@ -18,6 +18,8 @@
"watch": "nodemon index.js"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.664.0",
"@aws-sdk/s3-request-presigner": "^3.664.0",
"@parse/fs-files-adapter": "^3.0.0",
"@parse/s3-files-adapter": "^2.2.0",
"@pdf-lib/fontkit": "^1.1.1",
@@ -32,7 +34,6 @@
"form-data": "^4.0.0",
"generate-api-key": "^1.0.2",
"googleapis": "^144.0.0",
"jsonschema": "^1.4.1",
"mailgun.js": "^10.2.3",
"mongoose": "^8.7.0",
"multer": "^1.4.5-lts.1",
@@ -41,7 +42,7 @@
"nodemailer": "^6.9.15",
"parse": "^5.3.0",
"parse-dbtool": "^1.2.0",
"parse-server": "^7.3.0-alpha.8",
"parse-server": "^7.3.0",
"parse-server-api-mail-adapter": "^4.0.0",
"pdf-lib": "^1.17.1",
"posthog-node": "^4.2.0",
@@ -49,10 +50,10 @@
},
"type": "module",
"devDependencies": {
"@babel/eslint-parser": "^7.25.1",
"@babel/eslint-parser": "^7.25.7",
"eslint": "^9.11.1",
"jasmine": "^5.3.0",
"mongodb-runner": "^5.6.6",
"jasmine": "^5.3.1",
"mongodb-runner": "^5.7.0",
"nodemon": "^3.1.7",
"nyc": "^17.1.0",
"prettier": "^3.3.3"