diff --git a/src/api/Server.ts b/src/api/Server.ts
index fff94936..1a0ea6b2 100644
--- a/src/api/Server.ts
+++ b/src/api/Server.ts
@@ -1,7 +1,7 @@
import "missing-native-js-functions";
import { Server, ServerOptions } from "lambert-server";
import { Authentication, CORS } from "./middlewares/";
-import { Config, initDatabase, initEvent } from "@fosscord/util";
+import { Config, initDatabase, initEvent, Sentry } from "@fosscord/util";
import { ErrorHandler } from "./middlewares/ErrorHandler";
import { BodyParser } from "./middlewares/BodyParser";
import { Router, Request, Response, NextFunction } from "express";
@@ -38,6 +38,7 @@ export class FosscordServer extends Server {
await Config.init();
await initEvent();
await initInstance();
+ await Sentry.init(this.app);
let logRequests = process.env["LOG_REQUESTS"] != undefined;
if (logRequests) {
@@ -96,6 +97,8 @@ export class FosscordServer extends Server {
this.app.use(ErrorHandler);
TestClient(this.app);
+ Sentry.errorHandler(this.app);
+
if (logRequests)
console.log(
red(
diff --git a/src/bundle/Server.ts b/src/bundle/Server.ts
index e6f8d17c..abd05e1f 100644
--- a/src/bundle/Server.ts
+++ b/src/bundle/Server.ts
@@ -7,10 +7,7 @@ import * as Gateway from "@fosscord/gateway";
import { CDNServer } from "@fosscord/cdn";
import express from "express";
import { green, bold, yellow } from "picocolors";
-import { Config, initDatabase } from "@fosscord/util";
-import * as Sentry from "@sentry/node";
-import * as Tracing from "@sentry/tracing";
-import * as Integrations from "@sentry/integrations";
+import { Config, initDatabase, Sentry } from "@fosscord/util";
const app = express();
const server = http.createServer();
@@ -30,81 +27,12 @@ process.on("SIGTERM", async () => {
async function main() {
await initDatabase();
await Config.init();
-
- //Sentry
- if (Config.get().sentry.enabled) {
- console.log(
- `[Bundle] ${yellow(
- "You are using Sentry! This may slightly impact performance on large loads!",
- )}`,
- );
- Sentry.init({
- dsn: Config.get().sentry.endpoint,
- integrations: [
- new Sentry.Integrations.Http({ tracing: true }),
- new Tracing.Integrations.Express({ app }),
- new Tracing.Integrations.Mysql(),
- new Integrations.RewriteFrames({
- root: __dirname,
- }),
- ],
- tracesSampleRate: Config.get().sentry.traceSampleRate,
- environment: Config.get().sentry.environment,
- });
-
- Sentry.addGlobalEventProcessor((event, hint) => {
- if (event.transaction) {
- // Rewrite things that look like IDs to `:id` for sentry
- event.transaction = event.transaction
- .split("/")
- .map((x) => (!parseInt(x) ? x : ":id"))
- .join("/");
- }
-
- // TODO: does this even do anything?
- delete event.request?.cookies;
- if (event.request?.headers) {
- delete event.request.headers["X-Real-Ip"];
- delete event.request.headers["X-Forwarded-For"];
- delete event.request.headers["X-Forwarded-Host"];
- delete event.request.headers["X-Super-Properties"];
- }
-
- if (event.breadcrumbs) {
- event.breadcrumbs = event.breadcrumbs.filter((x) => {
- // Filter breadcrumbs that we don't care about
- if (x.message?.includes("identified as")) return false;
- if (x.message?.includes("[WebSocket] closed")) return false;
- if (
- x.message?.includes(
- "Got Resume -> cancel not implemented",
- )
- )
- return false;
- if (x.message?.includes("[Gateway] New connection from"))
- return false;
-
- return true;
- });
- }
-
- return event;
- });
-
- app.use(Sentry.Handlers.requestHandler());
- app.use(Sentry.Handlers.tracingHandler());
- }
+ await Sentry.init(app);
server.listen(port);
await Promise.all([api.start(), cdn.start(), gateway.start()]);
- if (Config.get().sentry.enabled) {
- app.use(Sentry.Handlers.errorHandler());
- app.use(function onError(err: any, req: any, res: any, next: any) {
- res.statusCode = 500;
- res.end(res.sentry + "\n");
- });
- }
+ Sentry.errorHandler(app);
console.log(`[Server] ${green(`listening on port ${bold(port)}`)}`);
}
diff --git a/src/cdn/Server.ts b/src/cdn/Server.ts
index 7340a735..abf59a2a 100644
--- a/src/cdn/Server.ts
+++ b/src/cdn/Server.ts
@@ -1,5 +1,5 @@
import { Server, ServerOptions } from "lambert-server";
-import { Config, initDatabase, registerRoutes } from "@fosscord/util";
+import { Config, initDatabase, registerRoutes, Sentry } from "@fosscord/util";
import path from "path";
import avatarsRoute from "./routes/avatars";
import guildProfilesRoute from "./routes/guild-profiles";
@@ -18,6 +18,8 @@ export class CDNServer extends Server {
async start() {
await initDatabase();
await Config.init();
+ await Sentry.init(this.app);
+
this.app.use((req, res, next) => {
res.set("Access-Control-Allow-Origin", "*");
// TODO: use better CSP policy
@@ -87,6 +89,8 @@ export class CDNServer extends Server {
);
this.log("verbose", "[Server] Route /guilds/banners registered");
+ Sentry.errorHandler(this.app);
+
return super.start();
}
diff --git a/src/cdn/start.ts b/src/cdn/start.ts
index c22984fa..4b619c25 100644
--- a/src/cdn/start.ts
+++ b/src/cdn/start.ts
@@ -1,6 +1,5 @@
require("module-alias/register");
-import dotenv from "dotenv";
-dotenv.config();
+import "dotenv/config";
import { CDNServer } from "./Server";
const server = new CDNServer({ port: Number(process.env.PORT) || 3003 });
diff --git a/src/gateway/Server.ts b/src/gateway/Server.ts
index 7e1489be..48f1a7d9 100644
--- a/src/gateway/Server.ts
+++ b/src/gateway/Server.ts
@@ -1,7 +1,13 @@
import "missing-native-js-functions";
import dotenv from "dotenv";
dotenv.config();
-import { closeDatabase, Config, initDatabase, initEvent } from "@fosscord/util";
+import {
+ closeDatabase,
+ Config,
+ initDatabase,
+ initEvent,
+ Sentry,
+} from "@fosscord/util";
import ws from "ws";
import { Connection } from "./events/Connection";
import http from "http";
@@ -50,6 +56,8 @@ export class Server {
await initDatabase();
await Config.init();
await initEvent();
+ await Sentry.init();
+
if (!this.server.listening) {
this.server.listen(this.port);
console.log(`[Gateway] online on 0.0.0.0:${this.port}`);
diff --git a/src/util/util/Sentry.ts b/src/util/util/Sentry.ts
new file mode 100644
index 00000000..3e7bb97b
--- /dev/null
+++ b/src/util/util/Sentry.ts
@@ -0,0 +1,102 @@
+import { Config } from "./Config";
+import { yellow } from "picocolors";
+
+import express from "express";
+import * as SentryNode from "@sentry/node";
+import * as Tracing from "@sentry/tracing";
+import * as Integrations from "@sentry/integrations";
+
+// Work around for when bundle calls api/etc
+let errorHandlersUsed = false;
+
+export const Sentry = {
+ /** Call BEFORE registering your routes */
+ init: async (app?: express.Application) => {
+ const { enabled, endpoint, traceSampleRate, environment } =
+ Config.get().sentry;
+ if (!enabled) return;
+
+ if (!!SentryNode.getCurrentHub().getClient()) return; // we've already initialised sentry
+
+ console.log("[Sentry] Enabling sentry...");
+
+ if (traceSampleRate >= 0.8) {
+ console.log(
+ `[Sentry] ${yellow(
+ "Your sentry trace sampling rate is >= 80%. For large loads, this may degrade performance.",
+ )}`,
+ );
+ }
+
+ SentryNode.init({
+ dsn: endpoint,
+ integrations: [
+ new SentryNode.Integrations.Http({ tracing: true }),
+ new Tracing.Integrations.Express({ app }),
+ new Tracing.Integrations.Mysql(),
+ new Integrations.RewriteFrames({
+ root: __dirname,
+ }),
+ ],
+ tracesSampleRate: traceSampleRate, // naming?
+ environment,
+ });
+
+ SentryNode.addGlobalEventProcessor((event, hint) => {
+ if (event.transaction) {
+ // Rewrite things that look like IDs to `:id` for sentry
+ event.transaction = event.transaction
+ .split("/")
+ .map((x) => (!parseInt(x) ? x : ":id"))
+ .join("/");
+ }
+
+ // TODO: does this even do anything?
+ delete event.request?.cookies;
+ if (event.request?.headers) {
+ delete event.request.headers["X-Real-Ip"];
+ delete event.request.headers["X-Forwarded-For"];
+ delete event.request.headers["X-Forwarded-Host"];
+ delete event.request.headers["X-Super-Properties"];
+ }
+
+ if (event.breadcrumbs) {
+ event.breadcrumbs = event.breadcrumbs.filter((x) => {
+ // Filter breadcrumbs that we don't care about
+ if (x.message?.includes("identified as")) return false;
+ if (x.message?.includes("[WebSocket] closed")) return false;
+ if (
+ x.message?.includes(
+ "Got Resume -> cancel not implemented",
+ )
+ )
+ return false;
+ if (x.message?.includes("[Gateway] New connection from"))
+ return false;
+
+ return true;
+ });
+ }
+
+ return event;
+ });
+
+ if (app) {
+ app.use(SentryNode.Handlers.requestHandler());
+ app.use(SentryNode.Handlers.tracingHandler());
+ }
+ },
+
+ /** Call AFTER registering your routes */
+ errorHandler: (app: express.Application) => {
+ if (!Config.get().sentry.enabled) return;
+ if (errorHandlersUsed) return;
+ errorHandlersUsed = true;
+
+ app.use(SentryNode.Handlers.errorHandler());
+ app.use(function onError(err: any, req: any, res: any, next: any) {
+ res.statusCode = 500;
+ res.end(res.sentry + "\n");
+ });
+ },
+};
diff --git a/src/util/util/index.ts b/src/util/util/index.ts
index 01ef4eae..0012579f 100644
--- a/src/util/util/index.ts
+++ b/src/util/util/index.ts
@@ -20,3 +20,4 @@ export * from "./String";
export * from "./Array";
export * from "./TraverseDirectory";
export * from "./InvisibleCharacters";
+export * from "./Sentry";
|