-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathserver-main.ts
More file actions
138 lines (121 loc) · 5.06 KB
/
server-main.ts
File metadata and controls
138 lines (121 loc) · 5.06 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
/*
# Copyright 2020 Open Climate Tech Contributors
# Licensed under the Apache License, Version 2.0
*/
'use strict';
// Custom Next.js + Express server.
//
// Express handles middleware (helmet, cookies, body parsing) and the routes that
// require Express-specific features (passport OAuth, SSE). Everything else
// (pages, pages/api/*, static assets) is handled by the Next.js request handler.
/* eslint quotes: 0 */
import express, { Request, Response, NextFunction } from 'express';
import bodyParser from 'body-parser';
import helmet from 'helmet';
import path from 'path';
import next from 'next';
const cookieParser = require('cookie-parser');
const oct_utils = require('./server-src/oct_utils');
import * as services from './server-src/services';
const logger = oct_utils.getLogger('main');
require('source-map-support').install();
const dev = process.env.NODE_ENV !== 'production';
const PORT = parseInt(process.env.PORT || '3141', 10);
// Next.js needs the project root (where .next/ and pages/ live). When this
// file is compiled to dist/, __dirname points to dist/, so use cwd instead
// (npm start / npm run dev both run from project root).
const projRootDir = process.cwd();
const nextApp = next({ dev, dir: projRootDir });
const nextHandler = nextApp.getRequestHandler();
async function main() {
await nextApp.prepare();
const app = express();
// ---------------------------------------------------------------------------
// Logging + dev CORS
// ---------------------------------------------------------------------------
app.use(function (req: Request, res: Response, next: NextFunction) {
const headers = Object.assign({}, req.headers);
headers.url = req.originalUrl || req.url;
logger.info('request Headers: %s', JSON.stringify(headers));
if (process.env.NODE_ENV === 'development') {
if (req.header('origin')) {
res.setHeader('Access-Control-Allow-Origin', req.header('origin') || '');
}
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Allow-Headers', 'origin, content-type, accept');
}
next();
});
// ---------------------------------------------------------------------------
// Security headers (CSP relaxed in dev for Next.js HMR)
// ---------------------------------------------------------------------------
app.use(helmet());
const trusted = ["'self'"];
if (process.env.NODE_ENV === 'development') {
trusted.push('http://localhost:*');
}
app.use(
helmet.contentSecurityPolicy({
directives: {
defaultSrc: trusted,
scriptSrc: [
"'unsafe-inline'",
"'unsafe-eval'", // required by Next.js dev mode
'https://www.googletagmanager.com',
'*.googletagmanager.com',
'unpkg.com',
].concat(trusted),
styleSrc: [
"'unsafe-inline'",
'*.w3schools.com',
'cdnjs.cloudflare.com',
'unpkg.com',
].concat(trusted),
imgSrc: [
'data:',
'blob:',
'www.googletagmanager.com',
'storage.googleapis.com',
'*.openstreetmap.org',
].concat(trusted),
mediaSrc: ['storage.googleapis.com'].concat(trusted),
connectSrc: ['www.google-analytics.com'].concat(trusted),
},
})
);
app.use(helmet.crossOriginEmbedderPolicy({ policy: 'credentialless' }));
// ---------------------------------------------------------------------------
// Body / cookies / https redirect
// ---------------------------------------------------------------------------
app.use(cookieParser());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(function (req: Request, res: Response, next: NextFunction) {
if (req.headers['x-forwarded-proto'] && req.headers['x-forwarded-proto'] === 'http') {
return res.redirect(['https://', req.get('Host'), req.url].join(''));
}
next();
});
// ---------------------------------------------------------------------------
// Static webroot pages (terms.html, privacy.html, disclaimer.html, ...)
// ---------------------------------------------------------------------------
app.use('/legal', express.static(path.join(projRootDir, 'webroot')));
// ---------------------------------------------------------------------------
// Initialize services: db, passport, pubsub, sse + Express-only API routes.
// ---------------------------------------------------------------------------
await services.initServices(app);
// ---------------------------------------------------------------------------
// Hand everything else off to Next.js (pages + pages/api + static assets).
// ---------------------------------------------------------------------------
app.all('*', (req: Request, res: Response) => {
return nextHandler(req, res);
});
app.listen(PORT, () => {
logger.info(`WildfireCheck listening on port ${PORT}`);
logger.info('Press Ctrl+C to quit.');
});
}
main().catch((err) => {
logger.error('Fatal startup error: %s', err && err.stack ? err.stack : err);
process.exit(1);
});