mirror of
https://github.com/jason5ng32/MyIP.git
synced 2026-11-04 08:55:47 +00:00
Frontend (@sentry/vue, loaded via build-time-gated dynamic import — no VITE_SENTRY_DSN_FRONTEND, no Sentry in the bundle; init joins the mount gate so instrumentation precedes the first render): - uncaught errors, route-change tracing / Web Vitals, error-only masked Session Replay, console.error capture fingerprinted per message prefix so each fallback source stays a distinct issue - explicit ip-source:exhausted signal via the app-events bus - hidden source maps uploaded (then deleted) by @sentry/vite-plugin, gated on SENTRY_AUTH_TOKEN Backend (@sentry/node, bootstrapped by sentry-instrument.js via node --import so ESM loader hooks land before express; never loaded without SENTRY_DSN_BACKEND): - Express error capture + per-route tracing - logger hook mirrors warn+ lines to Sentry Logs and elevates error+ to grouped, alertable Issues - Sentry Crons check-ins for the MaxMind / CAIDA / service-status jobs via the no-op-without-DSN wrapper common/sentry-cron.js Delivery & ops: - first-party tunnel /api/monitoring relays envelopes past ad blockers; DSN-allowlisted (never an open relay), exempt from the shared rate limiter with its own generous ceiling - rate-limit ledger file becomes opt-in now that the warn line reaches Sentry Logs; ecosystem.config.cjs carries the --import flag for pm2 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
74 lines
3.3 KiB
JavaScript
74 lines
3.3 KiB
JavaScript
// Sentry error monitoring for the SPA. This module is loaded on demand by
|
|
// main.js ONLY when VITE_SENTRY_DSN_FRONTEND is set at build time — a build
|
|
// without the env var never ships nor requests this chunk, so self-hosted
|
|
// deployments stay completely Sentry-free.
|
|
//
|
|
// App code must never import Sentry directly (it would drag the SDK back into
|
|
// the main bundle and bypass the gating). For explicit signals, emit a domain
|
|
// event on utils/app-events.js and subscribe to it here instead.
|
|
import * as Sentry from '@sentry/vue';
|
|
import { onAppEvent } from '@/utils/app-events';
|
|
|
|
const env = import.meta.env ?? {};
|
|
|
|
const initSentry = (app, router) => {
|
|
Sentry.init({
|
|
app,
|
|
dsn: env.VITE_SENTRY_DSN_FRONTEND,
|
|
// Distinguish local `pnpm dev` events from production ones in the
|
|
// Sentry UI ("development" / "production")
|
|
environment: env.MODE,
|
|
integrations: [
|
|
// Route-change performance tracing via vue-router → Web Vitals
|
|
// (LCP / CLS / INP) and per-route load metrics under Insights
|
|
Sentry.browserTracingIntegration({ router }),
|
|
// Session Replay, error-only: nothing is uploaded unless an error event fires
|
|
Sentry.replayIntegration(),
|
|
// console.error() → grouped Issues (tagged logger:console).
|
|
Sentry.captureConsoleIntegration({ levels: ['error'] }),
|
|
],
|
|
// 100% trace sampling — Should change based on plan quota
|
|
tracesSampleRate: 1.0,
|
|
// Replay: never record plain sessions, always keep the buffer that
|
|
// gets flushed when an error occurs
|
|
replaysSessionSampleRate: 0,
|
|
replaysOnErrorSampleRate: 1.0,
|
|
// MyIP is a privacy tool: never attach user IP / headers to events
|
|
sendDefaultPii: false,
|
|
// Relay envelopes through our own backend (api/sentry-tunnel.js)
|
|
tunnel: '/api/monitoring',
|
|
// Console-captured events group by the console message instead of
|
|
// the exception stack. The fallback chains all surface the same
|
|
// "TypeError: Failed to fetch" from fetchWithTimeout, so stack
|
|
// grouping would collapse every source's degradation into ONE issue
|
|
// — indistinguishable per source, and a single archive/discard on it
|
|
// silently hides them all.
|
|
beforeSend(event) {
|
|
if (event.logger === 'console') {
|
|
const firstArg = event.extra?.arguments?.[0];
|
|
if (typeof firstArg === 'string' && firstArg.trim()) {
|
|
event.fingerprint = [firstArg.trim().slice(0, 200)];
|
|
}
|
|
}
|
|
return event;
|
|
},
|
|
});
|
|
|
|
// Explicit product signals arrive via the app-events bus — business code
|
|
// stays Sentry-free and just emits domain events (see frontend/AGENTS.md).
|
|
|
|
// A whole IP source chain (primary + every fallback) failed. v6
|
|
// exhaustion is routine for IPv4-only visitors, so only v4 is reported;
|
|
// per-hop degradation inside a chain is already covered by the
|
|
// console.error capture above.
|
|
onAppEvent('ip-source:exhausted', ({ source, ipVersion }) => {
|
|
if (ipVersion !== 'v4') return;
|
|
Sentry.captureMessage(`IP source chain exhausted: ${source}`, {
|
|
level: 'error',
|
|
tags: { source, ipVersion },
|
|
});
|
|
});
|
|
};
|
|
|
|
export { initSentry };
|