Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion digital-form-builder
Submodule digital-form-builder updated 59 files
+4 −0 .github/workflows/branch--lint-unit-and-smoke-test.yml
+3 −1 .github/workflows/build.yml
+3 −1 .github/workflows/dependency-review.yml
+2 −2 .github/workflows/deploy-development.yml
+34 −21 .github/workflows/smoke-test.yml
+2 −7 README.md
+4 −3 designer/Dockerfile
+1 −3 designer/client/conditions/SelectConditions.tsx
+1 −1 designer/package.json
+3 −2 docker-compose.smoke.yml
+10 −23 docs/runner/submission-queue.md
+0 −180 docs/runner/summary-details-transforms.md
+1 −1 model/package.json
+0 −1 model/src/components/types.ts
+2 −1 package.json
+2 −2 queue-model/package.json
+7 −7 runner/Dockerfile
+0 −1 runner/config/custom-environment-variables.json
+1 −1 runner/config/production.json
+3 −3 runner/package.json
+2 −1 runner/src/client/sass/_govuk.scss
+1 −1 runner/src/client/sass/_upload-dialog.scss
+1 −5 runner/src/client/sass/application.scss
+1 −1 runner/src/client/sass/modal-dialog.scss
+1 −3 runner/src/server/forms/test.json
+1 −9 runner/src/server/plugins/engine/components/CheckboxesField.ts
+6 −20 runner/src/server/plugins/engine/components/FileUploadField.ts
+11 −3 runner/src/server/plugins/engine/components/NumberField.ts
+1 −6 runner/src/server/plugins/engine/components/SelectionControlField.ts
+0 −3 runner/src/server/plugins/engine/models/SummaryViewModel.detailsTransformationMap.ts
+1 −20 runner/src/server/plugins/engine/models/SummaryViewModel.ts
+0 −85 runner/src/server/plugins/engine/models/__tests__/summaryViewModel.detailsTransformationMap.jest.ts
+0 −3 runner/src/server/plugins/engine/models/__tests__/summaryViewModel.jest.ts
+5 −23 runner/src/server/plugins/engine/pageControllers/PageControllerBase.ts
+0 −54 runner/src/server/plugins/engine/pageControllers/RepeatingSectionSummaryPageController.ts
+0 −2 runner/src/server/plugins/engine/pageControllers/helpers.ts
+0 −1 runner/src/server/plugins/engine/pageControllers/index.ts
+6 −12 runner/src/server/plugins/engine/pluginHandlers/files/prehandlers/validateContentTypes.ts
+3 −3 runner/src/server/plugins/views.ts
+3 −10 runner/src/server/routes/public.ts
+12 −21 runner/src/server/services/pgBossQueueService.ts
+9 −41 runner/src/server/services/upload/uploadService.ts
+0 −11 runner/src/server/transforms/summaryDetails/index.ts
+0 −18 runner/src/server/transforms/summaryDetails/types.ts
+0 −1 runner/src/server/utils/configSchema.ts
+2 −1 runner/src/server/views/help/accessibility-statement.html
+0 −4 runner/src/server/views/help/cookies.html
+1 −1 runner/src/server/views/help/privacy.html
+0 −4 runner/src/server/views/help/terms-and-conditions.html
+14 −33 runner/src/server/views/layout.html
+0 −40 runner/src/server/views/partials/summary-card.html
+1 −1 runner/src/server/views/partials/summary-row.html
+0 −27 runner/src/server/views/repeating-section-summary.html
+3 −16 runner/src/server/views/summary.html
+2 −2 runner/test/cases/server/phase-banner.test.js
+9 −16 runner/test/cases/server/upload.test.js
+5 −3 submitter/Dockerfile
+1 −1 submitter/package.json
+138 −96 yarn.lock
4 changes: 3 additions & 1 deletion runner/config/custom-environment-variables.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,7 @@
"sentryDsn": "SENTRY_DSN",
"sentryTracesSampleRate": "SENTRY_TRACES_SAMPLE_RATE",
"copilotEnv": "COPILOT_ENV",
"enableVirusScan": "ENABLE_VIRUS_SCAN"
"enableVirusScan": "ENABLE_VIRUS_SCAN",
"preAwardApiUrl": "PRE_AWARD_API_URL",
"apiHost": "API_HOST"
}
2 changes: 2 additions & 0 deletions runner/config/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,6 @@ module.exports = {
copilotEnv: "",

enableVirusScan: false,
preAwardApiUrl: "https://api.communities.gov.localhost:4004/forms",
apiHost: "api.communities.gov.localhost:4004",
};
141 changes: 67 additions & 74 deletions runner/src/server/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
// @ts-ignore
import fs from "fs";
import "../instrument";
// @ts-ignore
import hapi, {ServerOptions} from "@hapi/hapi";

