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>
56 lines
2.2 KiB
JavaScript
56 lines
2.2 KiB
JavaScript
// Passthrough behavior of common/sentry-cron.js when no backend DSN is set —
|
|
// the wrapper must be a transparent no-op (result and rejection both flow
|
|
// through) so periodic jobs behave identically on Sentry-less deployments.
|
|
// Also covers common/logger.js's flattenLogAttributes (the pino-context →
|
|
// Sentry-log-attributes normalizer used by the forwarding hook).
|
|
import { test } from 'node:test';
|
|
import assert from 'node:assert';
|
|
|
|
// Ensure the gate is closed regardless of the shell environment, then import
|
|
// (both modules evaluate the gate once at load).
|
|
delete process.env.SENTRY_DSN_BACKEND;
|
|
const { withCronMonitor } = await import('../common/sentry-cron.js');
|
|
const { flattenLogAttributes } = await import('../common/logger.js');
|
|
|
|
test('returns the job result unchanged when Sentry is not configured', async () => {
|
|
const result = await withCronMonitor('test-job', async () => 42, {
|
|
schedule: { type: 'interval', value: 5, unit: 'minute' },
|
|
});
|
|
assert.strictEqual(result, 42);
|
|
});
|
|
|
|
test('propagates a rejected job to the caller', async () => {
|
|
await assert.rejects(
|
|
() => withCronMonitor('test-job', async () => { throw new Error('boom'); }, {}),
|
|
/boom/,
|
|
);
|
|
});
|
|
|
|
test('runs the job exactly once', async () => {
|
|
let calls = 0;
|
|
await withCronMonitor('test-job', async () => { calls += 1; }, {});
|
|
assert.strictEqual(calls, 1);
|
|
});
|
|
|
|
test('flattens Error values into message + stack attributes', () => {
|
|
const err = new Error('kaput');
|
|
const attrs = flattenLogAttributes({ err, ip: '1.2.3.4' });
|
|
assert.strictEqual(attrs['err.message'], 'kaput');
|
|
assert.ok(attrs['err.stack'].includes('kaput'));
|
|
assert.strictEqual(attrs.ip, '1.2.3.4');
|
|
assert.strictEqual('err' in attrs, false);
|
|
});
|
|
|
|
test('serializes nested objects and passes primitives through', () => {
|
|
const attrs = flattenLogAttributes({
|
|
req: { method: 'GET', url: '/api/x' },
|
|
count: 3,
|
|
flag: true,
|
|
nothing: null,
|
|
});
|
|
assert.strictEqual(attrs.req, '{"method":"GET","url":"/api/x"}');
|
|
assert.strictEqual(attrs.count, 3);
|
|
assert.strictEqual(attrs.flag, true);
|
|
assert.strictEqual(attrs.nothing, null);
|
|
});
|