-
Notifications
You must be signed in to change notification settings - Fork 468
Expand file tree
/
Copy pathapp.js
More file actions
166 lines (148 loc) · 6.48 KB
/
app.js
File metadata and controls
166 lines (148 loc) · 6.48 KB
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
164
165
166
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const fs = require('fs');
const express = require('express');
const rateLimit = require('express-rate-limit');
const path = require('path');
const favicon = require('serve-favicon');
const logger = require('morgan');
const bodyParser = require('body-parser');
const config = require('./config');
const { log, LogLevel } = require('./log');
const users = require('./routes/users');
const readings = require('./routes/readings');
const meters = require('./routes/meters');
const preferences = require('./routes/preferences');
const login = require('./routes/login');
const verification = require('./routes/verification');
const groups = require('./routes/groups');
const version = require('./routes/version');
const createRouterForReadings = require('./routes/unitReadings').createRouter;
const createRouterForCompareReadings = require('./routes/compareReadings').createRouter;
const baseline = require('./routes/baseline');
const maps = require('./routes/maps');
const logs = require('./routes/logs');
const obvius = require('./routes/obvius');
const csv = require('./routes/csv');
const conversionArray = require('./routes/conversionArray');
const units = require('./routes/units');
const conversions = require('./routes/conversions');
const ciks = require('./routes/ciks');
const crypto = require('node:crypto');
// Limit the rate of overall requests to OED
// Note that the rate limit may make the automatic test return the value of 429. In that case, the limiters below need to be increased.
// TODO Verify that user see the message returned, see https://express-rate-limit.mintlify.app/reference/configuration#message
// Create a limit of 200 requests/5 seconds
const generalLimiter = rateLimit({
windowMs: 5 * 1000, // 5 seconds
limit: 200, // 200 requests
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
legacyHeaders: false, // Disable the `X-RateLimit-*` headers
// If rate limit is 10, OED won't load and bad things will happen
message: async (req, res) => {
const string = `
<h1 style='text-align:center'>
You have been rate limited by your OED site.
</h1>
<h2 style ='text-align:center'>
We suggest you try these in this order:
</h2>
<h2
style='text-align:center'>
</h2>
<div>
<ol style = "text-align: center; list-style-position: inside;">
<li>
Click the 'Refresh this page' button below to try again.
</li>
<li>
If you keep returning to this page wait longer and click 'Refresh this page' button.
</li>
<li>
Contact your site to find why the rate limit is denying access to the OED site.
</li>
</ol>
</div>
<h3 style='text-align:center'>
<button onClick='window.location.reload();'>
Refresh this page
</button>
</h3>
`
return string
}
});
// Apply the limit to overall requests
const app = express().use(generalLimiter);
// This is limiting 3D-Graphic
const threeDLimiter = rateLimit({
/* Rationale: Each hour/day returned by 3D will have 365 points if a full year.
When returned for each hour in the day that is 365 * 24 = 8760 (max possible). For line graphics,
OED limits the number of points to 1440. Thus, a 3D request is 8760 / 1440 = 6 times
more data in the worst case of 3D vs. line graphics. Since OED limits to 200 request
per 5 seconds in general (that includes line graphics), the limit for 3D will be
200 / 6 = 33. Note the limit used to be much less because the database work for 3D
could be high. This is now resolved so it is around the same time as line graphics.
It is unclear a lower limit is actually needed but done to be safe. */
windowMs: 5 * 1000, // 5 seconds
limit: 33,
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
legacyHeaders: false // Disable the `X-RateLimit-*` headers
});
app.use('/api/unitReadings/threeD/meters', threeDLimiter);
// Limit the number of raw exports to 5 per 5 seconds
const exportRawLimiter = rateLimit({
windowMs: 5 * 1000, // 5 seconds
limit: 5, // 5 requests
standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
legacyHeaders: false // Disable the `X-RateLimit-*` headers
});
// Apply the raw export limit
app.use('/api/readings/line/raw/meters', exportRawLimiter);
// If other logging is turned off, there's no reason to log HTTP requests either.
// TODO: Potentially modify the Morgan logger to use the log API, thus unifying all our logging.
if (log.level !== LogLevel.SILENT) {
app.use(logger('dev'));
}
app.use(favicon(path.join(__dirname, '..', 'client', 'public', 'favicon.ico')));
app.use(bodyParser.json({ limit: '50mb' }));
app.use(bodyParser.urlencoded({ extended: false, limit: '50mb' }));
app.use('/api/users', users);
app.use('/api/meters', meters);
app.use('/api/readings', readings);
app.use('/api/preferences', preferences);
app.use('/api/login', login);
app.use('/api/groups', groups);
app.use('/api/verification', verification);
app.use('/api/version', version);
app.use('/api/unitReadings', createRouterForReadings());
app.use('/api/compareReadings', createRouterForCompareReadings());
app.use('/api/baselines', baseline);
app.use('/api/maps', maps);
app.use('/api/logs', logs);
app.use('/api/obvius', obvius);
app.use('/api/csv', csv);
app.use('/api/conversion-array', conversionArray);
app.use('/api/units', units);
app.use('/api/conversions', conversions);
app.use('/api/ciks', ciks);
app.use(express.static(path.join(__dirname, '..', 'client', 'public')));
const router = express.Router();
// Accept all other endpoint requests which will be handled by the client router
router.get('*', (req, res) => {
fs.readFile(path.resolve(__dirname, '..', 'client', 'index.html'), (err, html) => {
const subdir = config.subdir || '/';
let htmlPlusData = html.toString().replace('SUBDIR', subdir);
//assigns a value to the nonce in order to check for authenticity
const nonce = crypto.randomBytes(16).toString('base64url');
htmlPlusData = htmlPlusData.replace(/{{nonce}}/g, nonce);
res.setHeader('Content-Security-Policy', `default-src 'self'; img-src 'self' data: ; font-src 'self' https://maxcdn.bootstrapcdn.com ; media-src 'self'; script-src 'self' 'nonce-${nonce}' ; style-src 'self' 'nonce-${nonce}' 'unsafe-inline';`)
res.send(htmlPlusData);
});
});
app.use(router);
app.use((req, res) => {
res.status(404).send('<h1>404 Not Found</h1>');
});
module.exports = app;