fix: error-only Sentry telemetry, storm-proof capture, and crash fixes (#476)

Removes Sentry tracing entirely (BullMQ idle polling burned 4.8M transactions in 2 days at the baked 0.1 rate), decouples PostHog sampling, and replaces the type-only error scrub with a vetted-field sanitizer plus SafeError/ToolInputError contracts. One classified capture path with per-signature throttles and a per-process ceiling makes storms impossible (NODE-1E was 4,541 events from one 30s loop). Browser errors move to a dedicated web Sentry project with their own source maps. Adds the SNAPOTTER_TELEMETRY runtime kill switch and silences test fleets.

Crash fixes: remote 204/304 SSRF process kill (NODE-20), conversion-preset boot crash loop (NODE-21), Redis version preflight + unhandled subscribe rejection (NODE-1T), Sign PDF on plain-http origins (NODE-1K/1M), wavesurfer/pdf.js teardown rejections (NODE-1P/1N), bundle-import ZlibError to 400 (NODE-1Z), chart-maker input errors declassified (NODE-1H/1J), asset requests skip the session DB lookup (NODE-1D).
This commit is contained in:
SnapOtter
2026-07-10 21:41:49 +08:00
committed by GitHub
parent 3d1744aec8
commit ae6a4c8b7c
75 changed files with 2198 additions and 260 deletions
+11 -3
View File
@@ -5,6 +5,7 @@ import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from "re
import { useTranslation } from "@/contexts/i18n-context";
import { toNormalizedRect } from "@/lib/sign-geometry";
import type { SavedSignature } from "@/lib/signature-store";
import { safeRandomUUID } from "@/lib/uuid";
pdfjs.GlobalWorkerOptions.workerSrc = new URL(
"pdfjs-dist/build/pdf.worker.min.mjs",
@@ -90,7 +91,11 @@ export const SignCanvas = forwardRef<SignCanvasRef, Props>(function SignCanvas(
setPageCount(doc.numPages);
setPage(0);
setDocReady(true);
})();
})().catch(() => {
// loadingTask.destroy() in the cleanup rejects the in-flight promise;
// swallowing it is the fix for Sentry NODE-1N. A genuine load failure
// leaves docReady false, which the existing UI already handles.
});
return () => {
cancelled = true;
docRef.current?.loadingTask.destroy();
@@ -181,7 +186,10 @@ export const SignCanvas = forwardRef<SignCanvasRef, Props>(function SignCanvas(
}
layer.batchDraw();
onSelectionChange?.(false);
})();
})().catch(() => {
// getPage()/render() reject when the loadingTask is destroyed mid-render
// (file swap or unmount); expected teardown, same as the load effect.
});
return () => {
cancelled = true;
};
@@ -241,7 +249,7 @@ export const SignCanvas = forwardRef<SignCanvasRef, Props>(function SignCanvas(
layer.add(node);
tr.nodes([node]);
layer.batchDraw();
placementsRef.current.push({ id: crypto.randomUUID(), page, node });
placementsRef.current.push({ id: safeRandomUUID(), page, node });
onSelectionChange?.(true);
emitCount();
};
@@ -10,6 +10,7 @@ import {
type SavedSignature,
} from "@/lib/signature-store";
import { generateId } from "@/lib/utils";
import { safeRandomUUID } from "@/lib/uuid";
import { useFileStore } from "@/stores/file-store";
import type { SignCanvasRef } from "./sign-canvas";
import { SignaturePad } from "./signature-pad";
@@ -126,7 +127,7 @@ export function SignPdfSettings({ signProps }: { signProps?: SignProps }) {
const handleSavePad = (dataUrl: string, remember: boolean) => {
const sig: SavedSignature = remember
? addSignature(dataUrl)
: { id: crypto.randomUUID(), dataUrl, createdAt: Date.now() };
: { id: safeRandomUUID(), dataUrl, createdAt: Date.now() };
if (remember) refresh();
signProps?.canvasRef.current?.addSignature(sig);
setPadOpen(false);