Merge pull request #695 from OpenSignLabs/login_to_sandbox

refactor: handle conditions of sandbox
This commit is contained in:
nxglabs
2025-05-29 19:04:16 +00:00
parent 3d223ba343
commit d4c0051cd9
31 changed files with 302 additions and 291 deletions
-25
View File
@@ -1,25 +0,0 @@
# Use an official Node runtime as the base image
FROM node:18
# Set the working directory inside the container
WORKDIR /usr/src/app
# Copy package.json and package-lock.json first to leverage Docker cache
COPY ./package*.json ./
# Install application dependencies
RUN npm install
# Copy the current directory contents into the container
COPY ./ .
COPY ./.husky .
# Make port 3000 available to the world outside this container
EXPOSE 3000
# Define environment variables if needed
# ENV NODE_ENV production
# Run the application
ENTRYPOINT npm run start
+9
View File
@@ -13,6 +13,10 @@ RUN npm install
# Copy the current directory contents into the container
COPY apps/OpenSign/ .
COPY apps/OpenSign/.husky .
COPY apps/OpenSign/entrypoint.sh .
# make the entrypoint.sh file executable
RUN chmod +x entrypoint.sh
# Define environment variables if needed
ENV NODE_ENV=production
@@ -20,8 +24,13 @@ ENV GENERATE_SOURCEMAP=false
# build
RUN npm run build
# Inject env.js loader into index.html
RUN sed -i '/<head>/a\<script src="/env.js"></script>' build/index.html
# Make port 3000 available to the world outside this container
EXPOSE 3000
ENTRYPOINT ["./entrypoint.sh"]
# Run the application
CMD ["npm", "start"]
+29
View File
@@ -0,0 +1,29 @@
#!/bin/sh
ENV_FILE=./build/env.js
DOTENV_FILE=./.env.prod # ✅ use .env.prod
echo "Generating runtime env file at $ENV_FILE..."
echo "window.RUNTIME_ENV = {" > $ENV_FILE
# List of keys to include
RUNTIME_KEYS="REACT_APP_SERVERURL"
for key in $RUNTIME_KEYS; do
# First check docker env (-e), fallback to .env file
value=$(printenv "$key")
if [ -z "$value" ] && [ -f "$DOTENV_FILE" ]; then
# fallback: read from .env
value=$(grep "^$key=" "$DOTENV_FILE" | cut -d '=' -f2- | tr -d '\r\n' | sed 's/"/\\"/g')
else
value=$(echo "$value" | sed 's/"/\\"/g')
fi
echo " $key: \"$value\"," >> $ENV_FILE
done
echo "};" >> $ENV_FILE
exec "$@"
@@ -1,6 +1,7 @@
{
"header-news": "Neue Funktion: Benutzer des Teams-Plans können jetzt ihre eigenen AWS S3-Buckets für die Dateispeicherung integrieren",
"header-news-btn": "Jetzt einrichten",
"sandbox-news": "Dies ist eine Sandbox-Umgebung. Bitte nicht für produktive Zwecke verwenden.",
"create-account": "Konto erstellen",
"login": "Anmelden",
"language": "Sprache",
@@ -675,7 +676,7 @@
"public-template-mssg-1": "Um OpenSign in Ihr React- oder Next.js-Projekt zu integrieren, führen Sie einfach den folgenden Befehl aus:",
"public-template-mssg-2": "Stellen Sie sicher, dass npm oder yarn in Ihrem Projekt eingerichtet ist. Wenn Sie Yarn verwenden, können Sie npm install durch yarn add @opensign/react ersetzen.",
"public-template-mssg-3": "Benötigen Sie weitere Details oder Beispiele?",
"public-template-mssg-4": "Besuchen Sie die",
"public-template-mssg-4": "Besuchen Sie die ",
"public-template-mssg-5": " npm für die neuesten Updates, detaillierte Dokumentationen und Versionshistorie.",
"public-template-mssg-6": "Bevor Sie diesen Code-Schnipsel verwenden können, müssen Sie diese Vorlage öffentlich machen.",
"public-template-mssg-7": "Bevor Sie einen öffentlichen Link generieren können, müssen Sie diese Vorlage öffentlich machen.",
@@ -1,6 +1,7 @@
{
"header-news": "New feature: Protect your account with Two-Factor Authentication (2FA) and enjoy the future of login with Passkeys — no passwords needed.",
"header-news-btn": "Setup now",
"sandbox-news": "This is a sandbox environment. Please do not use it for production purposes.",
"create-account": "Create account",
"login": "Login",
"language": "Language",
@@ -675,7 +676,7 @@
"public-template-mssg-1": "To integrate OpenSign into your React or Next.js project, simply run the following command:",
"public-template-mssg-2": "Ensure you have npm or yarn set up in your project. If you're using Yarn, you can replace npm install with yarn add @opensign/react.",
"public-template-mssg-3": "Need more details or examples?",
"public-template-mssg-4": "Visit the",
"public-template-mssg-4": "Visit the ",
"public-template-mssg-5": " npm for the latest updates, detailed documentation, and version history.",
"public-template-mssg-6": "Before you can use this code snippet, you must make this template public.",
"public-template-mssg-7": "Before you can generate a public link you must make this template public.",
@@ -1,6 +1,7 @@
{
"header-news": "Nueva característica: los usuarios del plan Teams ahora pueden integrar sus propios depósitos de AWS S3 para el almacenamiento de archivos",
"header-news-btn": "Configurar ahora",
"sandbox-news": "Este es un entorno sandbox. Por favor, no lo utilice con fines de producción.",
"create-account": "Crear cuenta",
"login": "Iniciar sesión",
"language": "Idioma",
@@ -675,7 +676,7 @@
"public-template-mssg-1": "Para integrar OpenSign a tu proyecto React o Next.js, simplemente ejecuta los siguientes comandos:",
"public-template-mssg-2": "Asegúrate de tener «npm» o «yarn» configurado en tu proyecto. Si estás usando «yarn», puedes reemplazar «npm install» con «yarn add @opensign/react».",
"public-template-mssg-3": "¿Necesitas más detalles o ejemplos?",
"public-template-mssg-4": "Visita la",
"public-template-mssg-4": "Visita la ",
"public-template-mssg-5": " «npm» para las últimas actualizaciones, documentación detallada e historial de versiones.",
"public-template-mssg-6": "Antes de que puedas usar este fragmento de código, debes convertir esta plantilla en pública.",
"public-template-mssg-7": "Antes de poder generar un enlace público, debes hacer que esta plantilla sea pública.",
@@ -1,6 +1,7 @@
{
"header-news": "Nouvelle fonctionnalité : les utilisateurs du forfait Teams peuvent désormais intégrer leurs propres compartiments AWS S3 pour le stockage de fichiers",
"header-news-btn": "Configurer maintenant",
"sandbox-news": "Ceci est un environnement sandbox. Veuillez ne pas l'utiliser à des fins de production.",
"create-account": "Créer un compte",
"login": "Se Connecter",
"language": "Langue",
@@ -675,7 +676,7 @@
"public-template-mssg-1": "Pour intégrer OpenSign dans votre projet React ou Next.js, exécutez simplement la commande suivante :",
"public-template-mssg-2": "Assurez-vous que npm ou Yarn est configuré dans votre projet. Si vous utilisez Yarn, vous pouvez remplacer npm install par Yarn Add @opensign/react.",
"public-template-mssg-3": "Besoin de plus de détails ou d'exemples ?",
"public-template-mssg-4": "Visitez le",
"public-template-mssg-4": "Visitez le ",
"public-template-mssg-5": "npm pour les dernières mises à jour, une documentation détaillée et l'historique des versions.",
"public-template-mssg-6": "Avant de pouvoir utiliser cet extrait de code, vous devez rendre ce modèle public.",
"public-template-mssg-7": "Avant de pouvoir générer un lien public, vous devez rendre ce modèle public.",
@@ -1,6 +1,7 @@
{
"header-news": "Nuova funzionalità: Gli utenti del piano Teams possono ora integrare i propri bucket AWS S3 per l'archiviazione dei file",
"header-news-btn": "Configura Ora",
"sandbox-news": "Questo è un ambiente sandbox. Si prega di non utilizzarlo per scopi di produzione.",
"create-account": "Crea Account",
"login": "Accedi",
"language": "Lingua",
@@ -675,7 +676,7 @@
"public-template-mssg-1": "Per integrare OpenSign nel tuo progetto React o Next.js, esegui semplicemente il seguente comando:",
"public-template-mssg-2": "Assicurati di avere npm o yarn configurato nel tuo progetto. Se usi Yarn, puoi sostituire npm install con yarn add @opensign/react.",
"public-template-mssg-3": "Hai bisogno di maggiori dettagli o esempi?",
"public-template-mssg-4": "Visita il",
"public-template-mssg-4": "Visita il ",
"public-template-mssg-5": " npm per gli aggiornamenti più recenti, documentazione dettagliata e cronologia delle versioni.",
"public-template-mssg-6": "Prima di poter utilizzare questo frammento di codice, devi rendere questo modello pubblico.",
"public-template-mssg-7": "Prima di poter generare un link pubblico, devi rendere questo modello pubblico.",
+5 -5
View File
@@ -106,10 +106,10 @@ function App() {
element={<LazyPage Page={GuestLogin} />}
/>
<Route path="/debugpdf" element={<LazyPage Page={DebugPdf} />} />
<Route
path="/forgetpassword"
element={<LazyPage Page={ForgetPassword} />}
/>
<Route
path="/forgetpassword"
element={<LazyPage Page={ForgetPassword} />}
/>
<Route
element={
<ValidateSession>
@@ -165,7 +165,7 @@ function App() {
path="/recipientSignPdf/:docId"
element={<PdfRequestFiles />}
/>
<Route path="/users" element={<UserList />} />
<Route path="/users" element={<UserList />} />
<Route
path="/preferences"
element={<LazyPage Page={Preferences} />}
@@ -22,7 +22,7 @@ function DropdownWidgetOption(props) {
const resetState = () => {
setDropdownOptionList(["option-1", "option-2"]);
setDropdownName( props.currWidgetsDetails?.options?.name || props.type);
setDropdownName(props.currWidgetsDetails?.options?.name || props.type);
setIsReadOnly(false);
setIsHideLabel(false);
setMinCount(0);
@@ -115,7 +115,7 @@ function DropdownWidgetOption(props) {
defaultData,
isHideLabel
);
resetState()
resetState();
};
@@ -1,4 +1,4 @@
import React, {
import {
useState,
useRef,
} from "react";
@@ -168,34 +168,34 @@ function PlaceholderType(props) {
<div style={{ zIndex: props.isSignYourself && "99" }}>
{props.pos.options?.values?.map((data, ind) => {
return (
<div
key={ind}
className="select-none-cls flex items-center text-center gap-0.5 pointer-events-none"
>
<input
id={`checkbox-${props.pos.key + ind}`}
style={{ width: fontSize, height: fontSize }}
className={`${
ind === 0 ? "mt-0" : "mt-[5px]"
} flex justify-center op-checkbox rounded-[1px] `}
disabled={
props.isNeedSign &&
(props.pos.options?.isReadOnly ||
props.data?.signerObjId !== props.signerObjId)
}
type="checkbox"
readOnly
checked={!!selectCheckbox(ind, selectedCheckbox)}
/>
{!props.pos.options?.isHideLabel && (
<label
htmlFor={`checkbox-${props.pos.key + ind}`}
style={{ fontSize: fontSize, color: fontColor }}
className="text-xs mb-0 text-center"
>
{data}
</label>
)}
<div key={ind} className="select-none-cls pointer-events-none">
<label
htmlFor={`checkbox-${props.pos.key + ind}`}
style={{ fontSize: fontSize, color: fontColor }}
className={`mb-0 flex items-center gap-1 ${
ind > 0 ? "mt-[3px]" : "mt-[0px]"
}`}
>
<input
id={`checkbox-${props.pos.key + ind}`}
style={{
width: fontSize,
height: fontSize
}}
className="op-checkbox rounded-[1px]"
disabled={
props.isNeedSign &&
(props.pos.options?.isReadOnly ||
props.data?.signerObjId !== props.signerObjId)
}
type="checkbox"
readOnly
checked={!!selectCheckbox(ind, selectedCheckbox)}
/>
{!props.pos.options?.isHideLabel && (
<span className="leading-none">{data}</span>
)}
</label>
</div>
);
})}
@@ -243,7 +243,6 @@ function PlaceholderType(props) {
<i className="fa-light fa-circle-chevron-down mr-1 "></i>
</div>
);
case "initials":
return props.pos.SignUrl ? (
<img
@@ -444,36 +443,40 @@ function PlaceholderType(props) {
return (
<div
key={ind}
className="select-none-cls flex items-center text-center gap-0.5 pointer-events-none"
className="select-none-cls pointer-events-none"
>
<input
readOnly
id={`radio-${props.pos.key + ind}`}
<label
htmlFor={`radio-${props.pos.key + ind}`}
style={{
width: fontSize,
height: fontSize,
marginTop: ind > 0 ? "10px" : "0px"
fontSize: fontSize,
color: fontColor,
marginTop: ind > 0 ? "5px" : "0px"
}}
className={`op-radio rounded-full border- border-black appearance-none bg-white inline-block align-middle relative ${
handleRadioCheck(data) ? "checked-radio" : ""
}`}
type="radio"
disabled={
props.isNeedSign &&
(props.pos.options?.isReadOnly ||
props.data?.signerObjId !== props.signerObjId)
}
checked={handleRadioCheck(data)}
/>
{!props.pos.options?.isHideLabel && (
<label
htmlFor={`radio-${props.pos.key + ind}`}
style={{ fontSize: fontSize, color: fontColor }}
className="text-xs mb-0"
>
{data}
</label>
)}
className="text-xs mb-0 flex items-center gap-1 "
>
<input
readOnly
id={`radio-${props.pos.key + ind}`}
style={{
width: fontSize,
height: fontSize,
lineHeight: 2
}}
className={`op-radio rounded-full border- border-black appearance-none bg-white inline-block align-middle relative ${
handleRadioCheck(data) ? "checked-radio" : ""
}`}
type="radio"
disabled={
props.isNeedSign &&
(props.pos.options?.isReadOnly ||
props.data?.signerObjId !== props.signerObjId)
}
checked={handleRadioCheck(data)}
/>
{!props.pos.options?.isHideLabel && (
<span className="leading-none">{data}</span>
)}
</label>
</div>
);
})}
@@ -231,7 +231,9 @@ function WidgetsValueModal(props) {
setXyPosition,
uniqueId,
false,
data?.format
data?.format,
currWidgetsDetails?.options?.fontSize || 12,
currWidgetsDetails?.options?.fontColor || "black"
);
setSelectDate({ date: date, format: data?.format });
};
@@ -1327,47 +1329,42 @@ function WidgetsValueModal(props) {
<div className="border-[1px] border-gray-300 rounded-[2px] p-1 px-3">
{currWidgetsDetails?.options?.values?.map((data, ind) => {
return (
<div
key={ind}
className=" select-none-cls flex items-center text-center gap-0.5"
>
<input
id={`checkbox-${currWidgetsDetails?.key + ind}`}
className={`${
ind === 0 ? "mt-0" : "mt-[5px]"
} op-checkbox op-checkbox-sm rounded-[1px] `}
type="checkbox"
checked={!!selectCheckbox(ind, selectedCheckbox)}
onChange={(e) => {
if (e.target.checked) {
const maxRequired =
currWidgetsDetails?.options?.validation
?.maxRequiredCount;
const maxCountInt =
maxRequired && parseInt(maxRequired);
if (maxCountInt > 0) {
if (
selectedCheckbox &&
selectedCheckbox?.length <= maxCountInt - 1
) {
<div key={ind} className=" select-none-cls">
<label
htmlFor={`checkbox-${currWidgetsDetails?.key + ind}`}
className="text-xs flex items-center gap-1"
>
<input
id={`checkbox-${currWidgetsDetails?.key + ind}`}
className={`${
ind === 0 ? "mt-0" : "mt-[5px]"
} op-checkbox op-checkbox-xs rounded-[1px] `}
type="checkbox"
checked={!!selectCheckbox(ind, selectedCheckbox)}
onChange={(e) => {
if (e.target.checked) {
const maxRequired =
currWidgetsDetails?.options?.validation
?.maxRequiredCount;
const maxCountInt =
maxRequired && parseInt(maxRequired);
if (maxCountInt > 0) {
if (
selectedCheckbox &&
selectedCheckbox?.length <= maxCountInt - 1
) {
handleCheckboxValue(e.target.checked, ind);
}
} else {
handleCheckboxValue(e.target.checked, ind);
}
} else {
handleCheckboxValue(e.target.checked, ind);
}
} else {
handleCheckboxValue(e.target.checked, ind);
}
}}
/>
{!currWidgetsDetails?.options?.isHideLabel && (
<label
htmlFor={`checkbox-${currWidgetsDetails?.key + ind}`}
className="text-xs mb-0 text-center"
>
{data}
</label>
)}
}}
/>
{data}
</label>
</div>
);
})}
@@ -1515,32 +1512,23 @@ function WidgetsValueModal(props) {
<div className="border-[1px] border-gray-300 rounded-[2px] p-1 px-3">
{currWidgetsDetails?.options?.values.map((data, ind) => {
return (
<div
key={ind}
className="select-none-cls flex items-center text-center gap-0.5"
>
<input
id={`radio-${currWidgetsDetails?.key + ind}`}
style={{
marginTop: ind > 0 ? "10px" : "0px"
}}
className={`flex justify-center op-radio`}
type="radio"
value={data}
checked={handleRadioCheck(data)}
onChange={(e) => {
handleCheckRadio(e.target.value);
}}
/>
{!currWidgetsDetails?.options?.isHideLabel && (
<label
htmlFor={`radio-${currWidgetsDetails?.key + ind}`}
// style={{ fontSize: fontSize, color: fontColor }}
className="text-xs mb-0"
>
{data}
</label>
)}
<div key={ind} className="select-none-cls">
<label
htmlFor={`radio-${currWidgetsDetails?.key + ind}`}
className="cursor-pointer flex items-center text-sm gap-1"
>
<input
id={`radio-${currWidgetsDetails?.key + ind}`}
className={`op-radio op-radio-xs`}
type="radio"
value={data}
checked={handleRadioCheck(data)}
onChange={(e) => {
handleCheckRadio(e.target.value);
}}
/>
{data}
</label>
</div>
);
})}
@@ -1652,7 +1640,7 @@ function WidgetsValueModal(props) {
const validateExpression = (regexValidation) => {
if (widgetValue && regexValidation) {
let regexObject = regexValidation;
if (props.pos?.options?.validation?.type === "regex") {
if (currWidgetsDetails?.options?.validation?.type === "regex") {
regexObject = RegexParser(regexValidation);
}
let isValidate = regexObject.test(widgetValue);
@@ -1675,7 +1663,8 @@ function WidgetsValueModal(props) {
validateExpression(regexValidation);
break;
default:
regexValidation = props.pos?.options?.validation?.pattern || "";
regexValidation =
currWidgetsDetails?.options?.validation?.pattern || "";
validateExpression(regexValidation);
break;
}
@@ -2,9 +2,19 @@ import React, { useEffect, useState } from "react";
import AsyncSelect from "react-select/async";
import { useTranslation } from "react-i18next";
import axios from "axios";
import { handleUnlinkSigner } from "../../../constant/Utils";
const SelectSigners = (props) => {
const { t } = useTranslation();
const {
signerPos,
setSignerPos,
signersData,
setSignersData,
uniqueId,
isRemove,
handleAddUser
} = props;
const [userList, setUserList] = useState([]);
const [selected, setSelected] = useState();
const [userData, setUserData] = useState({});
@@ -30,7 +40,7 @@ const SelectSigners = (props) => {
//checking if user select no signer option from dropdown
if (item) {
//checking selected signer is already assign to the document or not
const alreadyAssign = props.signersData.some(
const alreadyAssign = signersData.some(
(item2) => item2.objectId === item.value
);
if (alreadyAssign) {
@@ -49,7 +59,7 @@ const SelectSigners = (props) => {
};
const handleAdd = () => {
if (userData && userData.objectId) {
props.details(userData);
handleAddUser(userData);
if (props.closePopup) {
props.closePopup();
}
@@ -60,7 +70,13 @@ const SelectSigners = (props) => {
};
//function to use remove signer from assigned widgets in create template flow
const handleRemove = () => {
props.handleUnlinkSigner();
handleUnlinkSigner(
signerPos,
setSignerPos,
signersData,
setSignersData,
uniqueId
);
if (props.closePopup) {
props.closePopup();
}
@@ -161,7 +177,7 @@ const SelectSigners = (props) => {
<button className="op-btn op-btn-primary" onClick={() => handleAdd()}>
{t("submit")}
</button>
{props.isExistSigner && props.handleUnlinkSigner && (
{props.isExistSigner && isRemove && (
<button
className="op-btn op-btn-accent op-btn-outline"
onClick={() => handleRemove()}
+46 -22
View File
@@ -1,6 +1,5 @@
import axios from "axios";
import moment from "moment";
import React from "react";
import { PDFDocument, rgb, degrees } from "pdf-lib";
import Parse from "parse";
import { appInfo } from "./appinfo";
@@ -21,7 +20,9 @@ export const isTabAndMobile = window.innerWidth < 1023;
export const textInputWidget = "text input";
export const textWidget = "text";
export const radioButtonWidget = "radio button";
export function getEnv() {
return window?.RUNTIME_ENV || {};
}
//function for create list of year for date widget
export const range = (start, end, step) => {
@@ -1665,10 +1666,11 @@ export const multiSignEmbed = async (widgets, pdfDoc, signyourself, scale) => {
].includes(position.type);
if (position.type === "checkbox") {
let checkboxGapFromTop, isCheck;
let y = yPos(position);
const optionsFontSize = fontSize || 13;
const checkboxSize = fontSize;
const checkboxTextGapFromLeft = fontSize + 5 || 22;
let y = yPos(position) + 2;
//calculate checkbox size to draw on pdf
const checkboxSize = fontSize - 1;
//calculate gap between checkbox and options
const checkboxTextGapFromLeft = fontSize + 5;
if (position?.options?.values.length > 0) {
position?.options?.values.forEach((item, ind) => {
const checkboxRandomId = "checkbox" + randomId();
@@ -1680,13 +1682,11 @@ export const multiSignEmbed = async (widgets, pdfDoc, signyourself, scale) => {
} else if (position?.options?.defaultValue) {
isCheck = position?.options?.defaultValue?.includes(ind);
}
const checkbox = form.createCheckBox(checkboxRandomId);
if (ind > 0) {
y = y + checkboxGapFromTop;
} else {
checkboxGapFromTop = fontSize + 5 || 26;
checkboxGapFromTop = fontSize + 3.2;
}
if (!position?.options?.isHideLabel) {
@@ -1694,10 +1694,10 @@ export const multiSignEmbed = async (widgets, pdfDoc, signyourself, scale) => {
const optionsPosition = compensateRotation(
page.getRotation().angle,
xPos(position) + checkboxTextGapFromLeft,
y,
y - 3,
1,
page.getSize(),
optionsFontSize,
fontSize,
updateColorInRgb,
font,
page
@@ -1780,7 +1780,7 @@ export const multiSignEmbed = async (widgets, pdfDoc, signyourself, scale) => {
: NewbreakTextIntoLines(textContent, fixedWidth);
// Set initial y-coordinate for the first line
let x = xPos(position);
let y = yPos(position);
let y = yPos(position) - 4;
// Embed each line on the page
for (const line of lines) {
const textPosition = compensateRotation(
@@ -1830,28 +1830,33 @@ export const multiSignEmbed = async (widgets, pdfDoc, signyourself, scale) => {
} else if (position.type === radioButtonWidget) {
const radioRandomId = "radio" + randomId();
const radioGroup = form.createRadioGroup(radioRandomId);
let radioOptionGapFromTop;
const optionsFontSize = fontSize || 13;
const radioTextGapFromLeft = fontSize + 5 || 20;
//draw radio button on document and options if hide label is enable
let radioButtonFromTop;
//getting radio buttons options text font size
const optionsFontSize = fontSize;
//calculate value of gap between radio button and options
const radioTextGapFromLeft = fontSize + 6;
//getting radio button font size
const radioSize = fontSize;
//getting position of radio widget in y direction
let y = yPos(position);
//on the basic of option's length create radio button and message
if (position?.options?.values.length > 0) {
position?.options?.values.forEach((item, ind) => {
if (ind > 0) {
y = y + radioOptionGapFromTop;
y = y + radioButtonFromTop;
} else {
radioOptionGapFromTop = fontSize + 10 || 25;
radioButtonFromTop = fontSize + 6;
}
if (!position?.options?.isHideLabel) {
// below line of code is used to embed label with radio button in pdf
const optionsPosition = compensateRotation(
page.getRotation().angle,
xPos(position) + radioTextGapFromLeft,
y,
y - 2,
1,
page.getSize(),
optionsFontSize,
fontSize,
updateColorInRgb,
font,
page
@@ -1860,7 +1865,7 @@ export const multiSignEmbed = async (widgets, pdfDoc, signyourself, scale) => {
page.drawText(item, optionsPosition);
}
let radioObj = {
x: xPos(position),
x: xPos(position) + 2,
y: y,
width: radioSize,
height: radioSize
@@ -3066,7 +3071,7 @@ export const mailTemplate = (param) => {
"</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Note</td><td></td><td style='color:#626363;font-weight:bold'>" +
param.note +
"</td></tr><tr><td></td><td></td></tr></table></div> <div style='margin-left:70px'><a target=_blank href=" +
param.sigingUrl +
param.signingUrl +
"><button style='padding:12px;background-color:#d46b0f;color:white;border:0px;font-weight:bold;margin-top:30px'>Sign here</button></a></div><div style='display:flex;justify-content:center;margin-top:10px'></div></div></div><div><p> This is an automated email from " +
appName +
". For any queries regarding this email, please contact the sender " +
@@ -3161,3 +3166,22 @@ export const checkRegularExpress = (validateType, setValidatePlaceholder) => {
setValidatePlaceholder("please enter value");
}
};
//function to use unlink signer from widgets
export const handleUnlinkSigner = (signerPos,setSignerPos,signersdata,setSignersData,uniqueId) => {
//remove existing signer's details from 'signerPos' array
const updatePlaceHolder = signerPos.map((x) => {
if (x.Id === uniqueId) {
return { ...x, signerPtr: {}, signerObjId: "" };
}
return { ...x };
});
setSignerPos(updatePlaceHolder);
//remove existing signer's details from 'signersdata' array and keep role and id
const updateSigner = signersdata.map((item) => {
if (item.Id == uniqueId) {
return { Role: item.Role, Id: item.Id, blockColor: item.blockColor };
}
return item;
});
setSignersData(updateSigner);
};
+6 -3
View File
@@ -1,9 +1,12 @@
import logo from "../assets/images/logo.png";
import { getEnv } from "./Utils";
export function serverUrl_fn() {
let baseUrl = process.env.REACT_APP_SERVERURL
? process.env.REACT_APP_SERVERURL
: window.location.origin + "/api/app";
const env = getEnv();
const serverurl = env?.REACT_APP_SERVERURL
? env.REACT_APP_SERVERURL // env.REACT_APP_SERVERURL is used for prod
: process.env.REACT_APP_SERVERURL; // process.env.REACT_APP_SERVERURL is used for dev (locally)
let baseUrl = serverurl ? serverurl : window.location.origin + "/api/app";
return baseUrl;
}
export const appInfo = {
+12 -9
View File
@@ -1,3 +1,13 @@
const userssetting = [
{
icon: "fa-light fa-users fa-fw",
title: "Users",
target: "_self",
pageType: "",
description: "",
objectId: "users"
}
];
export const subSetting = [
{
icon: "fa-light fa-sliders",
@@ -7,14 +17,7 @@ export const subSetting = [
description: "",
objectId: "preferences"
},
{
icon: "fa-light fa-users fa-fw",
title: "Users",
target: "_self",
pageType: "",
description: "",
objectId: "users"
}
...userssetting
];
const sidebarList = [
@@ -65,7 +68,7 @@ const sidebarList = [
pageType: "report",
description: "",
objectId: "6TeaPr321t"
},
}
]
},
{
+24 -23
View File
@@ -68,6 +68,7 @@ function Login() {
setState({ ...state, loading: false, alertType: type, alertMsg: msg });
setTimeout(() => setState({ ...state, alertMsg: "" }), 2000);
};
const checkUserExt = async () => {
const app = await getAppLogo();
if (app?.error === "invalid_json") {
@@ -82,11 +83,11 @@ function Login() {
} else {
setImage(appInfo?.applogo || undefined);
}
dispatch(fetchAppInfo());
if (localStorage.getItem("accesstoken")) {
setState({ ...state, loading: true });
GetLoginData();
}
dispatch(fetchAppInfo());
};
const handleChange = (event) => {
let { name, value } = event.target;
@@ -96,20 +97,15 @@ function Login() {
setState({ ...state, [name]: value });
};
const handleSubmit = async (event) => {
localStorage.removeItem("accesstoken");
event.preventDefault();
const handleLogin = async (
) => {
const email = state?.email
const password = state?.password
if (!emailRegex.test(state.email)) {
alert("Please enter a valid email address.");
return;
}
const { email, password } = state;
if (!email || !password) {
return;
}
localStorage.removeItem("accesstoken");
try {
setState({ ...state, loading: true });
localStorage.setItem("appLogo", appInfo.applogo);
@@ -132,6 +128,14 @@ function Login() {
showToast("danger", "Invalid username/password or region");
}
};
const handleLoginBtn = async (event) => {
event.preventDefault();
if (!emailRegex.test(state.email)) {
alert("Please enter a valid email address.");
return;
}
await handleLogin();
};
const setThirdpartyLoader = (value) => {
setState({ ...state, thirdpartyLoader: value });
@@ -275,7 +279,6 @@ function Login() {
const userInformation = JSON.parse(
localStorage.getItem("UserInformation")
);
// console.log("payload ", payload);
if (payload && payload.sessionToken) {
const params = {
userDetails: {
@@ -289,7 +292,6 @@ function Login() {
}
};
const userSignUp = await Parse.Cloud.run("usersignup", params);
// console.log("userSignUp ", userSignUp);
if (userSignUp && userSignUp.sessionToken) {
const LocalUserDetails = {
name: userInformation.name,
@@ -430,7 +432,7 @@ function Login() {
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-2">
<div>
<form onSubmit={handleSubmit} aria-label="Login Form">
<form onSubmit={handleLoginBtn} aria-label="Login Form">
<h1 className="text-[30px] mt-6">{t("welcome")}</h1>
<fieldset>
<legend className="text-[12px] text-[#878787]">
@@ -488,15 +490,14 @@ function Login() {
)}
</span>
</div>
<div className="relative mt-1">
<NavLink
to="/forgetpassword"
className="text-[13px] op-link op-link-primary underline-offset-1 focus:outline-none ml-1"
>
{t("forgot-password")}
</NavLink>
</div>
<div className="relative mt-1">
<NavLink
to="/forgetpassword"
className="text-[13px] op-link op-link-primary underline-offset-1 focus:outline-none ml-1"
>
{t("forgot-password")}
</NavLink>
</div>
</div>
</fieldset>
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 text-center text-xs font-bold mt-2">
+4 -4
View File
@@ -1,4 +1,4 @@
import React, { useState, useRef, useEffect } from "react";
import { useState, useRef, useEffect } from "react";
import { PDFDocument } from "pdf-lib";
import "../styles/signature.css";
import Parse from "parse";
@@ -916,7 +916,7 @@ function PdfRequestFiles(
const htmlReqBody =
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /></head><body>" +
replacedRequestBody +
"</body> </html>";
"</body></html>";
const variables = {
document_title: documentName,
@@ -929,7 +929,7 @@ function PdfRequestFiles(
receiver_phone: user?.Phone || "",
expiry_date: localExpireDate,
company_name: orgName,
signing_url: `<a href=${signPdf} target=_blank>Sign here</a>`
signing_url: signPdf
};
replaceVar = replaceMailVaribles(
requestSubject,
@@ -944,7 +944,7 @@ function PdfRequestFiles(
title: documentName,
organization: orgName,
localExpireDate: localExpireDate,
sigingUrl: signPdf
signingUrl: signPdf
};
let params = {
replyto: senderEmail || "",
+3 -3
View File
@@ -1220,7 +1220,7 @@ function PlaceHolderSign() {
receiver_phone: signerMail[i]?.Phone || "",
expiry_date: localExpireDate,
company_name: orgName,
signing_url: `<a href=${signPdf} target=_blank>Sign here</a>`
signing_url: signPdf
};
replaceVar = replaceMailVaribles(
requestSubject,
@@ -1249,7 +1249,7 @@ function PlaceHolderSign() {
receiver_phone: signerMail[i]?.Phone || "",
expiry_date: localExpireDate,
company_name: orgName,
signing_url: `<a href=${signPdf} target=_blank>Sign here</a>`
signing_url: signPdf
};
replaceVar = replaceMailVaribles(mailSubject, htmlReqBody, variables);
}
@@ -1260,7 +1260,7 @@ function PlaceHolderSign() {
title: documentName,
organization: orgName,
localExpireDate: localExpireDate,
sigingUrl: signPdf
signingUrl: signPdf
};
let params = {
extUserId: owner?.objectId,
+1 -1
View File
@@ -1,4 +1,4 @@
import React, { useState, useRef, useEffect } from "react";
import { useState, useRef, useEffect } from "react";
import { PDFDocument } from "pdf-lib";
import "../styles/signature.css";
import Parse from "parse";
@@ -1149,25 +1149,6 @@ const TemplatePlaceholder = () => {
const handleLinkUser = (id) => {
setIsAddUser({ [id]: true });
};
//function to use unlink signer from widgets
const handleUnlinkSigner = () => {
//remove existing signer's details from 'signerPos' array
const updatePlaceHolder = signerPos.map((x) => {
if (x.Id === uniqueId) {
return { ...x, signerPtr: {}, signerObjId: "" };
}
return { ...x };
});
setSignerPos(updatePlaceHolder);
//remove existing signer's details from 'signersdata' array and keep role and id
const updateSigner = signersdata.map((item) => {
if (item.Id == uniqueId) {
return { Role: item.Role, Id: item.Id, blockColor: item.blockColor };
}
return item;
});
setSignersData(updateSigner);
};
// `handleAddUser` is used to adduser
const handleAddUser = (data) => {
const signerPtr = {
@@ -1950,7 +1931,9 @@ const TemplatePlaceholder = () => {
closePopup={closePopup}
signersData={signersdata}
signerPos={signerPos}
handleUnlinkSigner={handleUnlinkSigner}
setSignerPos={setSignerPos}
setSignersData={setSignersData}
isRemove={true}
/>
)}
</div>
@@ -679,7 +679,7 @@ const ReportTable = (props) => {
receiver_phone: userDetails?.Phone || "",
expiry_date: localExpireDate,
company_name: doc.ExtUserPtr.Company,
signing_url: `<a href=${signPdf} target=_blank>Sign here</a>`
signing_url: signPdf
};
const res = replaceMailVaribles(subject, "", variables);
setMail((prev) => ({ ...prev, subject: res.subject }));
@@ -710,7 +710,7 @@ const ReportTable = (props) => {
receiver_phone: userDetails?.Phone || "",
expiry_date: localExpireDate,
company_name: doc.ExtUserPtr.Company,
signing_url: `<a href=${signPdf} target=_blank>Sign here</a>`
signing_url: signPdf
};
const res = replaceMailVaribles("", body, variables);
@@ -754,7 +754,7 @@ const ReportTable = (props) => {
receiver_phone: user?.signerPtr?.Phone || "",
expiry_date: localExpireDate,
company_name: doc?.ExtUserPtr?.Company || "",
signing_url: `<a href=${signPdf} target=_blank>Sign here</a>`
signing_url: signPdf
};
const subject =
doc?.RequestSubject ||
@@ -25,12 +25,10 @@ const LinkUserModal = (props) => {
handleClose={props.closePopup}
>
<SelectSigners
details={props.handleAddUser}
{...props}
closePopup={props.closePopup}
signersData={props?.signersData}
isContact={isContact}
setIsContact={setIsContact}
handleUnlinkSigner={props.handleUnlinkSigner}
isExistSigner={isExistSigner}
/>
{isContact && (
-27
View File
@@ -1,27 +0,0 @@
# Use an official Node runtime as the base image
FROM node:20
# Set the working directory inside the container
WORKDIR /usr/src/app
# Copy package.json and package-lock.json first to leverage Docker cache
COPY ./package*.json ./
# Install application dependencies
RUN npm install
# If you have native dependencies, you'll need extra tools. Uncomment the following line if needed.
# RUN apk add --no-cache make gcc g++ python3
# Copy the current directory contents into the container
COPY ./ .
# Make port 8080 available to the world outside this container
EXPOSE 8080
# Define environment variables if needed
# ENV NODE_ENV production
# ENV DATABASE_URL mongodb://db:27017
# Run the application
ENTRYPOINT npm start
+1 -1
View File
@@ -252,7 +252,7 @@ export const mailTemplate = param => {
"</td></tr><tr><td style='font-weight:bold;font-family:sans-serif;font-size:15px'>Note</td><td></td><td style='color:#626363;font-weight:bold'>" +
param.note +
"</td></tr><tr><td></td><td></td></tr></table></div> <div style='margin-left:70px'><a target=_blank href=" +
param.sigingUrl +
param.signingUrl +
"><button style='padding:12px;background-color:#d46b0f;color:white;border:0px;font-weight:bold;margin-top:30px'>Sign here</button></a></div><div style='display:flex;justify-content:center;margin-top:10px'></div></div></div><div><p> This is an automated email from " +
AppName +
'. For any queries regarding this email, please contact the sender ' +
@@ -4,9 +4,9 @@ export default async function Newsletter(request) {
const email = request.params?.email?.toLowerCase()?.replace(/\s/g, '');
const domain = request.params.domain;
try {
const envAppId = process.env.REACT_APP_APPID || 'opensign';
const envAppId = 'opensign';
const headers = { 'Content-Type': 'application/json', 'X-Parse-Application-Id': envAppId };
const envProdServer = process.env.REACT_APP_SERVERURL || 'https://app.opensignlabs.com/api/app';
const envProdServer = 'https://app.opensignlabs.com/api/app';
const newsletter = await axios.post(
`${envProdServer}/classes/Newsletter`,
{ Name: name, Email: email, Domain: domain },
@@ -14,7 +14,7 @@ async function deductcount(docsCount, extUserId) {
}
async function sendMail(document, publicUrl) {
//sessionToken
const baseUrl = new URL(publicUrl); //process.env.PUBLIC_URL
const baseUrl = new URL(publicUrl);
// console.log("pdfDetails", pdfDetails);
const timeToCompleteDays = document?.TimeToCompleteDays || 15;
@@ -71,7 +71,7 @@ async function sendMail(document, publicUrl) {
receiver_phone: existSigner?.Phone || '',
expiry_date: localExpireDate,
company_name: orgName,
signing_url: `<a href=${signPdf} target=_blank>Sign here</a>`,
signing_url: signPdf,
};
replaceVar = replaceMailVaribles(mailSubject, htmlReqBody, variables);
}
@@ -82,7 +82,7 @@ async function sendMail(document, publicUrl) {
title: document.Name,
organization: orgName,
localExpireDate: localExpireDate,
sigingUrl: signPdf,
signingUrl: signPdf,
};
let params = {
extUserId: document.ExtUserPtr.objectId,
@@ -116,7 +116,7 @@ async function sendNotifyMail(doc, signUser, mailProvider, publicUrl) {
const creatorEmail = doc.ExtUserPtr.Email;
const signerName = signUser.Name;
const signerEmail = signUser.Email;
const viewDocUrl = `${publicUrl}/recipientSignPdf/${doc.objectId}`; // ` ${process.env.PUBLIC_URL}/recipientSignPdf/${doc.objectId}`;
const viewDocUrl = `${publicUrl}/recipientSignPdf/${doc.objectId}`;
const subject = `Document "${pdfName}" has been signed by ${signerName}`;
const body =
"<html><head><meta http-equiv='Content-Type' content='text/html; charset=UTF-8'/></head><body><div style='background-color:#f5f5f5;padding:20px'><div style='background-color:white'>" +
+1 -1
View File
@@ -161,7 +161,7 @@ app.use(express.urlencoded({ limit: '50mb', extended: true }));
app.use(function (req, res, next) {
req.headers['x-real-ip'] = getUserIP(req);
const publicUrl = 'https://' + req?.get('host');
req.headers['public_url'] = publicUrl; // process.env.PUBLIC_URL
req.headers['public_url'] = publicUrl;
next();
});
function getUserIP(request) {
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "open_sign_server",
"version": "1.4.0",
"version": "2.21.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "open_sign_server",
"version": "1.4.0",
"version": "2.21.1",
"license": "MIT",
"dependencies": {
"@aws-sdk/client-s3": "^3.812.0",