Skip to content

Commit d53f17c

Browse files
committed
save payload file
1 parent 0d0ffd0 commit d53f17c

File tree

2 files changed

+109
-3
lines changed

2 files changed

+109
-3
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
When running the event-proxy-server, the request are saved in a json file. This folder is where all
2+
the generated files go.

utils/event-proxy-server/src/event-proxy-server.ts

Lines changed: 107 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import * as os from 'os';
66
import * as path from 'path';
77
import * as util from 'util';
88
import * as zlib from 'zlib';
9-
import type { Envelope } from '@sentry/types';
9+
import type { Envelope, EnvelopeItem } from '@sentry/types';
1010
import { parseEnvelope } from '@sentry/utils';
1111

1212
const writeFile = util.promisify(fs.writeFile);
@@ -25,6 +25,98 @@ interface SentryRequestCallbackData {
2525
sentryResponseStatusCode?: number;
2626
}
2727

28+
const TEMPORARY_FILE_PATH = 'payload-files/temporary.json';
29+
30+
function isDateLikeString(str: string): boolean {
31+
// matches strings in the format "YYYY-MM-DD"
32+
const datePattern = /^\d{4}-\d{2}-\d{2}/;
33+
return datePattern.test(str);
34+
}
35+
36+
function extractPathFromUrl(url: string): string {
37+
const localhost = 'http://localhost:3030/';
38+
return url.replace(localhost, '');
39+
}
40+
41+
function addCommaAfterEachLine(data: string): string {
42+
const jsonData = data.trim().split('\n');
43+
44+
const jsonDataWithCommas = jsonData.map((item, index) =>
45+
index < jsonData.length - 1 ? item + ',' : item,
46+
);
47+
48+
return jsonDataWithCommas.join('\n');
49+
}
50+
51+
function recursivelyReplaceData(obj: any) {
52+
for (let key in obj) {
53+
if (typeof obj[key] === 'string' && isDateLikeString(obj[key])) {
54+
obj[key] = `[[ISODateString]]`;
55+
} else if (key.includes('timestamp')) {
56+
obj[key] = `[[timestamp]]`;
57+
} else if (key.includes('_id')) {
58+
obj[key] = `[[ID]]`;
59+
} else if (typeof obj[key] === 'number' && obj[key] > 1000) {
60+
obj[key] = `[[highNumber]]`;
61+
} else if (typeof obj[key] === 'object' && obj[key] !== null) {
62+
recursivelyReplaceData(obj[key]);
63+
}
64+
}
65+
}
66+
67+
function replaceDynamicValues(data: string): string[] {
68+
const jsonData = JSON.parse(data);
69+
70+
recursivelyReplaceData(jsonData);
71+
72+
// change remaining dynamic values
73+
jsonData.forEach((item: any) => {
74+
if (item.trace?.public_key) {
75+
item.trace.public_key = '[[publicKey]]';
76+
}
77+
});
78+
79+
return jsonData;
80+
}
81+
82+
/** This function transforms all dynamic data (like timestamps) from the temporarily saved file.
83+
* The new content is saved into a new file with the url as the filename.
84+
* The temporary file is deleted in the end.
85+
*/
86+
function transformSavedJSON() {
87+
fs.readFile(TEMPORARY_FILE_PATH, 'utf8', (err, data) => {
88+
if (err) {
89+
console.error('Error reading file', err);
90+
return;
91+
}
92+
93+
const jsonData = addCommaAfterEachLine(data);
94+
const transformedJSON = replaceDynamicValues(jsonData);
95+
const objWithReq = transformedJSON[2] as unknown as { request: { url: string } };
96+
97+
if ('request' in objWithReq) {
98+
const url = objWithReq.request.url;
99+
const filepath = `payload-files/${extractPathFromUrl(url)}.json`;
100+
101+
fs.writeFile(filepath, JSON.stringify(transformedJSON, null, 2), err => {
102+
if (err) {
103+
console.error('Error writing file', err);
104+
} else {
105+
console.log(`Successfully modified timestamp in ${filepath}`);
106+
}
107+
});
108+
}
109+
});
110+
111+
fs.unlink(TEMPORARY_FILE_PATH, err => {
112+
if (err) {
113+
console.error('Error deleting file', err);
114+
} else {
115+
console.log(`Successfully deleted ${TEMPORARY_FILE_PATH}`);
116+
}
117+
});
118+
}
119+
28120
/**
29121
* Starts an event proxy server that will proxy events to sentry when the `tunnel` option is used. Point the `tunnel`
30122
* option to this server (like this `tunnel: http://localhost:${port option}/`).
@@ -33,6 +125,8 @@ interface SentryRequestCallbackData {
33125
export async function startEventProxyServer(options: EventProxyServerOptions): Promise<void> {
34126
const eventCallbackListeners: Set<(data: string) => void> = new Set();
35127

128+
console.log(`Proxy server "${options.proxyServerName}" running. Waiting for events...`);
129+
36130
const proxyServer = http.createServer((proxyRequest, proxyResponse) => {
37131
const proxyRequestChunks: Uint8Array[] = [];
38132

@@ -50,15 +144,25 @@ export async function startEventProxyServer(options: EventProxyServerOptions): P
50144
? zlib.gunzipSync(Buffer.concat(proxyRequestChunks)).toString()
51145
: Buffer.concat(proxyRequestChunks).toString();
52146

53-
let envelopeHeader = JSON.parse(proxyRequestBody.split('\n')[0]);
147+
fs.writeFile(TEMPORARY_FILE_PATH, `[${proxyRequestBody}]`, err => {
148+
if (err) {
149+
console.error(`Error writing file ${TEMPORARY_FILE_PATH}`, err);
150+
} else {
151+
console.log(`Successfully wrote to ${TEMPORARY_FILE_PATH}`);
152+
}
153+
});
154+
155+
transformSavedJSON();
156+
157+
const envelopeHeader: EnvelopeItem[0] = JSON.parse(proxyRequestBody.split('\n')[0]);
54158

55159
if (!envelopeHeader.dsn) {
56160
throw new Error(
57161
'[event-proxy-server] No dsn on envelope header. Please set tunnel option.',
58162
);
59163
}
60164

61-
const { origin, pathname, host } = new URL(envelopeHeader.dsn);
165+
const { origin, pathname, host } = new URL(envelopeHeader.dsn as string);
62166

63167
const projectId = pathname.substring(1);
64168
const sentryIngestUrl = `${origin}/api/${projectId}/envelope/`;

0 commit comments

Comments
 (0)