mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-08-17 21:25:54 +02:00
feat: enable custom file storage for document siging
This commit is contained in:
Generated
+553
-558
File diff suppressed because it is too large
Load Diff
@@ -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() === "") {
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -901,7 +901,7 @@ function PlaceHolderSign() {
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
if (signerPos?.length > 0) {
|
||||
if (signerPos?.length > 0 && !pdfDetails?.[0]?.IsCompleted) {
|
||||
autosavedetails();
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
@@ -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({});
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
Generated
+621
-665
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user