import Scooter from "@hapi/scooter";
import inert from "@hapi/inert";
import Schmervice from "schmervice";
import blipp from "blipp";

import {ConfigureFormsPlugin} from "./plugins/ConfigureFormsPlugin";
import {configureRateLimitPlugin} from "../../../digital-form-builder/runner/src/server/plugins/rateLimit";
import {configureBlankiePlugin} from "../../../digital-form-builder/runner/src/server/plugins/blankie";
import {configureCrumbPlugin} from "../../../digital-form-builder/runner/src/server/plugins/crumb";
Expand All @@ -27,6 +23,7 @@ import {HapiRequest, HapiResponseToolkit, RouteConfig} from "./types";
import getRequestInfo from "../../../digital-form-builder/runner/src/server/utils/getRequestInfo";
import {ViewLoaderPlugin} from "./plugins/ViewLoaderPlugin";
import publicRouterPlugin from "./plugins/engine/PublicRouterPlugin";
import { FormRoutesPlugin } from "./plugins/engine/FormRoutesPlugin";
import {config} from "./plugins/utils/AdapterConfigurationSchema";
import errorHandlerPlugin from "./plugins/ErrorHandlerPlugin";
import {AdapterCacheService} from "./services";
Expand All @@ -39,6 +36,7 @@ import {catboxProvider} from "./services/AdapterCacheService";
import LanguagePlugin from "./plugins/LanguagePlugin";
import {TranslationLoaderService} from "./services/TranslationLoaderService";
import {WebhookService} from "./services/WebhookService";
import {PreAwardApiService} from "./services/PreAwardApiService";
import {pluginLog} from "./plugins/logging";

const Sentry = require('@sentry/node');
Expand Down Expand Up @@ -69,7 +67,7 @@ const serverOptions = async (): Promise<ServerOptions> => {
xframe: true,
},
},
cache: [{provider: catboxProvider()}],
cache: [{provider: catboxProvider()}], // Will throw if Redis not configured
};

