This repository has been archived by the owner on Nov 27, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathserver.js
170 lines (149 loc) · 4.96 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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/* eslint-disable @typescript-eslint/no-var-requires */
const express = require("express");
const compression = require("compression");
const morgan = require("morgan");
const { createRequestHandler } = require("@remix-run/express");
const path = require("path");
const helmet = require("helmet");
const dotenv = require("dotenv-safe");
const { createHttpTerminator } = require("http-terminator");
const Redis = require("ioredis");
const { wrapExpressCreateRequestHandler } = require("@sentry/remix");
/* eslint-enable @typescript-eslint/no-var-requires */
const BUILD_DIR = path.join(process.cwd(), "build");
let isOnline = true;
// Fail fast on missing configuration parameters
dotenv.config();
const app = express();
// Set security-related http headers
app.use(
helmet({
//We have to disable this for embedding the useid widget
crossOriginEmbedderPolicy: false,
contentSecurityPolicy: {
directives: {
// unfortunately we have to allow unsafe inline scripts, as otherwise Remix does not work;
// issue is tracked here: https://github.com/remix-run/remix/issues/183
scriptSrc: [
"'self'",
"*.sentry.io",
"'unsafe-inline'",
process.env.USEID_DOMAIN,
],
frameSrc: [process.env.USEID_DOMAIN, "mailto:"],
"form-action":
process.env.USE_TEST_CSP === "true"
? "self localhost:3000 https://grund-stag.dev.ds4g.net e4k-portal.een.elster.de"
: "self https://www.grundsteuererklaerung-fuer-privateigentum.de/ www.elster.de",
// allow connections from WebSocket for development tooling
connectSrc:
process.env.NODE_ENV === "development"
? ["*"]
: ["'self'", "plausible.io", "*.sentry.io"],
upgradeInsecureRequests:
process.env.NODE_ENV === "development" ? null : [],
},
},
referrerPolicy: {
// keep referrer for "internal" traffic to facilitate plausible tracking
policy: "strict-origin-when-cross-origin",
},
})
);
app.use((_req, res, next) => {
res.setHeader("Permissions-Policy", "clipboard-write=(self)");
next();
});
app.use(compression());
// Remix fingerprints its assets so we can cache forever.
app.use(
"/build",
express.static("public/build", { immutable: true, maxAge: "1y" })
);
// Everything else (like favicon.ico) is cached for an hour. You may want to be
// more aggressive with this caching.
app.use(express.static("public", { maxAge: "1h" }));
app.use(
morgan("tiny", {
skip:
process.env.LOG_SUCCESSFUL_REQUESTS === "true"
? false
: function (req, res) {
return res.statusCode < 400;
},
})
);
app.set("trust proxy", true);
const getLoadContext = (req) => ({
clientIp: req.ip,
online: isOnline,
});
const createSentryRequestHandler =
wrapExpressCreateRequestHandler(createRequestHandler);
app.all(
"*",
process.env.NODE_ENV === "development"
? (req, res, next) => {
purgeRequireCache();
return createSentryRequestHandler({
build: require(BUILD_DIR),
getLoadContext,
mode: process.env.NODE_ENV,
})(req, res, next);
}
: createSentryRequestHandler({
build: require(BUILD_DIR),
getLoadContext,
mode: process.env.NODE_ENV,
})
);
const port = process.env.PORT || 3000;
const server = app.listen(port, () => {
console.log(`Express server listening on port ${port}`);
});
const httpTerminator = createHttpTerminator({
server,
});
if (!global.ioredis) {
global.ioredis = new Redis(process.env.REDIS_URL, {
maxRetriesPerRequest: null,
});
console.log("Redis connection opened");
}
const shutdown = async (signal) => {
if (server.listening) {
console.log(`${signal} received: closing HTTP server gracefully`);
isOnline = false;
await httpTerminator.terminate();
console.log("HTTP server closed");
if (global.__registeredQueues) {
const names = Object.keys(global.__registeredQueues);
for (const name of names) {
const worker = global.__registeredQueues[name].worker;
await worker.close();
console.log(`Worker ${name} stopped`);
}
}
if (global.ioredis) {
await global.ioredis.quit();
global.ioredis = undefined;
console.log("Redis connection closed");
}
}
};
const SIGINT = "SIGINT";
const SIGTERM = "SIGTERM";
process.on(SIGINT, async () => await shutdown(SIGINT));
process.on(SIGTERM, async () => await shutdown(SIGTERM));
function purgeRequireCache() {
// purge require cache on requests for "server side HMR" this won't let
// you have in-memory objects between requests in development,
// alternatively you can set up nodemon/pm2-dev to restart the server on
// file changes, but then you'll have to reconnect to databases/etc on each
// change. We prefer the DX of this, so we've included it for you by default
for (const key in require.cache) {
if (key.startsWith(BUILD_DIR)) {
delete require.cache[key];
}
}
}