-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathserver.js
155 lines (131 loc) · 7.08 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
/**
* Custom Server for Next.js Application
*
* Overview:
* This server script enhances the Next.js application by enabling custom server functionality.
* It primarily adds support for serving static files from a directory that's specified at deploy time,
* facilitating the use of Docker volumes or similar deployment strategies.
*
* server side session storage (cookies) for keycloak authentication are configured here
*
* Note:
* Next.js, by default, serves static files from the 'public' directory, which requires contents to be present at build time.
* This script extends that capability to allow serving files from a different directory after the build process.
*/
import express from "express";
import next from "next";
import session, { MemoryStore } from 'express-session';
import { createClient } from "redis"
import { RedisStore } from 'connect-redis';
import Keycloak from 'keycloak-connect';
import axios from 'axios';
const colourReset = "\x1b[0m";
const colourRed = "\x1b[31m";
const colourGreen = "\x1b[32m";
const colourYellow = "\x1b[33m";
// Configure the server port; default to 3000 if not specified in environment variables
if (process.env.PORT) { console.info('port specified in environment variable: ', colourGreen, process.env.PORT, colourReset); }
const port = process.env.PORT || 3000;
const keycloakEnabled = process.env.KEYCLOAK === 'true';
const redisHost = process.env.REDIS_HOST || 'localhost';
const redisPort = process.env.REDIS_PORT || 6379;
if (process.env.ASSET_PREFIX) { console.info('Resource and Asset Prefix: ', colourGreen, process.env.ASSET_PREFIX, colourReset); }
console.info('keycloak authorisation required: ', keycloakEnabled ? colourYellow : colourGreen, process.env.KEYCLOAK, colourReset)
// Determine the deployment mode based on NODE_ENV; default to 'development' mode if not specified
const dev = process.env.NODE_ENV !== "production";
// Initialise the Next.js application
const app = next({ dev });
const handle = app.getRequestHandler();
let store;
// Prepare the Next.js application and then start the Express server
app.prepare().then(() => {
const server = express();
if (keycloakEnabled) { // do keycloak auth stuff if env var is set
console.info('the following pages require keycloak authentication', process.env.PROTECTED_PAGES ? colourYellow : colourRed, process.env.PROTECTED_PAGES, colourReset)
console.info('the following pages require the', process.env.ROLE ? colourYellow : colourRed, process.env.ROLE, colourReset, 'role: ', process.env.ROLE_PROTECTED_PAGES ? colourYellow : colourRed, process.env.ROLE_PROTECTED_PAGES, colourReset)
server.set('trust proxy', true); // the client’s IP address is understood as the left-most entry in the X-Forwarded-For header.
if (!dev) {
let redisClient;
console.info(`development mode is:`, colourGreen, dev, colourReset, `-> connecting to redis session store at`, colourGreen, `${redisHost}:${redisPort}`, colourReset);
try {
redisClient = createClient({
socket: {
host: redisHost,
port: redisPort
}
});
} catch (error) {
console.info('Error while creating Redis Client, please ensure that Redis is running and the host is specified as an environment variable if this viz app is in a Docker container');
console.error(error);
}
redisClient.connect().catch('Error while creating Redis Client, please ensure that Redis is running and the host is specified as an environment variable if this viz app is in a Docker container', console.error);
store = new RedisStore({
client: redisClient,
prefix: "redis",
ttl: undefined,
});
} else {
store = new MemoryStore(); // use in-memory store for session data in dev mode
console.info(`development mode is:`, dev ? colourYellow : colourRed, dev, colourReset, `-> using in-memory session store (express-session MemoryStore())`);
}
server.use(
session({
secret: 'login',
resave: false,
saveUninitialized: true,
store: store,
})
);
const keycloak = new Keycloak({ store: store });
server.use(keycloak.middleware());
server.get('/api/userinfo', keycloak.protect(), (req, res) => {
const { preferred_username: userName, given_name: firstName, family_name: lastName, name: fullName, realm_access: { roles }, resource_access: clientRoles } = req.kauth.grant.access_token.content;
res.json({ userName, firstName, lastName, fullName, roles, clientRoles });
});
const protectedPages = process.env.PROTECTED_PAGES.split(',');
protectedPages.forEach(page => {
server.get(page, keycloak.protect());
});
const roleProtectedPages = process.env.ROLE_PROTECTED_PAGES.split(',');
roleProtectedPages.forEach(page => {
server.get(page, keycloak.protect(process.env.ROLE));
console.info('protecting page', page, 'with role', process.env.ROLE);
});
const useGeoServerProxy = process.env.REACT_APP_USE_GEOSERVER_PROXY === 'true';
console.info('REACT_APP_USE_GEOSERVER_PROXY is ' + useGeoServerProxy);
if (useGeoServerProxy) {
console.info('Server URL REACT_APP_SERVER_URL is ' + process.env.REACT_APP_SERVER_URL);
console.info('GeoServer requests from MapBox will be sent to ' + process.env.REACT_APP_SERVER_URL + '/geoserver-proxy')
server.get('/geoserver-proxy', keycloak.protect(), async (req, res) => {
const targetUrl = req.query.url;
let headers = { ...req.headers };
if (req.kauth?.grant) {
headers['Authorization'] = 'Bearer ' + req.kauth.grant.access_token.token;
}
try {
// Forward the request to the target URL with the modified headers
const response = await axios({
url: targetUrl,
method: req.method,
headers: headers,
responseType: 'stream', // To stream the response back
});
// Pipe the response back to the client
response.data.pipe(res);
} catch (err) {
// most of these errors can probably be ignored
console.error(err);
}
});
}
}
// Handle all other requests using Next.js
server.all("*", (req, res) => {
return handle(req, res);
});
// Start listening on the specified port and log server status
server.listen(port, (err) => {
if (err) throw err;
console.info('Running at', colourGreen, `http://localhost:${port}${colourReset}`, `(on host / inside container). Development mode :${dev ? colourYellow : colourGreen}`, dev, colourReset);
});
});