@apolitical/server 2.4.2 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/package.json +19 -19
- package/src/config.js +2 -1
- package/src/container.js +6 -0
- package/src/loaders/express.loader.js +36 -0
- package/src/loaders/probes.loader.js +13 -7
- package/src/middlewares/jwt/auth0.middleware.js +3 -3
- package/src/services/express.service.js +48 -0
- package/src/services/health.service.js +36 -0
- package/src/services/server.service.js +6 -62
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.5.0] - 2022-05-06
|
|
9
|
+
### Added
|
|
10
|
+
- Custom `readinessCheck` and `livenessCheck` to define liveness externally
|
|
11
|
+
- New probe `/health` endpoint to combine readiness and liveness
|
|
12
|
+
|
|
8
13
|
## [2.4.2] - 2022-03-03
|
|
9
14
|
### Bumped
|
|
10
15
|
- Version number for testing
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apolitical/server",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0",
|
|
4
4
|
"description": "Node.js module to encapsulate Apolitical's express server setup",
|
|
5
5
|
"author": "Apolitical Group Limited <engineering@apolitical.co>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -26,41 +26,41 @@
|
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@apolitical/logger": "2.0.1",
|
|
28
28
|
"@cloudnative/health-connect": "2.1.0",
|
|
29
|
-
"awilix": "
|
|
30
|
-
"body-parser": "1.
|
|
29
|
+
"awilix": "7.0.3",
|
|
30
|
+
"body-parser": "1.20.0",
|
|
31
31
|
"compression": "1.7.4",
|
|
32
32
|
"cookie-parser": "1.4.6",
|
|
33
33
|
"cors": "2.8.5",
|
|
34
|
-
"dotenv": "
|
|
35
|
-
"express": "4.
|
|
36
|
-
"express-jwt": "
|
|
34
|
+
"dotenv": "16.0.0",
|
|
35
|
+
"express": "4.18.1",
|
|
36
|
+
"express-jwt": "7.7.0",
|
|
37
37
|
"http-status-codes": "2.2.0",
|
|
38
|
-
"http-terminator": "3.0
|
|
39
|
-
"jsrsasign": "10.5.
|
|
40
|
-
"jwks-rsa": "2.
|
|
38
|
+
"http-terminator": "3.2.0",
|
|
39
|
+
"jsrsasign": "10.5.20",
|
|
40
|
+
"jwks-rsa": "2.1.1",
|
|
41
41
|
"jwt-decode": "3.1.2",
|
|
42
42
|
"morgan": "1.10.0",
|
|
43
43
|
"passport": "0.5.2",
|
|
44
44
|
"passport-jwt": "4.0.0",
|
|
45
|
-
"prerender-node": "3.
|
|
45
|
+
"prerender-node": "3.5.0",
|
|
46
46
|
"swagger-ui-express": "4.3.0"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@apolitical/eslint-config": "
|
|
49
|
+
"@apolitical/eslint-config": "2.0.0",
|
|
50
50
|
"@apolitical/testing": "1.0.2",
|
|
51
|
-
"audit-ci": "
|
|
52
|
-
"husky": "
|
|
53
|
-
"jest": "
|
|
54
|
-
"jest-junit": "13.
|
|
55
|
-
"lint-staged": "12.
|
|
56
|
-
"mock-jwks": "1.0.
|
|
57
|
-
"nock": "13.2.
|
|
51
|
+
"audit-ci": "6.2.0",
|
|
52
|
+
"husky": "8.0.1",
|
|
53
|
+
"jest": "28.1.0",
|
|
54
|
+
"jest-junit": "13.2.0",
|
|
55
|
+
"lint-staged": "12.4.1",
|
|
56
|
+
"mock-jwks": "1.0.3",
|
|
57
|
+
"nock": "13.2.4"
|
|
58
58
|
},
|
|
59
59
|
"engines": {
|
|
60
60
|
"node": ">=16.13.0"
|
|
61
61
|
},
|
|
62
62
|
"eslintConfig": {
|
|
63
|
-
"extends": "@apolitical/eslint-config/
|
|
63
|
+
"extends": "@apolitical/eslint-config/api.config"
|
|
64
64
|
},
|
|
65
65
|
"prettier": "@apolitical/eslint-config/prettier.config",
|
|
66
66
|
"jest": {
|
package/src/config.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const { NODE_ENV, LOG_LEVEL } = process.env;
|
|
4
4
|
|
|
5
5
|
const NAME = 'apolitical-server';
|
|
6
|
-
const VERSION = '2.
|
|
6
|
+
const VERSION = '2.5.0';
|
|
7
7
|
const ADMIN_ROLE = 'administrator';
|
|
8
8
|
|
|
9
9
|
module.exports = {
|
|
@@ -18,6 +18,7 @@ module.exports = {
|
|
|
18
18
|
},
|
|
19
19
|
ENDPOINTS: {
|
|
20
20
|
PROBES: {
|
|
21
|
+
HEALTH: '/health',
|
|
21
22
|
LIVENESS: '/liveness',
|
|
22
23
|
READINESS: '/readiness',
|
|
23
24
|
},
|
package/src/container.js
CHANGED
|
@@ -34,6 +34,7 @@ const jwtPassportHelper = require('./helpers/jwt/passport.helper');
|
|
|
34
34
|
const loggerHelper = require('./helpers/logger.helper');
|
|
35
35
|
// Loaders
|
|
36
36
|
const documentationLoader = require('./loaders/documentation.loader');
|
|
37
|
+
const expressLoader = require('./loaders/express.loader');
|
|
37
38
|
const loggerLoader = require('./loaders/logger.loader');
|
|
38
39
|
const middlewaresLoader = require('./loaders/middlewares.loader');
|
|
39
40
|
const probesLoader = require('./loaders/probes.loader');
|
|
@@ -45,6 +46,8 @@ const jwtApoliticalMiddleware = require('./middlewares/jwt/apolitical.middleware
|
|
|
45
46
|
const jwtAuth0Middleware = require('./middlewares/jwt/auth0.middleware');
|
|
46
47
|
const errorMiddleware = require('./middlewares/error.middleware');
|
|
47
48
|
// Services
|
|
49
|
+
const expressService = require('./services/express.service');
|
|
50
|
+
const healthService = require('./services/health.service');
|
|
48
51
|
const jwtService = require('./services/jwt.service');
|
|
49
52
|
const serverService = require('./services/server.service');
|
|
50
53
|
|
|
@@ -84,6 +87,7 @@ container.register({
|
|
|
84
87
|
loggerHelper: asFunction(loggerHelper).singleton(),
|
|
85
88
|
// Loaders
|
|
86
89
|
documentationLoader: asFunction(documentationLoader).singleton(),
|
|
90
|
+
expressLoader: asFunction(expressLoader).singleton(),
|
|
87
91
|
loggerLoader: asFunction(loggerLoader).singleton(),
|
|
88
92
|
middlewaresLoader: asFunction(middlewaresLoader).singleton(),
|
|
89
93
|
probesLoader: asFunction(probesLoader).singleton(),
|
|
@@ -95,6 +99,8 @@ container.register({
|
|
|
95
99
|
jwtAuth0Middleware: asFunction(jwtAuth0Middleware).singleton(),
|
|
96
100
|
errorMiddleware: asFunction(errorMiddleware).singleton(),
|
|
97
101
|
// Services
|
|
102
|
+
expressService: asFunction(expressService).singleton(),
|
|
103
|
+
healthService: asFunction(healthService).singleton(),
|
|
98
104
|
jwtService: asFunction(jwtService).singleton(),
|
|
99
105
|
serverService: asFunction(serverService).singleton(),
|
|
100
106
|
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
module.exports = ({
|
|
4
|
+
logger,
|
|
5
|
+
documentationLoader,
|
|
6
|
+
loggerLoader,
|
|
7
|
+
errorMiddleware,
|
|
8
|
+
middlewaresLoader,
|
|
9
|
+
probesLoader,
|
|
10
|
+
fallbackLoader,
|
|
11
|
+
}) => {
|
|
12
|
+
// Main (express) loader
|
|
13
|
+
return async function load(app, opts) {
|
|
14
|
+
const childLogger = logger.where(__filename, 'load');
|
|
15
|
+
childLogger.debug('Started');
|
|
16
|
+
// Load probes
|
|
17
|
+
probesLoader(app, opts);
|
|
18
|
+
// Load useful middlewares
|
|
19
|
+
middlewaresLoader(app, opts);
|
|
20
|
+
// Load logger
|
|
21
|
+
await loggerLoader(app, opts);
|
|
22
|
+
// Load custom resources
|
|
23
|
+
if (opts.appLoader) {
|
|
24
|
+
await opts.appLoader(app);
|
|
25
|
+
}
|
|
26
|
+
// Load documentation
|
|
27
|
+
documentationLoader(app, opts);
|
|
28
|
+
// Load fallback routing
|
|
29
|
+
fallbackLoader(app, opts);
|
|
30
|
+
// Use error handler
|
|
31
|
+
if (opts.handleErrors) {
|
|
32
|
+
app.use(errorMiddleware(opts));
|
|
33
|
+
}
|
|
34
|
+
childLogger.debug('Finished');
|
|
35
|
+
};
|
|
36
|
+
};
|
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
module.exports = ({
|
|
4
|
-
const { LIVENESS, READINESS } = config.ENDPOINTS.PROBES;
|
|
3
|
+
module.exports = ({ config, logger, healthService }) => {
|
|
4
|
+
const { HEALTH, LIVENESS, READINESS } = config.ENDPOINTS.PROBES;
|
|
5
5
|
|
|
6
|
-
return function load(app) {
|
|
6
|
+
return function load(app, { readinessCheck, livenessCheck }) {
|
|
7
7
|
const childLogger = logger.where(__filename, 'load');
|
|
8
8
|
childLogger.debug('Started');
|
|
9
|
-
//
|
|
10
|
-
|
|
9
|
+
// Overwrite checks when defined
|
|
10
|
+
if (readinessCheck) {
|
|
11
|
+
healthService.registerReadiness(readinessCheck);
|
|
12
|
+
}
|
|
13
|
+
if (livenessCheck) {
|
|
14
|
+
healthService.registerLiveness(livenessCheck);
|
|
15
|
+
}
|
|
11
16
|
// Load probes endpoints
|
|
12
|
-
app.get(
|
|
13
|
-
app.get(READINESS,
|
|
17
|
+
app.get(HEALTH, healthService.healthEndpoint());
|
|
18
|
+
app.get(READINESS, healthService.readinessEndpoint());
|
|
19
|
+
app.get(LIVENESS, healthService.livenessEndpoint());
|
|
14
20
|
childLogger.debug('Finished');
|
|
15
21
|
};
|
|
16
22
|
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
module.exports = ({ expressJwt, jwksRsa, config }) => {
|
|
3
|
+
module.exports = ({ expressJwt: { expressjwt: jwt }, jwksRsa, config }) => {
|
|
4
4
|
const { CACHE, RATE_LIMIT, RPM, URI, ALGORITHMS } = config.JWT.AUTH0;
|
|
5
|
-
//
|
|
5
|
+
// Define express JWT authentication (Auth0)
|
|
6
6
|
return function handler({ domain, audience, issuer }) {
|
|
7
|
-
return
|
|
7
|
+
return jwt({
|
|
8
8
|
secret: jwksRsa.expressJwtSecret({
|
|
9
9
|
cache: CACHE,
|
|
10
10
|
rateLimit: RATE_LIMIT,
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
module.exports = ({ express, httpTerminator, logger, expressLoader }) => {
|
|
4
|
+
let app = null;
|
|
5
|
+
let terminator = null; // I need your clothes, your boots and your motorcycle
|
|
6
|
+
|
|
7
|
+
function expressRunner(app, port) {
|
|
8
|
+
const childLogger = logger.where(__filename, 'expressRunner');
|
|
9
|
+
childLogger.debug('Started');
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
if (!port) {
|
|
12
|
+
childLogger.warn('Missing port option');
|
|
13
|
+
return reject(new Error('Cannot start server without port option'));
|
|
14
|
+
}
|
|
15
|
+
const server = app.listen(port, () => {
|
|
16
|
+
childLogger.info(`Server listening on ${port}`);
|
|
17
|
+
return resolve();
|
|
18
|
+
});
|
|
19
|
+
terminator = httpTerminator.createHttpTerminator({ server });
|
|
20
|
+
childLogger.debug('Finished');
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function startup(opts) {
|
|
25
|
+
const childLogger = logger.where(__filename, 'startup');
|
|
26
|
+
childLogger.debug('Started');
|
|
27
|
+
// Create, load and run the express app
|
|
28
|
+
if (!app) {
|
|
29
|
+
app = express();
|
|
30
|
+
await expressLoader(app, opts);
|
|
31
|
+
await expressRunner(app, opts.port);
|
|
32
|
+
}
|
|
33
|
+
childLogger.debug('Finished');
|
|
34
|
+
return app;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function shutdown() {
|
|
38
|
+
const childLogger = logger.where(__filename, 'shutdown');
|
|
39
|
+
childLogger.debug('Started');
|
|
40
|
+
// Terminate process and clean-up variables
|
|
41
|
+
await terminator.terminate();
|
|
42
|
+
app = null;
|
|
43
|
+
terminator = null;
|
|
44
|
+
childLogger.debug('Finished');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return { startup, shutdown };
|
|
48
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
module.exports = ({ health }) => {
|
|
4
|
+
// Setup health checker
|
|
5
|
+
const healthCheck = new health.HealthChecker();
|
|
6
|
+
|
|
7
|
+
// function registerStartup(startup) {
|
|
8
|
+
// healthCheck.registerStartupCheck(new health.StartupCheck("Startup Check", startup));
|
|
9
|
+
// }
|
|
10
|
+
|
|
11
|
+
function healthEndpoint() {
|
|
12
|
+
return health.HealthEndpoint(healthCheck);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function registerReadiness(readiness) {
|
|
16
|
+
healthCheck.registerReadinessCheck(new health.ReadinessCheck('Readiness Check', readiness));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function readinessEndpoint() {
|
|
20
|
+
return health.ReadinessEndpoint(healthCheck);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function registerLiveness(liveness) {
|
|
24
|
+
healthCheck.registerLivenessCheck(new health.LivenessCheck('Liveness Check', liveness));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function livenessEndpoint() {
|
|
28
|
+
return health.LivenessEndpoint(healthCheck);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function registerShutdown(shutdown) {
|
|
32
|
+
healthCheck.registerShutdownCheck(new health.ShutdownCheck('Shutdown Check', shutdown));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return { healthEndpoint, readinessEndpoint, registerReadiness, registerLiveness, livenessEndpoint, registerShutdown };
|
|
36
|
+
};
|
|
@@ -1,75 +1,19 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
module.exports = ({
|
|
4
|
-
express,
|
|
5
|
-
httpTerminator,
|
|
6
|
-
logger,
|
|
7
|
-
documentationLoader,
|
|
8
|
-
loggerLoader,
|
|
9
4
|
authenticationMiddleware,
|
|
10
5
|
authorisationMiddleware,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
probesLoader,
|
|
14
|
-
fallbackLoader,
|
|
6
|
+
expressService: { startup, shutdown },
|
|
7
|
+
healthService: { registerShutdown },
|
|
15
8
|
jwtService,
|
|
16
9
|
serverError,
|
|
17
10
|
}) => {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
function run(app, port) {
|
|
21
|
-
const childLogger = logger.where(__filename, 'run');
|
|
22
|
-
childLogger.debug('Started');
|
|
23
|
-
return new Promise((resolve, reject) => {
|
|
24
|
-
if (!port) {
|
|
25
|
-
childLogger.warn('Missing port option');
|
|
26
|
-
return reject(new Error('Cannot start server without port option'));
|
|
27
|
-
}
|
|
28
|
-
const server = app.listen(port, () => {
|
|
29
|
-
childLogger.info(`Server listening on ${port}`);
|
|
30
|
-
return resolve();
|
|
31
|
-
});
|
|
32
|
-
terminator = httpTerminator.createHttpTerminator({ server });
|
|
33
|
-
childLogger.debug('Finished');
|
|
34
|
-
});
|
|
35
|
-
}
|
|
11
|
+
// Register shutdown to clean up any resources used by the express app
|
|
12
|
+
registerShutdown(shutdown);
|
|
36
13
|
|
|
37
14
|
return {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
childLogger.debug('Started');
|
|
41
|
-
// Create Express server
|
|
42
|
-
const app = express();
|
|
43
|
-
// Load probes
|
|
44
|
-
probesLoader(app);
|
|
45
|
-
// Load useful middlewares
|
|
46
|
-
middlewaresLoader(app, opts);
|
|
47
|
-
// Load logger
|
|
48
|
-
await loggerLoader(app, opts);
|
|
49
|
-
// Load custom resources
|
|
50
|
-
if (opts.appLoader) {
|
|
51
|
-
await opts.appLoader(app);
|
|
52
|
-
}
|
|
53
|
-
// Load documentation
|
|
54
|
-
documentationLoader(app, opts);
|
|
55
|
-
// Load fallback routing
|
|
56
|
-
fallbackLoader(app, opts);
|
|
57
|
-
// Use error handler
|
|
58
|
-
if (opts.handleErrors) {
|
|
59
|
-
app.use(errorMiddleware(opts));
|
|
60
|
-
}
|
|
61
|
-
// Run the server
|
|
62
|
-
await run(app, opts.port);
|
|
63
|
-
childLogger.debug('Finished');
|
|
64
|
-
return app;
|
|
65
|
-
},
|
|
66
|
-
async stop() {
|
|
67
|
-
const childLogger = logger.where(__filename, 'stop');
|
|
68
|
-
childLogger.debug('Started');
|
|
69
|
-
// Terminate server process
|
|
70
|
-
await terminator.terminate();
|
|
71
|
-
childLogger.debug('Finished');
|
|
72
|
-
},
|
|
15
|
+
start: startup,
|
|
16
|
+
stop: shutdown,
|
|
73
17
|
jwt: jwtService,
|
|
74
18
|
errors: serverError,
|
|
75
19
|
middlewares: {
|