const httpsOptions = hasCertificate
Expand All @@ -89,120 +87,114 @@ const serverOptions = async (): Promise<ServerOptions> => {

function determineLocal(request: any) {
if (request.i18n) {
if (request.state && request.state.language) {
const language = request.state.language;
// Set the language based on the request state
if (language) {
request.i18n.setLocale(language); // Ensure request.i18n is set properly
} else {
request.i18n.setLocale("en");
}
} else if (request.query && request.query.lang) {
const language = request.query.lang;
// Set the language based on the request state
if (language) {
request.i18n.setLocale(language); // Ensure request.i18n is set properly
} else {
request.i18n.setLocale("en");
}
} else {
request.i18n.setLocale("en");
const language = request.state?.language || request.query?.lang || "en";
request.i18n.setLocale(language);
if (request.query?.lang && request.query.lang !== request.yar.get("lang")) {
request.yar.set("lang", request.query.lang);
}
}
}

async function createServer(routeConfig: RouteConfig) {
console.log("*** SERVER CREATING WITH PLUGINS ***")
console.log("*** FORM RUNNER SERVER STARTING ***")
const server = hapi.server(await serverOptions());
// @ts-ignore
const {formFileName, formFilePath, options} = routeConfig;

// Core plugins
if (config.rateLimit) {
await server.register(configureRateLimitPlugin(routeConfig));
}

await server.register(pluginLog);
await server.register(pluginSession);
await server.register(pluginPulse);
await server.register(inert);
await server.register(Scooter);
await server.register(configureInitialiseSessionPlugin({safelist: config.safelist,}));
// @ts-ignore
await server.register(configureInitialiseSessionPlugin({safelist: config.safelist}));
//@ts-ignore
await server.register(configureBlankiePlugin(config));
// @ts-ignore
//@ts-ignore
await server.register(configureCrumbPlugin(config, routeConfig));
await server.register(Schmervice);
await server.register(pluginAuth);
await server.register(LanguagePlugin);

server.registerService([AdapterCacheService, NotifyService, PayService, WebhookService, AddressService, TranslationLoaderService]);
if (config.isE2EModeEnabled && config.isE2EModeEnabled == "true") {
console.log("E2E Mode enabled")
server.registerService([Schmervice.withName("s3UploadService", MockUploadService),]);
// Register services in dependency order
server.registerService([PreAwardApiService]);
server.registerService([
AdapterCacheService,
NotifyService,
PayService,
WebhookService,
AddressService,
TranslationLoaderService
]);

// Upload service
if (config.isE2EModeEnabled === "true") {
console.log("E2E Mode enabled - using mock upload service")
server.registerService([Schmervice.withName("s3UploadService", MockUploadService)]);
} else {
server.registerService([S3UploadService]);
}

// @ts-ignore
//@ts-ignore
server.registerService(AdapterStatusService);

server.ext(
"onPreResponse",
(request: HapiRequest, h: HapiResponseToolkit) => {
const {response} = request;
// Response headers
server.ext("onPreResponse", (request: HapiRequest, h: HapiResponseToolkit) => {
const {response} = request;

if ("isBoom" in response && response.isBoom &&
response?.output?.statusCode >= 500 &&
response?.output?.statusCode < 600) {
Sentry.captureException(response);
}

if ("header" in response && response.header) {
response.header("X-Robots-Tag", "noindex, nofollow");

if ("isBoom" in response && response.isBoom
&& response?.output?.statusCode >= 500
&& response?.output?.statusCode < 600) {
Sentry.captureException(response);
return h.continue;
const existingCsp = response.headers["content-security-policy"];
if (typeof existingCsp === "string") {
const newCsp = existingCsp.replace(
/connect-src[^;]*/,
`connect-src 'self' https://${config.awsBucketName}.s3.${config.awsRegion}.amazonaws.com/`
);
response.header("Content-Security-Policy", newCsp);
}

if ("header" in response && response.header) {
response.header("X-Robots-Tag", "noindex, nofollow");

const existingHeaders = response.headers;
const existingCsp = existingHeaders["content-security-policy"] || "";
if (typeof existingCsp === "string") {
const newCsp = existingCsp?.replace(
/connect-src[^;]*/,
`connect-src 'self' https://${config.awsBucketName}.s3.${config.awsRegion}.amazonaws.com/`
);
response.header("Content-Security-Policy", newCsp);
}

const WEBFONT_EXTENSIONS = /\.(?:eot|ttf|woff|svg|woff2)$/i;
if (!WEBFONT_EXTENSIONS.test(request.url.toString())) {
response.header(
"cache-control",
"private, no-cache, no-store, must-revalidate, max-age=0"
);
response.header("pragma", "no-cache");
response.header("expires", "0");
} else {
response.header("cache-control", "public, max-age=604800, immutable");
}
// Cache control
const WEBFONT_EXTENSIONS = /\.(?:eot|ttf|woff|svg|woff2)$/i;
if (WEBFONT_EXTENSIONS.test(request.url.toString())) {
response.header("cache-control", "public, max-age=604800, immutable");
} else {
response.header("cache-control", "private, no-cache, no-store, must-revalidate, max-age=0");
response.header("pragma", "no-cache");
response.header("expires", "0");
}
return h.continue;
}
);

return h.continue;
});

// Request lifecycle handlers
server.ext("onPreHandler", (request: HapiRequest, h: HapiResponseToolkit) => {
determineLocal(request);
return h.continue;
});

server.ext("onRequest", (request: HapiRequest, h: HapiResponseToolkit) => {
// @ts-ignore
//@ts-ignore
const {pathname} = getRequestInfo(request);
//@ts-ignore
request.app.location = pathname;
determineLocal(request);
return h.continue;
});

// @ts-ignore
// Register application plugins
//@ts-ignore
await server.register(ViewLoaderPlugin);
// @ts-ignore
await server.register(ConfigureFormsPlugin(formFileName, formFilePath, options));
await server.register(FormRoutesPlugin);
await server.register(pluginApplicationStatus);
await server.register(publicRouterPlugin);
await server.register(errorHandlerPlugin);
Expand All @@ -213,8 +205,9 @@ async function createServer(routeConfig: RouteConfig) {
encoding: "base64json",
});

// Sentry error monitoring
// Error monitoring
await Sentry.setupHapiErrorHandler(server);

return server;
}

Expand Down
40 changes: 0 additions & 40 deletions runner/src/server/plugins/ConfigureFormsPlugin.ts

This file was deleted.

25 changes: 25 additions & 0 deletions runner/src/server/plugins/engine/FormRoutesPlugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import {RegisterFormAccessApi} from "./api";

const LOGGER_DATA = {
class: "FormRoutesPlugin",
}

/**
* This plugin registers all the routes needed for form access.
* Forms themselves are fetched on-demand from the Pre-Award API.
*/
export const FormRoutesPlugin = {
plugin: {
name: "@communitiesuk/runner/form-routes",
dependencies: "@hapi/vision",
register: async (server: any) => {
console.log({
...LOGGER_DATA,
message: `Registering form access routes. Forms will be fetched on-demand from Pre-Award API.`
});

// Register all form access routes (GET/POST handlers)
new RegisterFormAccessApi().register(server);
}
}
};
37 changes: 0 additions & 37 deletions runner/src/server/plugins/engine/MainPlugin.ts

This file was deleted.

Loading
Loading