hof 22.7.0-service-paused-beta.13 → 22.7.0-service-unavailable-beta.2
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 +7 -5
- package/README.md +5 -5
- package/config/hof-defaults.js +4 -2
- package/frontend/template-partials/translations/src/en/errors.json +1 -1
- package/frontend/template-partials/views/service-unavailable.html +13 -0
- package/index.js +8 -6
- package/middleware/errors.js +35 -31
- package/middleware/index.js +1 -1
- package/middleware/service-unavailable.js +64 -0
- package/package.json +2 -2
- package/sandbox/.env +1 -0
- package/sandbox/apps/sandbox/translations/en/default.json +4 -6
- package/sandbox/apps/sandbox/translations/src/en/errors.json +1 -1
- package/sandbox/package.json +1 -1
- package/utilities/helpers/index.js +40 -0
- package/frontend/template-partials/translations/src/en/pages.json +0 -8
- package/middleware/service-paused.js +0 -52
- /package/frontend/template-partials/views/{service-paused.html → 503.html} +0 -0
package/CHANGELOG.md
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
-
## 2025-05-
|
1
|
+
## 2025-05-20, Version 22.7.0 (Stable), @Rhodine-orleans-lindsay @sulthan-ahmed
|
2
2
|
### Added
|
3
|
-
- Service
|
4
|
-
- Adds 'service
|
5
|
-
- Includes default service
|
6
|
-
- Includes flag to set
|
3
|
+
- 'Service Unavailable' functionality added which allows for services to redirect to a 'Service Unavailable' page when there is a need to pause a service for things like maintenance:
|
4
|
+
- Adds 'service unavailable' error middleware
|
5
|
+
- Includes default service unavailable html view
|
6
|
+
- Includes flag to set `SERVICE_UNAVAILABLE` config to true to enable the functionality
|
7
|
+
### Changed
|
8
|
+
- Error pages can now show the service name in the title and journey header
|
7
9
|
### Security
|
8
10
|
- Updates patch and minor dependencies
|
9
11
|
|
package/README.md
CHANGED
@@ -805,7 +805,7 @@ const pdfData = await pdfModel.save();
|
|
805
805
|
|
806
806
|
# HOF Middleware
|
807
807
|
|
808
|
-
A collection of commonly used HOF middleware, exports `cookies`, `notFound`, `
|
808
|
+
A collection of commonly used HOF middleware, exports `cookies`, `notFound`, `serviceUnavailable` and `errors` on `middleware`
|
809
809
|
|
810
810
|
## Arranging the middleware in your app
|
811
811
|
|
@@ -844,18 +844,18 @@ You can also provide an array of healthcheck URLs with `healthcheckUrls`,
|
|
844
844
|
should you not want to throw a Cookies required error when requesting the app with specific URLs.
|
845
845
|
Kubernetes healthcheck URLs are provided as defaults if no overrides are supplied.
|
846
846
|
|
847
|
-
## Service Unavailable
|
848
|
-
Allows a service to be paused when required and then resumed. It ensures that anyone using the service
|
847
|
+
## Service Unavailable
|
848
|
+
Allows a service to be paused when required and then resumed. It ensures that anyone using the service that lands on any part of the form is diverted to a "Service Unavailable" page which will communicate to the user that the service is not available at this time.
|
849
849
|
|
850
850
|
### Usage
|
851
|
-
- Set the `
|
851
|
+
- Set the `SERVICE_UNAVAILABLE` env to `true` in your service.
|
852
852
|
|
853
853
|
### Page Content Customisation
|
854
854
|
There is default text for this page. Default text can be overridden by setting the `message` and `answers-saved` properties in the `errors.json` file of the service. Note that information relating to who to contact and alternatives to using the form is optional and so there is no default text for these unless the properties `contact` and `alternative` are set in errors.json:
|
855
855
|
|
856
856
|
```json
|
857
857
|
{
|
858
|
-
"service-
|
858
|
+
"service-unavailable" : {
|
859
859
|
"message": "This service will be unavailble for a week.",
|
860
860
|
"answers-saved": "Your answers have not been saved.",
|
861
861
|
"contact": "You can contact test@test.com for more information",
|
package/config/hof-defaults.js
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
'use strict';
|
2
2
|
/* eslint no-process-env: "off" */
|
3
3
|
const rateLimits = require('./rate-limits');
|
4
|
+
const Helper = require('../utilities/helpers');
|
5
|
+
const parseBoolean = Helper.getEnvBoolean;
|
4
6
|
|
5
7
|
const defaults = {
|
6
8
|
appName: process.env.APP_NAME || 'HOF Application',
|
@@ -9,7 +11,7 @@ const defaults = {
|
|
9
11
|
translations: 'translations',
|
10
12
|
start: true,
|
11
13
|
csp: {
|
12
|
-
disabled: process.env.DISABLE_CSP
|
14
|
+
disabled: parseBoolean(process.env.DISABLE_CSP, false, 'DISABLE_CSP')
|
13
15
|
},
|
14
16
|
getCookies: true,
|
15
17
|
getTerms: true,
|
@@ -52,7 +54,7 @@ const defaults = {
|
|
52
54
|
},
|
53
55
|
serveStatic: process.env.SERVE_STATIC_FILES !== 'false',
|
54
56
|
sessionTimeOutWarning: process.env.SESSION_TIMEOUT_WARNING || 300,
|
55
|
-
|
57
|
+
serviceUnavailable: parseBoolean(process.env.SERVICE_UNAVAILABLE, false, 'SERVICE_UNAVAILABLE')
|
56
58
|
};
|
57
59
|
|
58
60
|
module.exports = Object.assign({}, defaults, rateLimits);
|
@@ -27,7 +27,7 @@
|
|
27
27
|
"pre-time-to-wait": "Please try again in ",
|
28
28
|
"post-time-to-wait": " minutes."
|
29
29
|
},
|
30
|
-
"service-
|
30
|
+
"service-unavailable": {
|
31
31
|
"title": "Sorry, the service is unavailable",
|
32
32
|
"message": "This service is temporarily unavailable.",
|
33
33
|
"answers-saved": "Your answers have not been saved."
|
@@ -0,0 +1,13 @@
|
|
1
|
+
{{<error}}
|
2
|
+
{{$content}}
|
3
|
+
<h1 class="govuk-heading-l">{{title}}</h1>
|
4
|
+
<p class="govuk-body">{{message}}</p>
|
5
|
+
<p class="govuk-body">{{answers-saved}}</p>
|
6
|
+
{{#contact}}
|
7
|
+
<p class="govuk-body">{{contact}}</p>
|
8
|
+
{{/contact}}
|
9
|
+
{{#alternative}}
|
10
|
+
<p class="govuk-body">{{alternative}}</p>
|
11
|
+
{{/alternative}}
|
12
|
+
{{/content}}
|
13
|
+
{{/error}}
|
package/index.js
CHANGED
@@ -51,8 +51,8 @@ const loadRoutes = (app, config) => {
|
|
51
51
|
};
|
52
52
|
|
53
53
|
const applyErrorMiddlewares = (app, config) => {
|
54
|
-
if (config.
|
55
|
-
app.use(hofMiddleware.
|
54
|
+
if (config.serviceUnavailable === true) {
|
55
|
+
app.use(hofMiddleware.serviceUnavailable({
|
56
56
|
logger: config.logger
|
57
57
|
}));
|
58
58
|
}
|
@@ -161,7 +161,7 @@ function bootstrap(options) {
|
|
161
161
|
res.locals.sessionTimeoutWarningContent = config.sessionTimeoutWarningContent;
|
162
162
|
res.locals.exitFormContent = config.exitFormContent;
|
163
163
|
res.locals.saveExitFormContent = config.saveExitFormContent;
|
164
|
-
res.locals.
|
164
|
+
res.locals.serviceUnavailable = config.serviceUnavailable;
|
165
165
|
next();
|
166
166
|
});
|
167
167
|
|
@@ -238,10 +238,12 @@ function bootstrap(options) {
|
|
238
238
|
// Set up routing so <YOUR-SITE-URL>/assets are served from /node_modules/govuk-frontend/govuk/assets
|
239
239
|
app.use('/assets', express.static(path.join(__dirname, '/node_modules/govuk-frontend/govuk/assets')));
|
240
240
|
// Check if service has been paused and redirect accordingly
|
241
|
-
|
241
|
+
const bypassPaths = ['/assets', '/healthcheck', '/service-unavailable'];
|
242
|
+
|
243
|
+
if (config.serviceUnavailable === true) {
|
242
244
|
app.use((req, res, next) => {
|
243
|
-
if (req.path
|
244
|
-
return res.redirect('/service-
|
245
|
+
if (!bypassPaths.some(bypassPath => req.path.startsWith(bypassPath))) {
|
246
|
+
return res.redirect('/service-unavailable');
|
245
247
|
}
|
246
248
|
return next();
|
247
249
|
});
|
package/middleware/errors.js
CHANGED
@@ -5,71 +5,75 @@ const rateLimitsConfig = require('../config/rate-limits');
|
|
5
5
|
|
6
6
|
const errorTitle = code => `${code}_ERROR`;
|
7
7
|
const errorMsg = code => `There is a ${code}_ERROR`;
|
8
|
+
|
8
9
|
// eslint-disable-next-line complexity
|
9
10
|
const getContent = (err, translate) => {
|
10
11
|
const content = {};
|
11
12
|
|
13
|
+
// Helper to safely call translate if it's a function
|
14
|
+
const t = key => (typeof translate === 'function' ? translate(key) : undefined);
|
15
|
+
|
12
16
|
if (err.code === 'SESSION_TIMEOUT') {
|
13
17
|
err.status = 401;
|
14
18
|
err.template = 'session-timeout';
|
15
|
-
err.serviceName = (
|
16
|
-
err.title = (
|
17
|
-
err.message = (
|
18
|
-
content.serviceName = (
|
19
|
-
content.title = (
|
20
|
-
content.message = (
|
19
|
+
err.serviceName = t('journey.serviceName') || t('journey.header');
|
20
|
+
err.title = t('errors.session.title');
|
21
|
+
err.message = t('errors.session.message');
|
22
|
+
content.serviceName = t('journey.serviceName') || t('journey.header');
|
23
|
+
content.title = t('errors.session.title');
|
24
|
+
content.message = t('errors.session.message');
|
21
25
|
}
|
22
26
|
|
23
27
|
if (err.code === 'NO_COOKIES') {
|
24
28
|
err.status = 403;
|
25
29
|
err.template = 'cookie-error';
|
26
|
-
content.serviceName = (
|
27
|
-
content.title = (
|
28
|
-
content.message = (
|
30
|
+
content.serviceName = t('journey.serviceName') || t('journey.header');
|
31
|
+
content.title = t('errors.cookies-required.title');
|
32
|
+
content.message = t('errors.cookies-required.message');
|
29
33
|
}
|
30
34
|
|
31
35
|
if (err.code === 'DDOS_RATE_LIMIT') {
|
32
36
|
err.status = 429;
|
33
37
|
err.template = 'rate-limit-error';
|
34
|
-
err.serviceName = (
|
35
|
-
err.title = (
|
36
|
-
err.message = (
|
37
|
-
err.preTimeToWait = (
|
38
|
+
err.serviceName = t('journey.serviceName') || t('journey.header');
|
39
|
+
err.title = t('errors.ddos-rate-limit.title');
|
40
|
+
err.message = t('errors.ddos-rate-limit.message');
|
41
|
+
err.preTimeToWait = t('errors.ddos-rate-limit.pre-time-to-wait');
|
38
42
|
err.timeToWait = rateLimitsConfig.rateLimits.requests.windowSizeInMinutes;
|
39
|
-
err.postTimeToWait = (
|
40
|
-
content.title = (
|
41
|
-
content.serviceName = (
|
42
|
-
content.message = (
|
43
|
-
content.preTimeToWait = (
|
43
|
+
err.postTimeToWait = t('errors.ddos-rate-limit.post-time-to-wait');
|
44
|
+
content.title = t('errors.ddos-rate-limit.title');
|
45
|
+
content.serviceName = t('journey.serviceName') || t('journey.header');
|
46
|
+
content.message = t('errors.ddos-rate-limit.message');
|
47
|
+
content.preTimeToWait = t('errors.ddos-rate-limit.pre-time-to-wait');
|
44
48
|
content.timeToWait = rateLimitsConfig.rateLimits.requests.windowSizeInMinutes;
|
45
|
-
content.postTimeToWait = (
|
49
|
+
content.postTimeToWait = t('errors.ddos-rate-limit.post-time-to-wait');
|
46
50
|
}
|
47
51
|
|
48
52
|
if (err.code === 'SUBMISSION_RATE_LIMIT') {
|
49
53
|
err.status = 429;
|
50
54
|
err.template = 'rate-limit-error';
|
51
|
-
err.serviceName = (
|
52
|
-
err.title = (
|
53
|
-
err.message = (
|
54
|
-
err.preTimeToWait = (
|
55
|
+
err.serviceName = t('journey.serviceName') || t('journey.header');
|
56
|
+
err.title = t('errors.submission-rate-limit.title');
|
57
|
+
err.message = t('errors.submission-rate-limit.message');
|
58
|
+
err.preTimeToWait = t('errors.submission-rate-limit.pre-time-to-wait');
|
55
59
|
err.timeToWait = rateLimitsConfig.rateLimits.submissions.windowSizeInMinutes;
|
56
|
-
err.postTimeToWait = (
|
57
|
-
content.serviceName = (
|
58
|
-
content.title = (
|
59
|
-
content.message = (
|
60
|
-
content.preTimeToWait = (
|
60
|
+
err.postTimeToWait = t('errors.submission-rate-limit.post-time-to-wait');
|
61
|
+
content.serviceName = t('journey.serviceName') || t('journey.header');
|
62
|
+
content.title = t('errors.submission-rate-limit.title');
|
63
|
+
content.message = t('errors.submission-rate-limit.message');
|
64
|
+
content.preTimeToWait = t('errors.submission-rate-limit.pre-time-to-wait');
|
61
65
|
content.timeToWait = rateLimitsConfig.rateLimits.submissions.windowSizeInMinutes;
|
62
|
-
content.postTimeToWait = (
|
66
|
+
content.postTimeToWait = t('errors.submission-rate-limit.post-time-to-wait');
|
63
67
|
}
|
64
68
|
|
65
69
|
err.code = err.code || 'UNKNOWN';
|
66
70
|
err.status = err.status || 500;
|
67
71
|
|
68
72
|
if (!content.title) {
|
69
|
-
content.title = (
|
73
|
+
content.title = t('errors.default.title') || errorTitle(err.code);
|
70
74
|
}
|
71
75
|
if (!content.message) {
|
72
|
-
content.message = (
|
76
|
+
content.message = t('errors.default.message') || errorMsg(err.code);
|
73
77
|
}
|
74
78
|
return content;
|
75
79
|
};
|
package/middleware/index.js
CHANGED
@@ -0,0 +1,64 @@
|
|
1
|
+
/* eslint-disable consistent-return */
|
2
|
+
'use strict';
|
3
|
+
|
4
|
+
const getTranslations = translate => {
|
5
|
+
const translations = {
|
6
|
+
title: 'Sorry, this service is unavailable',
|
7
|
+
message: 'This service is temporarily unavailable',
|
8
|
+
'answers-saved': 'Your answers have not been saved'
|
9
|
+
};
|
10
|
+
|
11
|
+
if (translate) {
|
12
|
+
const contact = translate('errors.service-unavailable.contact');
|
13
|
+
const alternative = translate('errors.service-unavailable.alternative');
|
14
|
+
translations.serviceName = translate('journey.serviceName') || translate('journey.header');
|
15
|
+
translations.title = translate('errors.service-unavailable.title');
|
16
|
+
translations.message = translate('errors.service-unavailable.message');
|
17
|
+
translations['answers-saved'] = translate('errors.service-unavailable.answers-saved');
|
18
|
+
|
19
|
+
// Only render contact and alternative information if the key has a value set
|
20
|
+
if (contact === 'errors.service-unavailable.contact') {
|
21
|
+
translations.contact = '';
|
22
|
+
} else {
|
23
|
+
translations.contact = translate('errors.service-unavailable.contact');
|
24
|
+
}
|
25
|
+
if (alternative === 'errors.service-unavailable.alternative') {
|
26
|
+
translations.alternative = '';
|
27
|
+
} else {
|
28
|
+
translations.alternative = translate('errors.service-unavailable.alternative');
|
29
|
+
}
|
30
|
+
}
|
31
|
+
return translations;
|
32
|
+
};
|
33
|
+
|
34
|
+
module.exports = options => {
|
35
|
+
const opts = options || {};
|
36
|
+
const logger = opts.logger;
|
37
|
+
// These are paths that are allowed to bypass the "service unavailable" middleware.
|
38
|
+
// When the service is unavailable (for example, for maintenance), all routes except those listed here
|
39
|
+
// will return a paused response, typically a maintenance page.
|
40
|
+
//
|
41
|
+
// - '/assets': Static assets (CSS, JS, images) must still be served so the paused page displays correctly.
|
42
|
+
// - '/readyz' and '/livez': Health check endpoints must remain available for Kubernetes or other orchestration
|
43
|
+
// systems to determine if the container is healthy, even during maintenance.
|
44
|
+
const bypassPaths = opts.bypassPaths || ['/readyz', '/health', '/assets'];
|
45
|
+
|
46
|
+
return (req, res, next) => {
|
47
|
+
if (bypassPaths.some(path => req.path.startsWith(path))) {
|
48
|
+
return next();
|
49
|
+
}
|
50
|
+
const translate = opts.translate || req.translate;
|
51
|
+
const translations = getTranslations(translate);
|
52
|
+
if (logger && logger.warn) {
|
53
|
+
logger.warn('Service temporarily unavailable - service paused.');
|
54
|
+
}
|
55
|
+
res.status(503).render('service-unavailable', {
|
56
|
+
serviceName: translations.serviceName,
|
57
|
+
title: translations.title,
|
58
|
+
message: translations.message,
|
59
|
+
'answers-saved': translations['answers-saved'],
|
60
|
+
contact: translations.contact,
|
61
|
+
alternative: translations.alternative
|
62
|
+
});
|
63
|
+
};
|
64
|
+
};
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "hof",
|
3
3
|
"description": "A bootstrap for HOF projects",
|
4
|
-
"version": "22.7.0-service-
|
4
|
+
"version": "22.7.0-service-unavailable-beta.2",
|
5
5
|
"license": "MIT",
|
6
6
|
"main": "index.js",
|
7
7
|
"author": "HomeOffice",
|
@@ -27,7 +27,7 @@
|
|
27
27
|
"test:lint": "eslint . --config ./node_modules/eslint-config-hof/default.js",
|
28
28
|
"test:functional": "funkie mocha ./test/functional-tests --timeout 20000 --exit",
|
29
29
|
"test:client": "karma start test/frontend/toolkit/karma.conf.js",
|
30
|
-
"test:jest": "jest test/frontend/jest",
|
30
|
+
"test:jest": "jest test/frontend/jest test/utilities/helpers/jest",
|
31
31
|
"test:acceptance": "TAGS=\"${TAGS:=@feature}\" yarn run test:cucumber",
|
32
32
|
"test:acceptance_browser": "ACCEPTANCE_WITH_BROWSER=true TAGS=\"${TAGS:=@feature}\" yarn run test:cucumber",
|
33
33
|
"test:cucumber": "cucumber-js -f @cucumber/pretty-formatter \"sandbox/test/_features/**/*.feature\" --require sandbox/test/_features/test.setup.js --require \"sandbox/test/_features/step_definitions/**/*.js\" --tags $TAGS",
|
package/sandbox/.env
CHANGED
@@ -1,9 +1,4 @@
|
|
1
1
|
{
|
2
|
-
"errors": {
|
3
|
-
"service-paused": {
|
4
|
-
"contact": "You can email for more information"
|
5
|
-
}
|
6
|
-
},
|
7
2
|
"exit": {
|
8
3
|
"header": "You have left this form",
|
9
4
|
"title": "You have left this form"
|
@@ -111,7 +106,6 @@
|
|
111
106
|
},
|
112
107
|
"journey": {
|
113
108
|
"header": "HOF Bootstrap Sandbox Form",
|
114
|
-
"serviceName": "HOF Bootstrap Sandbox Form",
|
115
109
|
"confirmation": {
|
116
110
|
"details": "Your reference number <br><strong>HDJ2123F</strong>"
|
117
111
|
}
|
@@ -186,6 +180,10 @@
|
|
186
180
|
"paragraph-1": "Your form doesn't appear to have been worked on for 30 minutes so we closed it for security.",
|
187
181
|
"paragraph-2": "Any answers you saved have not been affected.",
|
188
182
|
"paragraph-3": "You can sign back in to your application at any time by returning to the <a href='/' class='govuk-link'>start page</a>."
|
183
|
+
},
|
184
|
+
"service-down": {
|
185
|
+
"message": "hello",
|
186
|
+
"descriptions": "hello"
|
189
187
|
}
|
190
188
|
},
|
191
189
|
"validation": {
|
package/sandbox/package.json
CHANGED
@@ -8,7 +8,7 @@
|
|
8
8
|
},
|
9
9
|
"scripts": {
|
10
10
|
"start": "node server.js",
|
11
|
-
"start:dev": "HOF_SANDBOX=true ../bin/hof-build watch
|
11
|
+
"start:dev": "HOF_SANDBOX=true ../bin/hof-build watch",
|
12
12
|
"dev": "yarn && GA_TAG=test nodemon server",
|
13
13
|
"build": "HOF_SANDBOX=true ../bin/hof-build",
|
14
14
|
"postinstall": "yarn run build"
|
@@ -2,6 +2,7 @@
|
|
2
2
|
'use strict';
|
3
3
|
|
4
4
|
const _ = require('lodash');
|
5
|
+
const logger = require('../../lib/logger');
|
5
6
|
|
6
7
|
module.exports = class Helpers {
|
7
8
|
/**
|
@@ -146,4 +147,43 @@ module.exports = class Helpers {
|
|
146
147
|
(!Object.keys(req.form.values).includes(condition.field) &&
|
147
148
|
_.get(req, `form.historicalValues[${condition.field}]`)));
|
148
149
|
}
|
150
|
+
|
151
|
+
/**
|
152
|
+
* Read an environment variable and coerce it to boolean.
|
153
|
+
*
|
154
|
+
* @param {*} rawValue The raw value (e.g. process.env.FEATURE_FLAG)
|
155
|
+
* @param {boolean} [fallback=false] Value to use if rawValue is null/undefined/invalid
|
156
|
+
* @param {string} [envVarName='ENV_VAR'] Name for log context
|
157
|
+
* @returns {boolean}
|
158
|
+
*/
|
159
|
+
static getEnvBoolean(rawValue, fallback = false, envVarName = 'ENV_VAR') {
|
160
|
+
if (typeof rawValue === 'boolean') {
|
161
|
+
return rawValue;
|
162
|
+
}
|
163
|
+
|
164
|
+
if (rawValue === null || rawValue === undefined) {
|
165
|
+
return fallback;
|
166
|
+
}
|
167
|
+
|
168
|
+
if (typeof rawValue === 'string') {
|
169
|
+
const normalized = rawValue.trim().toLowerCase();
|
170
|
+
if (normalized === 'true') {
|
171
|
+
return true;
|
172
|
+
}
|
173
|
+
if (normalized === 'false') {
|
174
|
+
return false;
|
175
|
+
}
|
176
|
+
logger.warn(
|
177
|
+
`Invalid environment variable ${envVarName} value "${rawValue}" ` +
|
178
|
+
'– must be "true" or "false"; defaulting to ' +
|
179
|
+
`${fallback}`
|
180
|
+
);
|
181
|
+
return fallback;
|
182
|
+
}
|
183
|
+
|
184
|
+
logger.warn(
|
185
|
+
`Invalid environment variable ${envVarName} type (${typeof rawValue}); defaulting to ${fallback}`
|
186
|
+
);
|
187
|
+
return fallback;
|
188
|
+
}
|
149
189
|
};
|
@@ -1,52 +0,0 @@
|
|
1
|
-
'use strict';
|
2
|
-
|
3
|
-
const getTranslations = translate => {
|
4
|
-
const translations = {
|
5
|
-
title: 'Sorry, this service is unavailable',
|
6
|
-
message: 'This service is temporarily unavailable',
|
7
|
-
'answers-saved': 'Your answers have not been saved'
|
8
|
-
};
|
9
|
-
|
10
|
-
if (translate) {
|
11
|
-
const contact = translate('errors.service-paused.contact');
|
12
|
-
const alternative = translate('errors.service-paused.alternative');
|
13
|
-
translations.serviceName = translate('journey.serviceName') || translate('journey.header');
|
14
|
-
translations.title = translate('errors.service-paused.title');
|
15
|
-
translations.message = translate('errors.service-paused.message');
|
16
|
-
translations['answers-saved'] = translate('errors.service-paused.answers-saved');
|
17
|
-
|
18
|
-
// Only render contact and alternative information if the key has a value set
|
19
|
-
if (contact === 'errors.service-paused.contact') {
|
20
|
-
translations.contact = '';
|
21
|
-
} else {
|
22
|
-
translations.contact = translate('errors.service-paused.contact');
|
23
|
-
}
|
24
|
-
if (alternative === 'errors.service-paused.alternative') {
|
25
|
-
translations.alternative = '';
|
26
|
-
} else {
|
27
|
-
translations.alternative = translate('errors.service-paused.alternative');
|
28
|
-
}
|
29
|
-
}
|
30
|
-
return translations;
|
31
|
-
};
|
32
|
-
|
33
|
-
module.exports = options => {
|
34
|
-
const opts = options || {};
|
35
|
-
const logger = opts.logger;
|
36
|
-
|
37
|
-
return (req, res) => {
|
38
|
-
const translate = opts.translate || req.translate;
|
39
|
-
const translations = getTranslations(translate);
|
40
|
-
if (logger && logger.warn) {
|
41
|
-
logger.warn('Service temporarily unavailable - service paused.');
|
42
|
-
}
|
43
|
-
res.status(503).render('service-paused', {
|
44
|
-
serviceName: translations.serviceName,
|
45
|
-
title: translations.title,
|
46
|
-
message: translations.message,
|
47
|
-
'answers-saved': translations['answers-saved'],
|
48
|
-
contact: translations.contact,
|
49
|
-
alternative: translations.alternative
|
50
|
-
});
|
51
|
-
};
|
52
|
-
};
|
File without changes
|