mirror of
https://github.com/OpenSignLabs/OpenSign.git
synced 2026-09-04 16:58:05 +02:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7930a8f439 | ||
|
|
7204183542 | ||
|
|
5561673fdc | ||
|
|
0df44062d6 | ||
|
|
8983440763 | ||
|
|
5a28c0ed06 | ||
|
|
cbca22c308 |
@@ -12,12 +12,9 @@ function BorderResize(props) {
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
width: getHeight() || "14px",
|
||||
height: getHeight() || "14px"
|
||||
}}
|
||||
style={{ width: getHeight() || "14px", height: getHeight() || "14px" }}
|
||||
className={`${props.right ? `-right-[12px]` : "-right-[2px]"} ${
|
||||
props.top ? `-bottom-[12px]` : "-bottom-[2px] "
|
||||
props.top ? `-bottom-[12px]` : "-bottom-[2px]"
|
||||
} absolute inline-block hover:cursor-sw-resize border-r-[3px] border-b-[3px] border-[#188ae2]`}
|
||||
></div>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
|
||||
const Cell = ({
|
||||
isEnabled,
|
||||
count,
|
||||
h,
|
||||
value,
|
||||
@@ -15,10 +16,11 @@ const Cell = ({
|
||||
hint
|
||||
}) => (
|
||||
<div
|
||||
className="flex items-center justify-center border border-gray-800 bg-white"
|
||||
className={`${isEnabled ? "bg-white border-gray-400" : "select-none-cls pointer-events-none border-gray-500"} flex items-center justify-center border-[1px]`}
|
||||
style={{ flex: `0 0 ${100 / count}%`, height: h }}
|
||||
>
|
||||
<input
|
||||
disabled={!isEnabled}
|
||||
maxLength={1}
|
||||
value={value}
|
||||
readOnly={!editable}
|
||||
@@ -27,7 +29,7 @@ const Cell = ({
|
||||
onKeyDown={editable ? (e) => onKeyDown && onKeyDown(e, index) : undefined}
|
||||
// trigger validation when leaving a cell
|
||||
onBlur={editable ? (e) => onBlur && onBlur(e, index) : undefined}
|
||||
className="w-full text-center focus:outline-none bg-transparent placeholder-gray-300"
|
||||
className={`${isEnabled ? "placeholder-gray-300" : "placeholder-gray-500"} w-full text-center focus:outline-none bg-transparent`}
|
||||
placeholder={hint}
|
||||
style={{ fontFamily: "Arial, sans-serif", fontSize, color: fontColor }}
|
||||
/>
|
||||
@@ -35,6 +37,7 @@ const Cell = ({
|
||||
);
|
||||
|
||||
export default function CellsWidget({
|
||||
isEnabled,
|
||||
count = 8,
|
||||
height = 40,
|
||||
value = "",
|
||||
@@ -120,6 +123,7 @@ export default function CellsWidget({
|
||||
{cells.map((val, i) => (
|
||||
<Cell
|
||||
key={i}
|
||||
isEnabled={isEnabled}
|
||||
count={cellCount}
|
||||
h={height}
|
||||
value={val}
|
||||
|
||||
@@ -2,7 +2,10 @@ import React, { useRef, useState } from "react";
|
||||
import PrevNext from "./PrevNext";
|
||||
import {
|
||||
base64ToArrayBuffer,
|
||||
decryptPdf,
|
||||
deletePdfPage,
|
||||
flattenPdf,
|
||||
getFileAsArrayBuffer,
|
||||
handleDownloadCertificate,
|
||||
handleDownloadPdf,
|
||||
handleRemoveWidgets,
|
||||
@@ -83,7 +86,42 @@ function Header(props) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const uploadedPdfBytes = await file.arrayBuffer();
|
||||
let uploadedPdfBytes = await file.arrayBuffer();
|
||||
try {
|
||||
uploadedPdfBytes = await flattenPdf(uploadedPdfBytes);
|
||||
} catch (err) {
|
||||
if (err?.message?.includes("is encrypted")) {
|
||||
try {
|
||||
const pdfFile = await decryptPdf(file, "");
|
||||
const pdfArrayBuffer = await getFileAsArrayBuffer(pdfFile);
|
||||
uploadedPdfBytes = await flattenPdf(pdfArrayBuffer);
|
||||
} catch (err) {
|
||||
if (err?.response?.status === 401) {
|
||||
const password = prompt(
|
||||
`PDF "${file.name}" is password-protected. Enter password:`
|
||||
);
|
||||
if (password) {
|
||||
try {
|
||||
const pdfFile = await decryptPdf(file, password);
|
||||
const pdfArrayBuffer = await getFileAsArrayBuffer(pdfFile);
|
||||
uploadedPdfBytes = await flattenPdf(pdfArrayBuffer);
|
||||
// Upload the file to Parse Server
|
||||
} catch (err) {
|
||||
console.error("Incorrect password or decryption failed", err);
|
||||
alert("Incorrect password or decryption failed.");
|
||||
}
|
||||
} else {
|
||||
alert("Please provided Password.");
|
||||
}
|
||||
} else {
|
||||
console.log("Err ", err);
|
||||
alert("error while uploading pdf.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
alert("error while uploading pdf.");
|
||||
}
|
||||
}
|
||||
const uploadedPdfDoc = await PDFDocument.load(uploadedPdfBytes, {
|
||||
ignoreEncryption: true
|
||||
});
|
||||
@@ -341,28 +379,28 @@ function Header(props) {
|
||||
</span>
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => setIsDeletePage(true)}
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
<i className="fa-light fa-trash text-gray-500 2xl:text-[30px] mr-[3px]"></i>
|
||||
<span className="font-[500]">
|
||||
{t("delete-page")}
|
||||
</span>
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => setIsReorderModal(true)}
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
<i className="fa-light fa-list-ol text-gray-500 2xl:text-[30px] mr-[3px]"></i>
|
||||
<span className="font-[500]">
|
||||
{t("reorder-pages")}
|
||||
</span>
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => setIsDeletePage(true)}
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
<i className="fa-light fa-trash text-gray-500 2xl:text-[30px] mr-[3px]"></i>
|
||||
<span className="font-[500]">
|
||||
{t("delete-page")}
|
||||
</span>
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
onClick={() => setIsReorderModal(true)}
|
||||
>
|
||||
<div className="flex flex-row">
|
||||
<i className="fa-light fa-list-ol text-gray-500 2xl:text-[30px] mr-[3px]"></i>
|
||||
<span className="font-[500]">
|
||||
{t("reorder-pages")}
|
||||
</span>
|
||||
</div>
|
||||
</DropdownMenu.Item>
|
||||
|
||||
<DropdownMenu.Item
|
||||
className="DropdownMenuItem"
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import React, { useRef, useState } from "react";
|
||||
import { useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
base64ToArrayBuffer,
|
||||
decryptPdf,
|
||||
deletePdfPage,
|
||||
flattenPdf,
|
||||
getFileAsArrayBuffer,
|
||||
handleRemoveWidgets,
|
||||
reorderPdfPages
|
||||
} from "../../constant/Utils";
|
||||
@@ -70,7 +73,42 @@ function PdfZoom(props) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const uploadedPdfBytes = await file.arrayBuffer();
|
||||
let uploadedPdfBytes = await file.arrayBuffer();
|
||||
try {
|
||||
uploadedPdfBytes = await flattenPdf(uploadedPdfBytes);
|
||||
} catch (err) {
|
||||
if (err?.message?.includes("is encrypted")) {
|
||||
try {
|
||||
const pdfFile = await decryptPdf(file, "");
|
||||
const pdfArrayBuffer = await getFileAsArrayBuffer(pdfFile);
|
||||
uploadedPdfBytes = await flattenPdf(pdfArrayBuffer);
|
||||
} catch (err) {
|
||||
if (err?.response?.status === 401) {
|
||||
const password = prompt(
|
||||
`PDF "${file.name}" is password-protected. Enter password:`
|
||||
);
|
||||
if (password) {
|
||||
try {
|
||||
const pdfFile = await decryptPdf(file, password);
|
||||
const pdfArrayBuffer = await getFileAsArrayBuffer(pdfFile);
|
||||
uploadedPdfBytes = await flattenPdf(pdfArrayBuffer);
|
||||
// Upload the file to Parse Server
|
||||
} catch (err) {
|
||||
console.error("Incorrect password or decryption failed", err);
|
||||
alert("Incorrect password or decryption failed.");
|
||||
}
|
||||
} else {
|
||||
alert("Please provided Password.");
|
||||
}
|
||||
} else {
|
||||
console.log("Err ", err);
|
||||
alert("error while uploading pdf.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
alert("error while uploading pdf.");
|
||||
}
|
||||
}
|
||||
const uploadedPdfDoc = await PDFDocument.load(uploadedPdfBytes, {
|
||||
ignoreEncryption: true
|
||||
});
|
||||
|
||||
@@ -740,7 +740,7 @@ function Placeholder(props) {
|
||||
return "not-allowed";
|
||||
}
|
||||
} else {
|
||||
return "all-scroll";
|
||||
return "move";
|
||||
}
|
||||
};
|
||||
|
||||
@@ -946,23 +946,24 @@ function Placeholder(props) {
|
||||
props.pos.key === props?.currWidgetsDetails?.key && <BorderResize />
|
||||
)}
|
||||
|
||||
{/* 1- Show a ouline if props.pos.key === props?.currWidgetsDetails?.key, indicating the current user's selected widget.
|
||||
2- If props.isShowBorder is true, display ouline for all widgets.
|
||||
3- Use the combination of props?.isAlllowModify and !props?.assignedWidgetId.includes(props.pos.key) to determine when to show ouline:
|
||||
3.1- When isAlllowModify is true, show ouline.
|
||||
3.2- Do not display ouline for widgets already assigned (props.assignedWidgetId.includes(props.pos.key) is true).
|
||||
{/* 1- Show a border if props.pos.key === props?.currWidgetsDetails?.key, indicating the current user's selected widget.
|
||||
2- If props.isShowBorder is true, display border for all widgets.
|
||||
3- Use the combination of props?.isAlllowModify and !props?.assignedWidgetId.includes(props.pos.key) to determine when to show border:
|
||||
3.1- When isAlllowModify is true, show border.
|
||||
3.2- Do not display border for widgets already assigned (props.assignedWidgetId.includes(props.pos.key) is true).
|
||||
*/}
|
||||
{props.pos.key === props?.currWidgetsDetails?.key &&
|
||||
(props.isShowBorder ||
|
||||
(props?.isAlllowModify &&
|
||||
!props?.assignedWidgetId.includes(props.pos.key))) && (
|
||||
<div
|
||||
style={{ borderColor: themeColor }}
|
||||
className="w-[calc(100%+21px)] h-[calc(100%+21px)] cursor-move absolute inline-block border-[1px] border-dashed"
|
||||
></div>
|
||||
)}
|
||||
<div
|
||||
className={`${
|
||||
props.pos.key === props?.currWidgetsDetails?.key &&
|
||||
(props.isShowBorder ||
|
||||
(props?.isAlllowModify &&
|
||||
!props?.assignedWidgetId.includes(props.pos.key)))
|
||||
? "outline-[0.3px] outline-dashed outline-offset-[10px]"
|
||||
: ""
|
||||
} flex items-stretch justify-center`}
|
||||
className="flex items-stretch justify-center"
|
||||
style={{
|
||||
outlineColor: themeColor,
|
||||
left: xPos(props.pos, props.isSignYourself),
|
||||
top: yPos(props.pos, props.isSignYourself),
|
||||
width: "100%",
|
||||
|
||||
@@ -226,6 +226,7 @@ function PlaceholderType(props) {
|
||||
props.isPlaceholder || props.isSignYourself || props.isSelfSign;
|
||||
return (
|
||||
<CellsWidget
|
||||
isEnabled={iswidgetEnable}
|
||||
count={count}
|
||||
height={height}
|
||||
value={cells.join("")}
|
||||
|
||||
@@ -3,7 +3,12 @@ import { useTranslation } from "react-i18next";
|
||||
import { Document, Page } from "react-pdf";
|
||||
import { useSelector } from "react-redux";
|
||||
import { PDFDocument } from "pdf-lib";
|
||||
import { base64ToArrayBuffer } from "../../constant/Utils";
|
||||
import {
|
||||
base64ToArrayBuffer,
|
||||
decryptPdf,
|
||||
flattenPdf,
|
||||
getFileAsArrayBuffer
|
||||
} from "../../constant/Utils";
|
||||
import { maxFileSize } from "../../constant/const";
|
||||
|
||||
function RenderAllPdfPage(props) {
|
||||
@@ -87,7 +92,42 @@ function RenderAllPdfPage(props) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const uploadedPdfBytes = await file.arrayBuffer();
|
||||
let uploadedPdfBytes = await file.arrayBuffer();
|
||||
try {
|
||||
uploadedPdfBytes = await flattenPdf(uploadedPdfBytes);
|
||||
} catch (err) {
|
||||
if (err?.message?.includes("is encrypted")) {
|
||||
try {
|
||||
const pdfFile = await decryptPdf(file, "");
|
||||
const pdfArrayBuffer = await getFileAsArrayBuffer(pdfFile);
|
||||
uploadedPdfBytes = await flattenPdf(pdfArrayBuffer);
|
||||
} catch (err) {
|
||||
if (err?.response?.status === 401) {
|
||||
const password = prompt(
|
||||
`PDF "${file.name}" is password-protected. Enter password:`
|
||||
);
|
||||
if (password) {
|
||||
try {
|
||||
const pdfFile = await decryptPdf(file, password);
|
||||
const pdfArrayBuffer = await getFileAsArrayBuffer(pdfFile);
|
||||
uploadedPdfBytes = await flattenPdf(pdfArrayBuffer);
|
||||
// Upload the file to Parse Server
|
||||
} catch (err) {
|
||||
console.error("Incorrect password or decryption failed", err);
|
||||
alert("Incorrect password or decryption failed.");
|
||||
}
|
||||
} else {
|
||||
alert("Please provided Password.");
|
||||
}
|
||||
} else {
|
||||
console.log("Err ", err);
|
||||
alert("error while uploading pdf.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
alert("error while uploading pdf.");
|
||||
}
|
||||
}
|
||||
const uploadedPdfDoc = await PDFDocument.load(uploadedPdfBytes, {
|
||||
ignoreEncryption: true
|
||||
});
|
||||
|
||||
@@ -123,6 +123,23 @@ const WidgetNameModal = (props) => {
|
||||
}
|
||||
};
|
||||
|
||||
const handleChangeValidateInput = (e) => {
|
||||
if (e) {
|
||||
if (e.target.value === "ssn") {
|
||||
setFormdata({
|
||||
...formdata,
|
||||
[e.target.name]: e.target.value,
|
||||
hint: "xxx-xx-xxxx",
|
||||
cellCount: 11
|
||||
});
|
||||
} else {
|
||||
setFormdata({ ...formdata, [e.target.name]: e.target.value });
|
||||
}
|
||||
} else {
|
||||
setFormdata({ ...formdata, textvalidate: "" });
|
||||
}
|
||||
};
|
||||
|
||||
const handledefaultChange = (e) => {
|
||||
if (formdata.textvalidate) {
|
||||
const regexObject = RegexParser(handleValidation(formdata.textvalidate));
|
||||
@@ -148,7 +165,7 @@ const WidgetNameModal = (props) => {
|
||||
//allow space in text regex
|
||||
return "/^[a-zA-Z ]+$/";
|
||||
case "ssn":
|
||||
return "^\\d{3}-\\d{2}-\\d{4}$";
|
||||
return "/^(?!000|666|9\\d{2})\\d{3}-(?!00)\\d{2}-(?!0000)\\d{4}$/";
|
||||
default:
|
||||
return type;
|
||||
}
|
||||
|
||||
@@ -694,17 +694,20 @@ function WidgetsValueModal(props) {
|
||||
}
|
||||
}
|
||||
if (isTab === "type") {
|
||||
//trim user name or typed name value to show in initial signature
|
||||
const trimmedName = typedSignature
|
||||
? typedSignature?.trim()
|
||||
: currentUserName?.trim();
|
||||
//get full name of user
|
||||
const fullUserName = typedSignature || currentUserName
|
||||
const firstCharacter = trimmedName?.charAt(0);
|
||||
const userName =
|
||||
currWidgetsDetails?.type === "initials"
|
||||
? firstCharacter
|
||||
: typedSignature;
|
||||
: fullUserName;
|
||||
const signatureValue = currWidgetsDetails?.typeSignature;
|
||||
setTypedSignature(signatureValue || userName || "");
|
||||
convertToImg(fontSelect, userName);
|
||||
convertToImg(fontSelect, signatureValue || userName || "");
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isTab]);
|
||||
@@ -1472,6 +1475,7 @@ function WidgetsValueModal(props) {
|
||||
case cellsWidget:
|
||||
return (
|
||||
<CellsWidget
|
||||
isEnabled={true}
|
||||
count={cellsValue.length}
|
||||
height="100%"
|
||||
value={cellsValue.join("")}
|
||||
@@ -1762,6 +1766,10 @@ function WidgetsValueModal(props) {
|
||||
regexValidation = /^[0-9\s]*$/;
|
||||
validateExpression(regexValidation);
|
||||
break;
|
||||
case "ssn":
|
||||
regexValidation = /^(?!000|666|9\d{2})\d{3}-(?!00)\d{2}-(?!0000)\d{4}$/;
|
||||
validateExpression(regexValidation);
|
||||
break;
|
||||
default:
|
||||
// Grab the current pattern (if it exists)
|
||||
const pattern = currWidgetsDetails?.options?.validation?.pattern;
|
||||
@@ -1872,6 +1880,11 @@ function WidgetsValueModal(props) {
|
||||
}
|
||||
};
|
||||
const handleclose = () => {
|
||||
// If validation is shown, clear the response and close the modal
|
||||
if (isShowValidation) {
|
||||
handleClear();
|
||||
setIsShowValidation(false);
|
||||
}
|
||||
dispatch(setIsShowModal({}));
|
||||
dispatch(setLastIndex(""));
|
||||
if (currWidgetsDetails?.type === textWidget && uniqueId) {
|
||||
@@ -1936,7 +1949,7 @@ function WidgetsValueModal(props) {
|
||||
<>
|
||||
<ModalUi
|
||||
isOpen={true}
|
||||
handleClose={() => !isShowValidation && handleclose()}
|
||||
handleClose={() => handleclose()}
|
||||
position="bottom"
|
||||
>
|
||||
<div className="h-[100%] p-[18px]">
|
||||
|
||||
@@ -98,6 +98,27 @@ export const getSecureUrl = async (url) => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes a trailing path segment from a URL string, if present.
|
||||
*
|
||||
* @param {string} url - The original URL.
|
||||
* @param {string} segment - The segment to strip off (default: "app").
|
||||
* @returns {string} - The URL with the trailing segment removed, or unmodified if it didn’t match.
|
||||
*/
|
||||
export function removeTrailingSegment(url, segment = "app") {
|
||||
// Normalize a trailing slash (e.g. “/app/” → “/app”)
|
||||
const normalized = url.endsWith("/") ? url.slice(0, -1) : url;
|
||||
|
||||
const lastSlash = normalized.lastIndexOf("/");
|
||||
const lastPart = normalized.slice(lastSlash + 1);
|
||||
|
||||
if (lastPart === segment) {
|
||||
return normalized.slice(0, lastSlash);
|
||||
}
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
export const color = [
|
||||
"#93a3db",
|
||||
"#e6c3db",
|
||||
@@ -3315,7 +3336,7 @@ export const getOriginalWH = async (pdf) => {
|
||||
};
|
||||
|
||||
//function is used to check required and optional widgets and ensure required widget should be response
|
||||
export const handleCheckResponse = (checkUser,setminRequiredCount) => {
|
||||
export const handleCheckResponse = (checkUser, setminRequiredCount) => {
|
||||
let checkboxExist,
|
||||
showAlert = false,
|
||||
widgetKey,
|
||||
@@ -3414,3 +3435,74 @@ export const handleCheckResponse = (checkUser,setminRequiredCount) => {
|
||||
showAlert
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* decryptPdf
|
||||
* @param {File} file - The password-protected PDF file to decrypt.
|
||||
* @param {string | undefined} password - The password used to unlock the PDF.
|
||||
* @returns {Promise<File>} - A Promise that resolves to a decrypted PDF as a File object.
|
||||
*
|
||||
*/
|
||||
export const decryptPdf = async (file, password) => {
|
||||
const name = generatePdfName(16);
|
||||
const baseApi = localStorage.getItem("baseUrl") || "";
|
||||
const url = removeTrailingSegment(baseApi) + "/decryptpdf?ts=" + Date.now();
|
||||
let formData = new FormData();
|
||||
formData.append("file", file);
|
||||
formData.append("password", password);
|
||||
const config = {
|
||||
headers: { "content-type": "multipart/form-data" },
|
||||
responseType: "blob"
|
||||
};
|
||||
const response = await axios.post(url, formData, config);
|
||||
const pdfBlob = new Blob([response.data], { type: "application/pdf" });
|
||||
return new File([pdfBlob], name, { type: "application/pdf" });
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a Base64 string to a File object.
|
||||
*
|
||||
* @param {string} base64String - The Base64 string, with or without the data URI prefix.
|
||||
* e.g. "data:image/png;base64,iVBORw0KGgoAAAANS…" or "iVBORw0KGgoAAAANS…"
|
||||
* @param {string} filename - Desired filename for the File object, e.g. "photo.png"
|
||||
* @returns {File} - The resulting File object
|
||||
*/
|
||||
export function base64ToFile(base64String, filename) {
|
||||
// Separate out the mime-type and the actual Base64 payload
|
||||
const [header, payload] = base64String.includes(",")
|
||||
? base64String.split(",")
|
||||
: [null, base64String];
|
||||
// Determine the MIME type (fallback to application/octet-stream)
|
||||
const mimeMatch = header?.match(/data:(.*?);base64/);
|
||||
const mime = mimeMatch ? mimeMatch[1] : "application/octet-stream";
|
||||
|
||||
// Decode Base64 to raw binary data held in a string
|
||||
const binaryString = atob(payload);
|
||||
// Create an ArrayBuffer and a view (as unsigned 8-bit)
|
||||
const len = binaryString.length;
|
||||
const u8arr = new Uint8Array(len);
|
||||
for (let i = 0; i < len; i++) {
|
||||
u8arr[i] = binaryString.charCodeAt(i);
|
||||
}
|
||||
|
||||
// Create a File object (Blob subclass) with the binary data
|
||||
return new File([u8arr], filename, { type: mime });
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the given File object and returns its contents as an ArrayBuffer.
|
||||
*
|
||||
* @param {File} file
|
||||
* The File instance to be read.
|
||||
* @returns {Promise<ArrayBuffer>}
|
||||
* A promise that resolves with the file’s binary data as an ArrayBuffer,
|
||||
* or rejects with an error if the read fails.
|
||||
*/
|
||||
export function getFileAsArrayBuffer(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => resolve(e.target.result);
|
||||
reader.onerror = (e) => reject(e.target.error);
|
||||
reader.readAsArrayBuffer(file);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -12,9 +12,12 @@ import {
|
||||
flattenPdf,
|
||||
generatePdfName,
|
||||
generateTitleFromFilename,
|
||||
getFileName,
|
||||
getSecureUrl,
|
||||
toDataUrl
|
||||
toDataUrl,
|
||||
decryptPdf,
|
||||
base64ToFile,
|
||||
getFileAsArrayBuffer,
|
||||
removeTrailingSegment
|
||||
} from "../constant/Utils";
|
||||
import { PDFDocument } from "pdf-lib";
|
||||
import axios from "axios";
|
||||
@@ -115,15 +118,6 @@ const Forms = (props) => {
|
||||
}));
|
||||
};
|
||||
|
||||
function getFileAsArrayBuffer(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => resolve(e.target.result);
|
||||
reader.onerror = (e) => reject(e.target.error);
|
||||
reader.readAsArrayBuffer(file);
|
||||
});
|
||||
}
|
||||
|
||||
// `removeFile` is used to reset progress, percentage and remove file if exists
|
||||
const removeFile = (e) => {
|
||||
setfileload(false);
|
||||
@@ -136,12 +130,13 @@ const Forms = (props) => {
|
||||
setpercentage(0);
|
||||
try {
|
||||
const files = Array.from(e.target.files);
|
||||
setSelectedFiles(files.map((f) => f.name));
|
||||
const filesNameArr = files.map((f) => f.name);
|
||||
setSelectedFiles(filesNameArr);
|
||||
if (!files.length) {
|
||||
alert(t("file-alert-2"));
|
||||
return;
|
||||
}
|
||||
setFormData((prev) => ({ ...prev, file: files[0] }));
|
||||
// setFormData((prev) => ({ ...prev, file: files[0] }));
|
||||
const totalMb = Math.round(
|
||||
files.reduce((sum, f) => sum + f.size, 0) / Math.pow(1024, 2)
|
||||
);
|
||||
@@ -155,10 +150,74 @@ const Forms = (props) => {
|
||||
|
||||
const pdfBuffers = [];
|
||||
for (const file of files) {
|
||||
setFormData((prev) => ({ ...prev, file: file }));
|
||||
if (file.type === "application/pdf") {
|
||||
const buffer = await getFileAsArrayBuffer(file);
|
||||
const flat = await flattenPdf(buffer);
|
||||
pdfBuffers.push(flat);
|
||||
try {
|
||||
const buffer = await getFileAsArrayBuffer(file);
|
||||
const flat = await flattenPdf(buffer);
|
||||
pdfBuffers.push(flat);
|
||||
} catch (err) {
|
||||
if (err?.message?.includes("is encrypted")) {
|
||||
try {
|
||||
setIsDecrypting(true);
|
||||
const pdfFile = await decryptPdf(file, "");
|
||||
setIsDecrypting(false);
|
||||
setfileload(true);
|
||||
const res = await getFileAsArrayBuffer(pdfFile);
|
||||
const flatPdf = await flattenPdf(res);
|
||||
// Upload the file to Parse Server
|
||||
pdfBuffers.push(flatPdf);
|
||||
} catch (err) {
|
||||
removeFile(e);
|
||||
if (err?.response?.status === 401) {
|
||||
// setIsPassword(true);
|
||||
const password = prompt(
|
||||
`PDF "${file.name}" is password-protected. Enter password:`
|
||||
);
|
||||
|
||||
if (password) {
|
||||
try {
|
||||
const pdfFile = await decryptPdf(file, password);
|
||||
setIsDecrypting(false);
|
||||
setfileload(true);
|
||||
const res = await getFileAsArrayBuffer(pdfFile);
|
||||
const flatPdf = await flattenPdf(res);
|
||||
// Upload the file to Parse Server
|
||||
pdfBuffers.push(flatPdf);
|
||||
} catch (err) {
|
||||
console.error(
|
||||
"Incorrect password or decryption failed",
|
||||
err
|
||||
);
|
||||
setSelectedFiles(
|
||||
filesNameArr.filter((f) => f !== file.name)
|
||||
);
|
||||
setIsDecrypting(false);
|
||||
setfileload(false);
|
||||
removeFile(e);
|
||||
alert(`Incorrect password for file: ${file.name}`);
|
||||
}
|
||||
} else {
|
||||
console.error("password not provided");
|
||||
setSelectedFiles(
|
||||
filesNameArr.filter((f) => f !== file.name)
|
||||
);
|
||||
setIsDecrypting(false);
|
||||
setfileload(false);
|
||||
removeFile(e);
|
||||
}
|
||||
} else {
|
||||
console.log("Error uploading file: ", err?.response);
|
||||
setIsDecrypting(false);
|
||||
e.target.value = "";
|
||||
removeFile(e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log("err ", err);
|
||||
removeFile(e);
|
||||
}
|
||||
}
|
||||
} else if (file.type.includes("image/")) {
|
||||
const image = await toDataUrl(file);
|
||||
const pdfDoc = await PDFDocument.create();
|
||||
@@ -182,7 +241,7 @@ const Forms = (props) => {
|
||||
) {
|
||||
try {
|
||||
const baseApi = localStorage.getItem("baseUrl") || "";
|
||||
const url = baseApi.replace("/app", "") + "docxtopdf";
|
||||
const url = removeTrailingSegment(baseApi) + "/docxtopdf";
|
||||
let fd = new FormData();
|
||||
fd.append("file", file);
|
||||
setfileload(true);
|
||||
@@ -240,17 +299,12 @@ const Forms = (props) => {
|
||||
const pdfBytes = await merged.save({ useObjectStreams: false });
|
||||
const name = generatePdfName(16);
|
||||
const pdfName = `${name}.pdf`;
|
||||
const fileAdapterId = extUserData?.TenantId?.ActiveFileAdapter || "";
|
||||
let uploadedUrl = "";
|
||||
if (fileAdapterId) {
|
||||
const base64 = await merged.saveAsBase64({ useObjectStreams: false });
|
||||
const fileBase64 = base64.split(",").pop();
|
||||
const fileRes = await saveToCustomFile(fileBase64, pdfName, fileAdapterId);
|
||||
if (fileRes.url) {
|
||||
uploadedUrl = fileRes.url;
|
||||
}
|
||||
} else {
|
||||
const parseFile = new Parse.File(pdfName, [...pdfBytes], "application/pdf");
|
||||
const parseFile = new Parse.File(
|
||||
pdfName,
|
||||
[...pdfBytes],
|
||||
"application/pdf"
|
||||
);
|
||||
const response = await parseFile.save({
|
||||
progress: (progressValue, loaded, total, { type }) => {
|
||||
if (type === "upload" && progressValue !== null) {
|
||||
@@ -265,14 +319,12 @@ const Forms = (props) => {
|
||||
uploadedUrl = fileRes.url;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (uploadedUrl) {
|
||||
const tenantId = localStorage.getItem("TenantId");
|
||||
SaveFileSize(pdfBytes.byteLength, uploadedUrl, tenantId);
|
||||
setFileUpload(uploadedUrl);
|
||||
setfileload(false);
|
||||
const title = generateTitleFromFilename(files[0].name);
|
||||
const title = generateTitleFromFilename(filesNameArr?.[0]);
|
||||
setFormData((obj) => ({ ...obj, Name: title }));
|
||||
removeFile(e);
|
||||
} else {
|
||||
@@ -382,9 +434,6 @@ const Forms = (props) => {
|
||||
className: "contracts_Users",
|
||||
objectId: ExtCls[0].objectId
|
||||
});
|
||||
if (extUserData?.TenantId?.ActiveFileAdapter) {
|
||||
object.set("FileAdapterId", extUserData?.TenantId?.ActiveFileAdapter);
|
||||
}
|
||||
const res = await object.save();
|
||||
if (res) {
|
||||
setSigners([]);
|
||||
@@ -509,25 +558,7 @@ const Forms = (props) => {
|
||||
try {
|
||||
const size = formData?.file?.size;
|
||||
const name = generatePdfName(16);
|
||||
const baseApi = localStorage.getItem("baseUrl") || "";
|
||||
const url = baseApi.replace("/app", "") + "decryptpdf";
|
||||
let Data = new FormData();
|
||||
Data.append("file", formData?.file);
|
||||
Data.append("password", formData.password);
|
||||
const config = {
|
||||
headers: {
|
||||
"content-type": "multipart/form-data"
|
||||
// sessiontoken: Parse.User.current().getSessionToken()
|
||||
},
|
||||
responseType: "blob"
|
||||
};
|
||||
const response = await axios.post(url, Data, config);
|
||||
const pdfBlob = new Blob([response.data], {
|
||||
type: "application/pdf"
|
||||
});
|
||||
const pdfFile = new File([pdfBlob], name, {
|
||||
type: "application/pdf"
|
||||
});
|
||||
const pdfFile = await decryptPdf(formData?.file, formData?.password);
|
||||
setIsDecrypting(false);
|
||||
const res = await getFileAsArrayBuffer(pdfFile);
|
||||
const flatPdf = await flattenPdf(res);
|
||||
|
||||
@@ -1608,13 +1608,7 @@ function PlaceHolderSign() {
|
||||
parseInt(defaultdata?.cellCount || 5)
|
||||
),
|
||||
validation:
|
||||
isSubscribe && inputype
|
||||
? {
|
||||
type: inputype,
|
||||
pattern:
|
||||
inputype === "regex" ? defaultdata.textvalidate : ""
|
||||
}
|
||||
: {},
|
||||
{},
|
||||
fontSize:
|
||||
fontSize || currWidgetsDetails?.options?.fontSize || 12,
|
||||
fontColor:
|
||||
@@ -2636,7 +2630,7 @@ function PlaceHolderSign() {
|
||||
title={t("document-alert")}
|
||||
showClose={false}
|
||||
>
|
||||
<div className="h-[100%] p-[20px]">
|
||||
<div className="h-[100%] p-[20px] text-base-content">
|
||||
<p>{isAlreadyPlace.message}</p>
|
||||
<div className="h-[1px] w-full my-[15px] bg-[#9f9f9f]"></div>
|
||||
<button
|
||||
|
||||
@@ -7,6 +7,7 @@ import axios from 'axios';
|
||||
dotenv.config();
|
||||
|
||||
export const cloudServerUrl = 'http://localhost:8080/app';
|
||||
export const serverAppId = process.env.APP_ID || 'opensign';
|
||||
export const appName = 'OpenSign™';
|
||||
|
||||
export const MAX_NAME_LENGTH = 250;
|
||||
|
||||
@@ -15,15 +15,18 @@ export const upload = multer({ storage });
|
||||
|
||||
export default async function decryptpdf(req, res) {
|
||||
const inputPath = req.file.path;
|
||||
const outputPath = './exports/out.pdf';
|
||||
const password = req.body.password || '';
|
||||
try {
|
||||
const file = fs.readFileSync(inputPath);
|
||||
const pdf = await Coherentpdf.fromMemory(file, password);
|
||||
await Coherentpdf.decryptPdf(pdf, password);
|
||||
await Coherentpdf.toFile(pdf, outputPath, false, false);
|
||||
const buffer = fs.readFileSync(outputPath);
|
||||
res.set('Content-Type', 'application/pdf');
|
||||
// Get decrypted buffer directly from memory (no file I/O)
|
||||
const buffer = await Coherentpdf.toMemory(pdf, false, false);
|
||||
res.set({
|
||||
'Content-Type': 'application/pdf',
|
||||
'Content-Disposition': 'inline; filename="decrypted.pdf"',
|
||||
'Content-Length': buffer.length,
|
||||
});
|
||||
res.send(buffer);
|
||||
fs.unlink(inputPath, () => {});
|
||||
} catch (err) {
|
||||
|
||||
@@ -3,7 +3,7 @@ import axios from 'axios';
|
||||
import multer from 'multer';
|
||||
import libre from 'libreoffice-convert';
|
||||
import util from 'node:util';
|
||||
import { cloudServerUrl, getSecureUrl } from '../../Utils.js';
|
||||
import { cloudServerUrl, getSecureUrl, serverAppId } from '../../Utils.js';
|
||||
|
||||
libre.convertAsync = util.promisify(libre.convert);
|
||||
|
||||
@@ -29,7 +29,7 @@ function generatePdfName(length) {
|
||||
|
||||
export default async function docxtopdf(req, res) {
|
||||
const serverUrl = cloudServerUrl;
|
||||
const appId = process.env.APP_ID;
|
||||
const appId = serverAppId;
|
||||
const masterKey = process.env.MASTER_KEY;
|
||||
const inputPath = req.file.path;
|
||||
const name = generatePdfName(16);
|
||||
|
||||
@@ -3,7 +3,7 @@ import multer from 'multer';
|
||||
import multerS3 from 'multer-s3';
|
||||
import aws from 'aws-sdk';
|
||||
import dotenv from 'dotenv';
|
||||
import { cloudServerUrl, useLocal } from '../../Utils.js';
|
||||
import { cloudServerUrl, serverAppId, useLocal } from '../../Utils.js';
|
||||
dotenv.config();
|
||||
|
||||
function sanitizeFileName(fileName) {
|
||||
@@ -50,7 +50,7 @@ async function uploadFile(req, res) {
|
||||
const DO_SPACE = process.env.DO_SPACE;
|
||||
|
||||
const parseBaseUrl = cloudServerUrl; //process.env.SERVER_URL;
|
||||
const parseAppId = process.env.APP_ID;
|
||||
const parseAppId = serverAppId;
|
||||
let fileStorage;
|
||||
if (useLocal === 'true') {
|
||||
fileStorage = multer.diskStorage({
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import axios from 'axios';
|
||||
import { cloudServerUrl } from '../../Utils.js';
|
||||
import { cloudServerUrl, serverAppId } from '../../Utils.js';
|
||||
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
|
||||
const APPID = process.env.APP_ID;
|
||||
const APPID = serverAppId;
|
||||
const masterKEY = process.env.MASTER_KEY;
|
||||
async function addTeamAndOrg(extUser) {
|
||||
try {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import axios from 'axios';
|
||||
import { cloudServerUrl } from '../../Utils.js';
|
||||
import { cloudServerUrl, serverAppId } from '../../Utils.js';
|
||||
async function AuthLoginAsMail(request) {
|
||||
try {
|
||||
//function for login user using user objectId without touching user's password
|
||||
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
|
||||
const APPID = process.env.APP_ID;
|
||||
const APPID = serverAppId;
|
||||
const masterKEY = process.env.MASTER_KEY;
|
||||
|
||||
let otpN = request.params.otp;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import axios from 'axios';
|
||||
import { appName, cloudServerUrl } from '../../Utils.js';
|
||||
import { appName, cloudServerUrl, serverAppId } from '../../Utils.js';
|
||||
|
||||
export default async function forwardDoc(request) {
|
||||
try {
|
||||
@@ -58,7 +58,7 @@ export default async function forwardDoc(request) {
|
||||
mailRes = await axios.post(`${cloudServerUrl}/functions/sendmailv3`, params, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Parse-Application-Id': process.env.APP_ID,
|
||||
'X-Parse-Application-Id': serverAppId,
|
||||
'X-Parse-Master-Key': process.env.MASTER_KEY,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import axios from 'axios';
|
||||
import { cloudServerUrl } from '../../Utils.js';
|
||||
import { cloudServerUrl, serverAppId } from '../../Utils.js';
|
||||
|
||||
export default async function GetTemplate(request) {
|
||||
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
|
||||
@@ -10,7 +10,7 @@ export default async function GetTemplate(request) {
|
||||
if (sessiontoken) {
|
||||
const userRes = await axios.get(serverUrl + '/users/me', {
|
||||
headers: {
|
||||
'X-Parse-Application-Id': process.env.APP_ID,
|
||||
'X-Parse-Application-Id': serverAppId,
|
||||
'X-Parse-Session-Token': sessiontoken,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import axios from 'axios';
|
||||
import { cloudServerUrl } from '../../Utils.js';
|
||||
const appId = process.env.APP_ID;
|
||||
import { cloudServerUrl, serverAppId } from '../../Utils.js';
|
||||
const appId = serverAppId;
|
||||
const masterkey = process.env.MASTER_KEY;
|
||||
export default async function createBatchContact(req) {
|
||||
if (!req?.user) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import axios from 'axios';
|
||||
import { cloudServerUrl, mailTemplate, replaceMailVaribles } from '../../Utils.js';
|
||||
import { cloudServerUrl, mailTemplate, replaceMailVaribles, serverAppId } from '../../Utils.js';
|
||||
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
|
||||
const appId = process.env.APP_ID;
|
||||
const appId = serverAppId;
|
||||
async function deductcount(docsCount, extUserId) {
|
||||
try {
|
||||
const extCls = new Parse.Object('contracts_Users');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import axios from 'axios';
|
||||
import { cloudServerUrl } from '../../Utils.js';
|
||||
import { cloudServerUrl, serverAppId } from '../../Utils.js';
|
||||
export default async function getDocument(request) {
|
||||
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
|
||||
const docId = request.params.docId;
|
||||
@@ -30,7 +30,7 @@ export default async function getDocument(request) {
|
||||
try {
|
||||
const userRes = await axios.get(serverUrl + '/users/me', {
|
||||
headers: {
|
||||
'X-Parse-Application-Id': process.env.APP_ID,
|
||||
'X-Parse-Application-Id': serverAppId,
|
||||
'X-Parse-Session-Token': sessiontoken,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import axios from 'axios';
|
||||
import { cloudServerUrl } from '../../Utils.js';
|
||||
import { cloudServerUrl, serverAppId } from '../../Utils.js';
|
||||
export default async function getDrive(request) {
|
||||
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
|
||||
const appId = process.env.APP_ID;
|
||||
const appId = serverAppId;
|
||||
const limit = request.params.limit;
|
||||
const skip = request.params.skip;
|
||||
const docId = request.params.docId;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { cloudServerUrl } from '../../Utils.js';
|
||||
import { cloudServerUrl, serverAppId } from '../../Utils.js';
|
||||
import reportJson from './reportsJson.js';
|
||||
import axios from 'axios';
|
||||
|
||||
@@ -14,7 +14,7 @@ export default async function getReport(request) {
|
||||
const searchTerm = request.params.searchTerm || '';
|
||||
|
||||
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
|
||||
const appId = process.env.APP_ID;
|
||||
const appId = serverAppId;
|
||||
const masterKey = process.env.MASTER_KEY;
|
||||
const sessionToken = request.headers['sessiontoken'] || request.headers['x-parse-session-token'];
|
||||
try {
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
saveFileUsage,
|
||||
getSecureUrl,
|
||||
appName,
|
||||
serverAppId,
|
||||
} from '../../../Utils.js';
|
||||
import GenerateCertificate from './GenerateCertificate.js';
|
||||
import { pdflibAddPlaceholder } from '@signpdf/placeholder-pdf-lib';
|
||||
@@ -15,7 +16,7 @@ import { SignPdf } from '@signpdf/signpdf';
|
||||
import { P12Signer } from '@signpdf/signer-p12';
|
||||
|
||||
const serverUrl = cloudServerUrl; // process.env.SERVER_URL;
|
||||
const APPID = process.env.APP_ID;
|
||||
const APPID = serverAppId;
|
||||
const masterKEY = process.env.MASTER_KEY;
|
||||
const eSignName = 'OpenSign';
|
||||
const eSigncontact = 'hello@opensignlabs.com';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import axios from 'axios';
|
||||
import { cloudServerUrl } from '../../Utils.js';
|
||||
import { cloudServerUrl, serverAppId } from '../../Utils.js';
|
||||
const serverUrl = cloudServerUrl; //process.env.SERVER_URL;
|
||||
const APPID = process.env.APP_ID;
|
||||
const APPID = serverAppId;
|
||||
const masterKEY = process.env.MASTER_KEY;
|
||||
|
||||
async function saveUser(userDetails) {
|
||||
|
||||
@@ -15,7 +15,7 @@ import AWS from 'aws-sdk';
|
||||
import { app as customRoute } from './cloud/customRoute/customApp.js';
|
||||
import { exec } from 'child_process';
|
||||
import { createTransport } from 'nodemailer';
|
||||
import { appName, cloudServerUrl, smtpenable, smtpsecure, useLocal } from './Utils.js';
|
||||
import { appName, cloudServerUrl, serverAppId, smtpenable, smtpsecure, useLocal } from './Utils.js';
|
||||
import { SSOAuth } from './auth/authadapter.js';
|
||||
import createContactIndex from './migrationdb/createContactIndex.js';
|
||||
import { validateSignedLocalUrl } from './cloud/parsefunction/getSignedUrl.js';
|
||||
@@ -97,7 +97,7 @@ export const config = {
|
||||
cloud: function () {
|
||||
import('./cloud/main.js');
|
||||
},
|
||||
appId: process.env.APP_ID || 'opensign',
|
||||
appId: serverAppId,
|
||||
logLevel: ['error'],
|
||||
maxLimit: 500,
|
||||
maxUploadSize: '30mb',
|
||||
@@ -234,8 +234,8 @@ if (!process.env.TESTING) {
|
||||
createContactIndex();
|
||||
|
||||
const migrate = isWindows
|
||||
? `set APPLICATION_ID=${process.env.APP_ID}&& set SERVER_URL=${cloudServerUrl}&& set MASTER_KEY=${process.env.MASTER_KEY}&& npx parse-dbtool migrate`
|
||||
: `APPLICATION_ID=${process.env.APP_ID} SERVER_URL=${cloudServerUrl} MASTER_KEY=${process.env.MASTER_KEY} npx parse-dbtool migrate`;
|
||||
? `set APPLICATION_ID=${serverAppId}&& set SERVER_URL=${cloudServerUrl}&& set MASTER_KEY=${process.env.MASTER_KEY}&& npx parse-dbtool migrate`
|
||||
: `APPLICATION_ID=${serverAppId} SERVER_URL=${cloudServerUrl} MASTER_KEY=${process.env.MASTER_KEY} npx parse-dbtool migrate`;
|
||||
exec(migrate, (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
console.error(`Error: ${error.message}`);
|
||||
|
||||
Generated
+360
-496
File diff suppressed because it is too large
Load Diff
@@ -21,13 +21,13 @@
|
||||
"@aws-sdk/client-s3": "^3.828.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.828.0",
|
||||
"@parse/fs-files-adapter": "^3.0.0",
|
||||
"@parse/s3-files-adapter": "^4.1.1",
|
||||
"@parse/s3-files-adapter": "^4.2.0",
|
||||
"@pdf-lib/fontkit": "^1.1.1",
|
||||
"@signpdf/placeholder-pdf-lib": "^3.2.6",
|
||||
"@signpdf/signer-p12": "^3.2.4",
|
||||
"@signpdf/signpdf": "^3.2.5",
|
||||
"aws-sdk": "^2.1692.0",
|
||||
"axios": "^1.9.0",
|
||||
"axios": "^1.10.0",
|
||||
"coherentpdf": "^2.5.5",
|
||||
"cors": "^2.8.5",
|
||||
"date-fns-tz": "^3.2.0",
|
||||
@@ -48,7 +48,7 @@
|
||||
"parse-server": "^8.2.1",
|
||||
"parse-server-api-mail-adapter": "^4.1.0",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"posthog-node": "^5.0.0",
|
||||
"posthog-node": "^5.1.0",
|
||||
"qrcode": "^1.5.4",
|
||||
"rate-limiter-flexible": "^7.1.1",
|
||||
"speakeasy": "^2.0.0",
|
||||
@@ -57,9 +57,9 @@
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"@babel/eslint-parser": "^7.27.5",
|
||||
"eslint": "^9.28.0",
|
||||
"eslint": "^9.29.0",
|
||||
"jasmine": "^5.8.0",
|
||||
"mongodb-runner": "^5.8.3",
|
||||
"mongodb-runner": "^5.9.0",
|
||||
"nodemon": "^3.1.10",
|
||||
"nyc": "^17.1.0",
|
||||
"prettier": "^3.5.3"
|
||||
|
||||
Reference in New Issue
Block a user