/
app.ts
136 lines (120 loc) · 4.15 KB
/
app.ts
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
import * as path from "path";
import * as express from "express";
import * as serveStatic from "serve-static";
import * as compression from "compression";
import * as cookieParser from "cookie-parser";
import * as cookieSignature from "cookie-signature";
import * as chalk from "chalk";
import * as morgan from "morgan";
import flash = require("connect-flash");
import {
// Constants
PORT, STATIC_ROOT, VERSION_NUMBER, VERSION_HASH, COOKIE_OPTIONS,
// Configuration
config
} from "./common";
import {
User
} from "./schema";
import { setupRoutes as graphQlRoutes } from "./routes/api/graphql";
// Set up Express and its middleware
export let app = express();
app.use(compression());
let cookieParserInstance = cookieParser(undefined, COOKIE_OPTIONS as cookieParser.CookieParseOptions);
app.use(cookieParserInstance);
morgan.token("sessionid", (request, response) => {
const FAILURE_MESSAGE = "Unknown session";
if (!request.cookies["connect.sid"]) {
return FAILURE_MESSAGE;
}
let rawID: string = request.cookies["connect.sid"].slice(2);
let id = cookieSignature.unsign(rawID, config.secrets.session);
if (typeof id === "string") {
return id;
}
return FAILURE_MESSAGE;
});
morgan.format("hackgt", (tokens, request, response) => {
let statusColorizer: (input: string) => string = input => input; // Default passthrough function
if (response.statusCode >= 500) {
statusColorizer = chalk.default.red;
}
else if (response.statusCode >= 400) {
statusColorizer = chalk.default.yellow;
}
else if (response.statusCode >= 300) {
statusColorizer = chalk.default.cyan;
}
else if (response.statusCode >= 200) {
statusColorizer = chalk.default.green;
}
return [
tokens.date(request, response, "iso"),
tokens["remote-addr"](request, response),
tokens.sessionid(request, response),
tokens.method(request, response),
tokens.url(request, response),
statusColorizer(tokens.status(request, response)),
tokens["response-time"](request, response), "ms", "-",
tokens.res(request, response, "content-length")
].join(" ");
});
app.use(morgan("hackgt"));
app.use(flash());
// Throw and show a stack trace on an unhandled Promise rejection instead of logging an unhelpful warning
process.on("unhandledRejection", err => {
throw err;
});
// Check for number of admin users and warn if none
(async () => {
let users = await User.find({"admin": true});
if (users.length !== 0) {
return;
}
console.warn("No admin users are configured; admins can be added in config.json");
})().catch(err => {
throw err;
});
// Auth needs to be the first route configured or else requests handled before it will always be unauthenticated
import {authRoutes} from "./routes/auth";
app.use("/auth", authRoutes);
// Metrics
import {trackEvent} from "./middleware";
app.use((request, response, next) => {
// Track endpoints without extensions
if (path.extname(request.url) === "") {
let email: string | undefined = request.user ? request.user.email : undefined;
trackEvent("visit", request, email);
}
next();
});
let apiRouter = express.Router();
// API routes go here
import {userRoutes, registrationRoutes} from "./routes/api/user";
apiRouter.use("/user/:uuid", userRoutes);
apiRouter.use("/registration", registrationRoutes);
import {settingsRoutes} from "./routes/api/settings";
apiRouter.use("/settings", settingsRoutes);
app.use("/api", apiRouter);
// Uploaded file downloading for admins
import {uploadsRoutes} from "./routes/uploads";
app.use("/uploads", uploadsRoutes);
// User facing routes
import {templateRoutes} from "./routes/templates";
app.use("/", templateRoutes);
app.route("/version").get((request, response) => {
response.json({
"version": VERSION_NUMBER,
"hash": VERSION_HASH,
"node": process.version
});
});
app.use("/node_modules", serveStatic(path.resolve(__dirname, "../node_modules")));
app.use("/js", serveStatic(path.resolve(STATIC_ROOT, "js")));
app.use("/css/theme.css", serveStatic(config.style.theme));
app.use("/favicon.ico", serveStatic(config.style.favicon));
app.use("/css", serveStatic(path.resolve(STATIC_ROOT, "css")));
graphQlRoutes(app);
app.listen(PORT, () => {
console.log(`Registration system v${VERSION_NUMBER} @ ${VERSION_HASH} started on port ${PORT}`);
});