-
-
Notifications
You must be signed in to change notification settings - Fork 9
/
index.js
119 lines (101 loc) · 3.23 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
import {readFile} from "fs/promises";
import fetch from "node-fetch";
import bcrypt from "bcrypt";
import express from "express";
import winston from "winston";
import expressWinston from "express-winston";
import bodyParser from "body-parser";
import helmet from "helmet";
import asyncWrap from "express-async-wrap";
import config from "./lib/config.js";
import {IncrementalsPlugin} from "./IncrementalsPlugin.js";
const packageJson = JSON.parse(await readFile(new URL("./package.json", import.meta.url)));
const app = express()
const port = config.PORT
const logger = winston.createLogger({
level: "debug",
transports: [
new winston.transports.Console({})
],
format: winston.format.combine(
winston.format.timestamp(),
winston.format.align(),
winston.format.splat(),
winston.format.printf(info => `${info.timestamp} ${info.level}: ${info.message}`)
),
exitOnError: false, // do not exit on handled exceptions
});
/* Logger after routing */
app.use(expressWinston.logger({
winstonInstance: logger,
}));
app.use(helmet());
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({extended: false}))
// parse application/json
app.use(bodyParser.json())
const healthchecks = {
jenkins: async function () {
const jenkinsOpts = {};
if (!config.JENKINS_AUTH) {
return {jenkins: "no_auth"};
}
jenkinsOpts.headers = {"Authorization": "Basic " + new Buffer.from(config.JENKINS_AUTH, "utf8").toString("base64")};
const response = await fetch(config.JENKINS_HOST + "/whoAmI/api/json", jenkinsOpts)
if (response.status !== 200) {
throw new Error("Unable to talk to jenkins");
}
await response.json();
return {jenkins: "ok"}
}
}
app.get("/readiness", asyncWrap(async (req, res) => {
res.status(200);
let responseJson = {errors: []};
for (const key of Object.keys(healthchecks)) {
try {
responseJson = {...responseJson, ...(await healthchecks[key]())};
} catch (e) {
logger.error(`Healthcheck: ${e}`);
responseJson.errors.push(key);
res.status(500);
}
}
res.json(responseJson);
}));
app.get("/liveness", asyncWrap(async (_req, res) => {
res.status(200).json({
status: "OK",
version: packageJson.version
});
}));
const encodedPassword = bcrypt.hashSync(config.PRESHARED_KEY, 10);
app.post("/", asyncWrap(async (req, res) => {
const authorization = (req.get("Authorization") || "").replace(/^Bearer /, "");
// we bcrypt so nobody can learn from timing attacks
// https://www.npmjs.com/package/bcrypt#a-note-on-timing-attacks
const check = await bcrypt.compare(authorization, encodedPassword);
if (!check) {
res.status(403).send("Not authorized");
return
}
const context = {log: logger};
const obj = new IncrementalsPlugin(context, {body: req.body});
res.send((await obj.main()).body);
}))
/*Error handler goes last */
app.use(function (err, req, res, next) {
logger.error(err.stack)
res.status(err.status || err.code || 400).send(err.message || "Unknown error");
next()
})
// Handle ^C
process.on("SIGINT", shutdown);
// Do graceful shutdown
function shutdown() {
logger.info("Got SIGINT");
process.exit();
}
app.listen(port, () => {
logger.info(`Incrementals listening at http://localhost:${port}`)
})