-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
163 lines (153 loc) · 4.68 KB
/
index.js
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
import express from 'express';
import fs from 'fs';
import sqlite3 from 'sqlite3';
import { open } from 'sqlite';
import SQL from 'sql-template-strings';
import defaultConfig from './default-config.json' assert { type: 'json' };
import configOverrides from './config.json' assert { type: 'json' };
import https from 'https';
import bodyParser from 'body-parser';
import { encode } from 'html-entities';
const config = { ...defaultConfig, ...configOverrides };
//setup express app
const app = express();
app.use(express.static('public'));
app.use(bodyParser.json({ limit: '1mb' }));
app.use(bodyParser.urlencoded({ limit: '1mb', extended: true }));
//setup db
const db = await open({
filename: 'database.db',
driver: sqlite3.Database
});
await db.exec(
`create table if not exists request (id integer primary key, channel text, method text, headers text, query text, body text, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP)`
);
await db.exec(`CREATE INDEX if not exists request_channel ON request(channel)`);
await db.exec(
`CREATE INDEX if not exists request_timestamp ON request(timestamp)`
);
try {
await db.exec(`alter table request add column ipAddress text`);
} catch (e) {
//ignore
}
//helper functions
function json(obj) {
return JSON.stringify(obj, null, 5);
}
function saveRequest(req, channel) {
const { method } = req;
const headers = json(req.headers);
const query = json(req.query);
const body = json(req.body);
const ipAddress = req.ip;
db.run(
SQL`Insert into request (channel, method, headers, query, body, ipAddress ) values (${channel}, ${method}, ${headers}, ${query}, ${body}, ${ipAddress}) `
);
}
let requestTimestamps = [];
const limitPerMinute = 60;
function rateLimiter(req, res, next) {
const now = Math.round(new Date().getTime() / 1000);
const oneMinAgo = now - 60;
if (requestTimestamps.length >= limitPerMinute) {
// get rid of old timestamps
requestTimestamps = requestTimestamps.filter((ts) => ts > oneMinAgo);
}
if (requestTimestamps.length >= limitPerMinute) {
return res
.status(503)
.send(`Service only accepts at most ${limitPerMinute} requests a minute`);
}
requestTimestamps.push(now);
return next();
}
//respond to post and get messages
const channelReceiver = (req, res) => {
console.log(req.baseUrl);
const channel = req.params.channel;
saveRequest(req, channel);
res.send(
`successfully logged to ${channel}. View log at <a href="/log/${channel}">/log/${channel}</a>`
);
};
app.all('/channel/:channel', rateLimiter, channelReceiver);
app.get('/log/:channel', async (req, res) => {
const channel = req.params.channel;
const result = await db.all(
'SELECT * FROM request WHERE channel = ? order by timestamp desc limit 1000',
channel
);
const logElementRow = (key, val) => {
return `
<div class="log-element-row">
<div class="log-element-cell log-element-key">
${key}
</div>
<pre class="log-element-cell log-element-val">
${encode(val)}
</pre>
</div>
`;
};
let logContent = '';
for (const row of result) {
logContent += `
<div class="log-element">
${logElementRow('timestamp', row.timestamp)}
${logElementRow('method', row.method)}
${logElementRow('headers', row.headers)}
${logElementRow('ipAddress', row.ipAddress)}
${logElementRow('query', row.query)}
${logElementRow('body', row.body)}
</div>
`;
}
res.send(
`
<html>
<link rel="stylesheet" type="text/css" href="../main.css" />
<body>
<div id="head">
<a href="/"><img src="../Rob-burrito_a_reel_to_reel_old_style_recorder_on_the_moon_conne_786624a8-c494-46eb-891a-928b026461c7.png"
style="float:left"></a>
<div>
<h1>
<a href="/">Request Recorder</a>
</h1>
</div>
</div>
<div id="content">
<h4>Simulate a post in a new tab to request url</h4>
<form method="post" target="_blank" action="${
'/channel/' + channel
}" id="request-form">
<div>
<input name="testField" placeholder="Enter a value for testField" />
</div>
<div>
<input type="submit" />
</div>
</form>
<hr>
<h2>Log for ${encode(req.params.channel)}</h2>
${logContent}
</div>
</html>
`
);
});
app.listen(config.port, () => {
console.log(`Example app listening on port ${config.port}`);
});
let key = '';
let cert = '';
if (config.certLocation) {
cert = fs.readFileSync(config.certLocation);
key = fs.readFileSync(config.keyLocation);
}
if (key) {
https.createServer({ key, cert }, app).listen(443);
} else {
console.log('no ssl key and cert set in config.json');
}