mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-02 16:15:39 +00:00
Admins had logs and nothing else: client errors only reached the browser console. An admin can now set a Sentry-protocol address (DSN) for Bugsink, GlitchTip or Sentry under System > Monitoring, or with `ERROR_REPORTING_DSN`, `ERROR_REPORTING_DSN_FILE`, `ERROR_REPORTING_SAMPLE_RATE` and `ERROR_REPORTING_ENVIRONMENT`. Bulwark Lite reads the same from `errorReporting` in policy.json. Everything stays off while no address is set. - browsers report errors from the error boundaries, window.onerror, unhandled rejections and failed JMAP requests (method and error type only), with route and JMAP method breadcrumbs and the chunks' debug IDs - the server reports what Next hands to onRequestError and what routes log with logger.error - every report is scrubbed first: quoted text, addresses, URLs, file names, tokens and the rest after the first line are removed, and the server scrubs what browsers post to /api/errors again - reports are deduplicated and rate-limited per session and process - errors are counted per day for "errors today" on the admin page - startup messages and audit entries go through the logger, so `LOG_FORMAT=json` covers them
100 lines
2.4 KiB
TypeScript
100 lines
2.4 KiB
TypeScript
"use client";
|
|
|
|
import React, { Component, ReactNode } from "react";
|
|
import { useTranslations } from "next-intl";
|
|
import { debug } from "@/lib/debug";
|
|
import { captureException } from "@/lib/error-reporting/client";
|
|
|
|
export interface FallbackProps {
|
|
error: Error;
|
|
resetError: () => void;
|
|
t: (key: string) => string;
|
|
}
|
|
|
|
interface ErrorBoundaryProps {
|
|
children: ReactNode;
|
|
fallback: (props: FallbackProps) => ReactNode;
|
|
onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
|
|
onReset?: () => void;
|
|
}
|
|
|
|
interface ErrorBoundaryState {
|
|
hasError: boolean;
|
|
error: Error | null;
|
|
}
|
|
|
|
/**
|
|
* Core error boundary class component (React requirement).
|
|
* Receives translation function as prop from the functional wrapper.
|
|
*/
|
|
class ErrorBoundaryCore extends Component<
|
|
ErrorBoundaryProps & { t: (key: string) => string },
|
|
ErrorBoundaryState
|
|
> {
|
|
state: ErrorBoundaryState = { hasError: false, error: null };
|
|
|
|
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
|
|
return { hasError: true, error };
|
|
}
|
|
|
|
componentDidCatch(error: Error, errorInfo: React.ErrorInfo): void {
|
|
// Always log errors
|
|
debug.error("[ErrorBoundary]", error.message, {
|
|
stack: error.stack,
|
|
componentStack: errorInfo.componentStack,
|
|
});
|
|
|
|
// Off unless error reporting is configured (lib/error-reporting/client.ts).
|
|
// The component stack stays here: it names components, nothing else.
|
|
captureException(error, "boundary", false);
|
|
|
|
// Call optional error handler
|
|
this.props.onError?.(error, errorInfo);
|
|
}
|
|
|
|
resetError = (): void => {
|
|
this.props.onReset?.();
|
|
this.setState({ hasError: false, error: null });
|
|
};
|
|
|
|
render(): ReactNode {
|
|
if (this.state.hasError && this.state.error) {
|
|
return this.props.fallback({
|
|
error: this.state.error,
|
|
resetError: this.resetError,
|
|
t: this.props.t,
|
|
});
|
|
}
|
|
return this.props.children;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Functional wrapper that injects translations into the error boundary.
|
|
* Use this component to wrap any part of your UI that might throw errors.
|
|
*
|
|
* @example
|
|
* <ErrorBoundary fallback={SidebarErrorFallback}>
|
|
* <Sidebar />
|
|
* </ErrorBoundary>
|
|
*/
|
|
export function ErrorBoundary({
|
|
children,
|
|
fallback,
|
|
onError,
|
|
onReset,
|
|
}: ErrorBoundaryProps) {
|
|
const t = useTranslations("errors");
|
|
|
|
return (
|
|
<ErrorBoundaryCore
|
|
fallback={fallback}
|
|
onError={onError}
|
|
onReset={onReset}
|
|
t={t}
|
|
>
|
|
{children}
|
|
</ErrorBoundaryCore>
|
|
);
|
|
}
|