Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import { Router } from "express";
/**
* Houses the Sentry tunnel
*/
const app = Router();
if (process.env.SENTRY_DSN && process.env.SENTRY_TUNNEL_PROJECT_IDS) {
// only register the endpoint if the environment variables are set
const SENTRY_HOST = new URL(process.env.SENTRY_DSN).hostname;
const SENTRY_PROJECT_IDS: string[] =
process.env.SENTRY_TUNNEL_PROJECT_IDS.split(",");
app.post("/_meta", async (req, res) => {
try {
// read POST data as raw text
const envelope = await new Promise<string>((res) => {
let data = "";
req.setEncoding("utf8");
req.on("data", (chunk) => {
data += chunk;
});
req.on("end", () => {
res(data);
});
});
// sentry sends data to the tunnel in 3 different parts, all JSON separated by newlines
// the following reads the header line and verifies the DSN provided matches the constants above
const piece = envelope.split("\n")[0];
const header = JSON.parse(piece);
const dsn = new URL(header["dsn"]);
const project_id = dsn.pathname?.replace("/", "");
if (dsn.hostname !== SENTRY_HOST) {
throw new Error(`Invalid sentry hostname: ${dsn.hostname}`);
}
if (!project_id || !SENTRY_PROJECT_IDS.includes(project_id)) {
throw new Error(`Invalid sentry project id: ${project_id}`);
}
// forward the data to sentry
const upstream_sentry_url = `https://${SENTRY_HOST}/api/${project_id}/envelope/`;
await fetch(upstream_sentry_url, {
method: "POST",
body: envelope,
});
res.json({});
} catch (e) {
console.error("error tunneling to sentry", e);
res.status(500).json({ error: "error tunneling" });
}
});
}
export default app;