Compare commits

...
Author SHA1 Message Date
RaktimaandGitHub 6271fedcbf Merge pull request #2183 from OpenSignLabs/updates-26432416990
Merge pull request #2474 from nxglabs/sync-to-public_repo-26403170706
2026-05-26 10:03:53 +05:30
raktima-opensignlabs d3cb8d575c Merge pull request #2474 from nxglabs/sync-to-public_repo-26403170706
Merge pull request #2473 from nxglabs/raktima-patch-main-6
2026-05-26 04:33:08 +00:00
RaktimaandGitHub 0c295961c3 Merge pull request #2179 from OpenSignLabs/updates-26234947661
Merge pull request #2467 from nxglabs/sync-to-public_repo-26233821722
2026-05-21 20:46:42 +05:30
raktima-opensignlabs 5fbd1fc6f2 Merge pull request #2467 from nxglabs/sync-to-public_repo-26233821722
Merge pull request #2466 from nxglabs/raktima-main-patch-5
2026-05-21 15:14:25 +00:00
RaktimaandGitHub 3f4007d564 Merge pull request #2177 from OpenSignLabs/updates-26033200443
Merge pull request #2463 from nxglabs/sync-to-public_repo-26032162086
2026-05-18 17:57:04 +05:30
raktima-opensignlabs b49dc6bc6a Merge pull request #2463 from nxglabs/sync-to-public_repo-26032162086
Merge pull request #2462 from nxglabs/staging
2026-05-18 12:22:49 +00:00
RaktimaandGitHub b325095d60 Merge pull request #2175 from OpenSignLabs/updates-25926972891
Merge pull request #2460 from nxglabs/sync-to-public_repo-25861367352
2026-05-18 10:11:45 +05:30
6 changed files with 73 additions and 46 deletions
+23 -32
View File
@@ -39,27 +39,9 @@ const mime = {
};
function contentType(filePath) {
// Files under /.well-known/ are JSON by convention (WebAuthn, apple-app-site-association, etc.)
// and are extensionless, so explicitly set application/json there.
const rel = path.relative(root, filePath).split(path.sep).join("/");
if (rel.startsWith(".well-known/")) {
return "application/json; charset=utf-8";
}
return mime[path.extname(filePath).toLowerCase()] || "application/octet-stream";
}
function applyWellKnownCors(req, res, filePath) {
const rel = path.relative(root, filePath).split(path.sep).join("/");
if (!rel.startsWith(".well-known/")) return;
// .well-known/* must be readable cross-origin (e.g. WebAuthn related-origins
// requests come from any RP host). Echo the request origin when present so
// credentialed fetches still work; otherwise allow all.
const origin = req.headers.origin;
res.setHeader("Access-Control-Allow-Origin", origin || "*");
res.setHeader("Vary", "Origin");
res.setHeader("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS");
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
}
function safeJoin(reqPath) {
let decoded;
@@ -96,23 +78,25 @@ function streamFile(req, res, filePath, stats) {
"Last-Modified": stats.mtime.toUTCString(),
"Cache-Control": cacheControl(filePath)
};
applyWellKnownCors(req, res, filePath);
if (req.method === "HEAD") {
res.writeHead(200, headers);
return res.end();
}
const stream = fs.createReadStream(filePath);
stream.on("error", () => {
stream.on("error", (err) => {
console.error("Stream error:", err);
if (!res.headersSent) {
res.writeHead(500, { "Content-Type": "text/plain" });
return res.end("Internal Server Error");
res.end("Internal Server Error");
} else {
res.destroy();
}
res.destroy();
});
res.on("close", () => {
if (!stream.destroyed) stream.destroy();
});
res.on("error", () => {
res.on("error", (err) => {
console.error("Response error:", err);
if (!stream.destroyed) stream.destroy();
});
res.writeHead(200, headers);
@@ -122,7 +106,7 @@ function streamFile(req, res, filePath, stats) {
function sendIndex(req, res) {
const indexPath = path.join(root, "index.html");
fs.stat(indexPath, (err, stats) => {
if (err || !stats.isFile()) {
if (err || !stats || !stats.isFile()) {
res.writeHead(500, { "Content-Type": "text/plain" });
return res.end("index.html not found");
}
@@ -133,14 +117,15 @@ function sendIndex(req, res) {
const server = http.createServer((req, res) => {
if (req.method === "OPTIONS") {
const filePath = safeJoin(req.url || "/");
if (filePath) applyWellKnownCors(req, res, filePath);
res.writeHead(204);
return res.end();
}
if (req.method !== "GET" && req.method !== "HEAD") {
res.writeHead(405, { Allow: "GET, HEAD, OPTIONS" });
return res.end();
}
const reqUrl = req.url || "/";
const filePath = safeJoin(reqUrl);
if (!filePath) {
@@ -149,21 +134,27 @@ const server = http.createServer((req, res) => {
}
fs.stat(filePath, (err, stats) => {
if (!err && stats.isFile()) {
if (err) {
// No file at this path → SPA fallback to index.html
return sendIndex(req, res);
}
if (stats.isFile()) {
return streamFile(req, res, filePath, stats);
}
if (!err && stats.isDirectory()) {
if (stats.isDirectory()) {
const indexInDir = path.join(filePath, "index.html");
return fs.stat(indexInDir, (e, s) => {
if (!e && s.isFile()) return streamFile(req, res, indexInDir, s);
return fs.stat(indexInDir, (dirErr, dirStats) => {
if (!dirErr && dirStats && dirStats.isFile()) {
return streamFile(req, res, indexInDir, dirStats);
}
return sendIndex(req, res);
});
}
// No file at this path → SPA fallback to index.html
sendIndex(req, res);
// Path exists but is neither file nor directory → SPA fallback
return sendIndex(req, res);
});
});
server.listen(port, host, () => {
console.log(`Serving ${root} on http://${host}:${port}`);
});
});
@@ -32,7 +32,12 @@ const PrefillWidgets = ({ prefills = [], setPrefills, onNext }) => {
>
<div className="py-3 px-[10px] op-card border-[1px] border-gray-400">
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-10 gap-y-4 w-full">
{prefills.map((widget, index) => (
{[...prefills]
.sort((a, b) =>
a.pageNumber !== b.pageNumber
? a.pageNumber - b.pageNumber
: (a.yPosition ?? 0) - (b.yPosition ?? 0)
).map((widget, index) => (
<RenderWidgets
key={widget.key}
showLabel
@@ -148,15 +148,25 @@ function PrefillWidgetModal(props) {
return true;
})
}));
//latten the filtered array and exclude read-only widgets
const flatArray = filteredArray?.flatMap((page) =>
page.pos
.filter((widget) => !widget.options?.isReadOnly)
.map((widget) => ({
widget,
pageNumber: page.pageNumber
}))
);
// Flatten the filtered array, exclude read-only widgets,
// carry yPosition for sorting, then sort by pageNumber asc → yPosition asc
// (mirrors the newSignPos.sort in PdfRequestFiles so widgets appear in
// the same top-to-bottom, page-1-first order as they do in the document)
const flatArray = filteredArray
?.flatMap((page) =>
page.pos
.filter((widget) => !widget.options?.isReadOnly)
.map((widget) => ({
widget,
pageNumber: page.pageNumber,
yPosition: widget.yPosition ?? 0
}))
)
?.sort((a, b) =>
a.pageNumber !== b.pageNumber
? a.pageNumber - b.pageNumber // primary: page order (page 1 first)
: a.yPosition - b.yPosition // secondary: top-to-bottom within page
);
return flatArray || [];
}, [props.prefillData]);
@@ -896,4 +906,4 @@ function PrefillWidgetModal(props) {
);
}
export default PrefillWidgetModal;
export default PrefillWidgetModal;
+2 -2
View File
@@ -577,8 +577,8 @@ function PdfRequestFiles(
setContractName("_Contactbook");
setSignerUserId(contact?.objectId);
handleTourStatus(isTourEnabled, contact?.TourStatus);
} catch (err) {
console.log("err while getting tourstatus", err);
} catch (error) {
console.log("err while getting tourstatus", error);
}
}
}
@@ -126,6 +126,7 @@ export default async function docxtopdf(req, res) {
try {
// ---- Auth: current user ----
const userRes = await axios.get(`${serverUrl}/users/me`, { headers: sessionHeader });
const uploadedSizeBytes = req.file.size ?? req.file.buffer.length;
// ---- contracts_Users ----
const whereUser = JSON.stringify({
@@ -425,7 +425,27 @@ async function PDF(req) {
className = 'contracts_Users';
signUser = _resDoc.ExtUserPtr;
}
// Strict-order gating: when both `SendinOrder` and `SendInOrderStrict`
// are enabled the document creator wants the signing flow locked to a
// strict sequence — a signer/approver may only act once every previous
// signer/approver placeholder has a Signed/Approved audit entry. We
// skip this check entirely for the document owner (className=Users)
// because owners never sign through this path.
if (reqUserId && _resDoc?.SendinOrder === true && _resDoc?.SendInOrderStrict === true) {
const placeholders = Array.isArray(_resDoc?.Placeholders)
? _resDoc.Placeholders.filter(p => p?.Role !== 'prefill')
: [];
const myIdx = findPlaceholderIndex(placeholders, reqUserId);
if (myIdx > 0) {
const pendingId = findPendingPriorSigner(placeholders, myIdx, _resDoc?.AuditTrail);
if (pendingId) {
throw new Parse.Error(
Parse.Error.OPERATION_FORBIDDEN,
'Strict signing order is enabled — please wait for the previous signers to complete their action before signing.'
);
}
}
}
const username = signUser.Name;
const userEmail = signUser.Email;
if (req.params.pdfFile) {