-
Notifications
You must be signed in to change notification settings - Fork 1
/
server.js
154 lines (124 loc) · 5.25 KB
/
server.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
const helpers = require('./helpers');
const scanner = require('./scanner');
const tasksController = require('./controllers/tasks');
const logger = helpers.getLogger("server"); // generate logger for 'server' module
const morgan = require('morgan');
const jsend = require('jsend');
const addReqId = require('express-request-id')();
const _ = require('lodash');
const Prometheus = require('prom-client')
const config = require('config'); // the name can be misleading - this is an npm package
const express = require('express');
const listEndpoints = require('express-list-endpoints');
const app = express();
const AWS = require('aws-sdk');
const uncaughtErrors = new Prometheus.Counter({
name: 'mousetrap_uncaught_errors',
help: 'The number of uncaught errors'
});
const start = async () => {
const _logger = logger.child({ event: "initialization" });
if (process.env.LOG_LEVEL === "trace")
AWS.config.logger = console;
// create a connection with a running clamav daemon
//
// configuring max init attempts and wait interval between attempts to be backward-compatible
// works even without altering the config yaml
let maxInitAttemps;
let initWaitInterval;
config.has("clamd.maxInitAttemps") ? maxInitAttemps = config.get("clamd.maxInitAttemps") : maxInitAttemps = 5;
config.has("clamd.initWaitInterval") ? initWaitInterval = config.get("clamd.initWaitInterval") : initWaitInterval = 5000;
for (let i = 0; i < maxInitAttemps; i++) {
try {
await scanner.initialize();
_logger.info({ step: "init_clamd", success: true })
break;
} catch (error) {
_logger.error({ step: "init_clamd", success: false, error: { message: error.message, code: error.code, stack: error.stack }, msg: `Could not initialize connection to ClamAV, Attempt ${i + 1} out of ${maxInitAttemps}` })
}
if (i === maxInitAttemps - 1) {
_logger.error({ step: "init_controllers", success: false, error: { message: error.message, code: error.code, stack: error.stack }, msg: "Could not initialize connection to ClamAV, aborting startup." })
process.exit(2);
}
await helpers.delay(initWaitInterval)
}
// initialize controller
try {
await tasksController.initialize();
_logger.info({ step: "init_controllers", success: true })
} catch (error) {
_logger.error({ step: "init_controllers", success: false, error: { message: error.message, code: error.code, stack: error.stack }, msg: "Could not initialize all controllers, aborting startup." })
process.exit(1);
}
Prometheus.collectDefaultMetrics()
// add a request id to every request
app.use(addReqId);
// remove the information about what type of framework is the site running on
app.disable('x-powered-by');
// morgan logs
morgan.token('id', (req) => req.id); // add the request id to every express log
app.use(morgan(':id :method :url :status :response-time'));
// parse all request as json objects, and not regular text
app.use(express.json());
/////////////////////////////////////////////////////
// list all routes
app.get('/favicon.ico', (req, res) => res.sendStatus(404));
app.get('/', (_, res) => res.json(listEndpoints(app)));
app.get('/metrics', (req, res) => {
res.set('Content-Type', Prometheus.register.contentType)
res.end(Prometheus.register.metrics())
});
app.use('/health', require('./routes/healthchecks'));
app.use('/api/tasks', require('./routes/tasks'));
/////////////////////////////////////////////////////
app.use(noRoute);
app.use(onError);
/////////////////////////////////////////////////////
/****
* HELPERS
****/
function noRoute(req, res, next) {
const error = new Error("Not Found");
error.status = 404;
next(error);
}
function onError(err, req, res, next) {
// 1. set the status of the response
res.status(err.status || 500);
// 2. create the error object
const errObj = jsend.error({
code: res.statusCode,
message: err.message,
data: {
requestId: req.id,
error: helpers.isDevelopment() ? err.stack : undefined, // undefined elements arnt copied
path: req.path
}
});
// 3. merge additional data embedded in the error into the error object
if (err.extension)
errObj.data = _.merge(errObj.data, err.extension)
// 4. log the error stack
if (res.statusCode !== 404)
logger.error(req.path, err.stack);
res.json(errObj);
}
}
const gracefulShutdown = async () => {
logger.warn({ msg: "Received shutdown request." })
await tasksController.shutdown();
logger.info({ msg: "Graceful shutdown success." })
}
process.on('uncaughtException', (err) => {
console.log((new Date).toUTCString() + ' uncaughtException:');
console.log(err);
uncaughtErrors.inc();
});
process.on('unhandledRejection', (err) => {
console.log((new Date).toUTCString() + ' unhandledRejection');
console.log(err);
uncaughtErrors.inc();
});
app.gracefulShutdown = gracefulShutdown;
app.start = start;
module.exports = app;