Session 10: Internal auth refactor, prefetch cascade keys, Sentry, welcome email (1286 tests)
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* Sentry init for the Express backend (Session 10).
|
||||
*
|
||||
* Boot is GRACEFUL when SENTRY_DSN is unset: `initSentry()` no-ops and
|
||||
* the module's exported `Sentry` is a stub whose every method is a
|
||||
* noop. This lets callers wire `Sentry.captureException(...)` etc.
|
||||
* unconditionally — when the DSN isn't configured (dev, CI, tests),
|
||||
* nothing crashes and no events are sent.
|
||||
*
|
||||
* PII posture: `sendDefaultPii: false` plus an `events`-stage hook
|
||||
* that strips `user.ip_address` and `request.cookies`. We log code
|
||||
* paths and errors, not user identities.
|
||||
*
|
||||
* Sample rate: 10% traces (free tier friendly). 100% errors.
|
||||
*/
|
||||
|
||||
const realSentry = require('@sentry/node');
|
||||
|
||||
// Initialized lazily so this module is safe to require even when the
|
||||
// DSN is absent.
|
||||
let _initialized = false;
|
||||
|
||||
function buildClient() {
|
||||
return {
|
||||
init: realSentry.init,
|
||||
captureException: realSentry.captureException,
|
||||
captureMessage: realSentry.captureMessage,
|
||||
setupExpressErrorHandler: realSentry.setupExpressErrorHandler,
|
||||
addBreadcrumb: realSentry.addBreadcrumb,
|
||||
setUser: realSentry.setUser,
|
||||
setTag: realSentry.setTag,
|
||||
setContext: realSentry.setContext,
|
||||
};
|
||||
}
|
||||
|
||||
function buildNoop() {
|
||||
// Every Sentry surface used in the codebase returns either undefined
|
||||
// (most) or a no-op express middleware (`setupExpressErrorHandler`).
|
||||
const noopMiddleware = (_req, _res, next) => next();
|
||||
return {
|
||||
init: () => {},
|
||||
captureException: () => {},
|
||||
captureMessage: () => {},
|
||||
setupExpressErrorHandler: (_app) => {
|
||||
// The real one mutates the app; the noop simply returns without
|
||||
// attaching anything. The caller pattern is:
|
||||
// Sentry.setupExpressErrorHandler(app);
|
||||
// which evaluates the call for its side effects.
|
||||
},
|
||||
addBreadcrumb: () => {},
|
||||
setUser: () => {},
|
||||
setTag: () => {},
|
||||
setContext: () => {},
|
||||
Handlers: { errorHandler: () => noopMiddleware },
|
||||
};
|
||||
}
|
||||
|
||||
let Sentry = buildNoop();
|
||||
|
||||
function initSentry({ dsn = process.env.SENTRY_DSN, environment = process.env.NODE_ENV, release } = {}) {
|
||||
if (_initialized) return Sentry;
|
||||
if (!dsn) {
|
||||
// Stay on the noop client.
|
||||
return Sentry;
|
||||
}
|
||||
Sentry = buildClient();
|
||||
Sentry.init({
|
||||
dsn,
|
||||
environment: environment || 'development',
|
||||
release,
|
||||
tracesSampleRate: 0.1,
|
||||
sendDefaultPii: false,
|
||||
beforeSend(event) {
|
||||
// PII scrubbing. Sentry occasionally fills these via auto-context.
|
||||
if (event.user) {
|
||||
delete event.user.ip_address;
|
||||
delete event.user.email;
|
||||
}
|
||||
if (event.request) {
|
||||
delete event.request.cookies;
|
||||
// Strip the Authorization header to avoid logging bearer tokens.
|
||||
if (event.request.headers) {
|
||||
delete event.request.headers.authorization;
|
||||
delete event.request.headers.cookie;
|
||||
delete event.request.headers['x-internal-key'];
|
||||
delete event.request.headers['x-vyndr-internal-key'];
|
||||
}
|
||||
}
|
||||
return event;
|
||||
},
|
||||
});
|
||||
_initialized = true;
|
||||
return Sentry;
|
||||
}
|
||||
|
||||
function getSentry() {
|
||||
return Sentry;
|
||||
}
|
||||
|
||||
function isInitialized() {
|
||||
return _initialized;
|
||||
}
|
||||
|
||||
// Test helper — reset to the noop client so subsequent initSentry()
|
||||
// calls re-run. Not exported via the main surface; live behind __internals.
|
||||
function __resetForTests() {
|
||||
_initialized = false;
|
||||
Sentry = buildNoop();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
initSentry,
|
||||
getSentry,
|
||||
isInitialized,
|
||||
// Convenience re-export so callers can do `const { Sentry } = require(...)`.
|
||||
// Note: this is a live binding — mutated by initSentry() on first call.
|
||||
get Sentry() { return Sentry; },
|
||||
__internals: { __resetForTests, buildNoop, buildClient },
|
||||
};
|
||||
Reference in New Issue
Block a user