From 0da7d01decf437607c71ae5d54d0629f9599bdea Mon Sep 17 00:00:00 2001 From: David Mehren Date: Thu, 23 Sep 2021 22:36:13 +0200 Subject: [PATCH] Add dev-proxy to frontend dev server This adds a reverse proxy to the backend, that automatically redirects requests that are not handled by the backend to the React dev server running on port 3001. The reverse proxy is only enabled when NODE_ENV is set to 'development'. Signed-off-by: David Mehren --- package.json | 1 + src/main.ts | 2 ++ src/utils/frontend-integration.ts | 35 ++++++++++++++++++ src/utils/use-unless.ts | 18 ++++++++++ yarn.lock | 59 +++++++++++++++++++++++++++++-- 5 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 src/utils/frontend-integration.ts create mode 100644 src/utils/use-unless.ts diff --git a/package.json b/package.json index 3c54f8776..73b577eef 100644 --- a/package.json +++ b/package.json @@ -83,6 +83,7 @@ "eslint-plugin-jest": "25.0.5", "eslint-plugin-local-rules": "1.1.0", "eslint-plugin-prettier": "4.0.0", + "http-proxy-middleware": "^2.0.1", "jest": "27.2.5", "prettier": "2.4.1", "source-map-support": "0.5.20", diff --git a/src/main.ts b/src/main.ts index 1a9048bb6..1fcc74a35 100644 --- a/src/main.ts +++ b/src/main.ts @@ -14,6 +14,7 @@ import { AuthConfig } from './config/auth.config'; import { MediaConfig } from './config/media.config'; import { ConsoleLoggerService } from './logger/console-logger.service'; import { BackendType } from './media/backends/backend-type.enum'; +import { setupFrontendProxy } from './utils/frontend-integration'; import { setupSessionMiddleware } from './utils/session'; import { setupValidationPipe } from './utils/setup-pipes'; import { setupPrivateApiDocs, setupPublicApiDocs } from './utils/swagger'; @@ -47,6 +48,7 @@ async function bootstrap(): Promise { `Serving OpenAPI docs for private api under '/private/apidoc'`, 'AppBootstrap', ); + setupFrontendProxy(app, logger); } setupSessionMiddleware(app, authConfig); diff --git a/src/utils/frontend-integration.ts b/src/utils/frontend-integration.ts new file mode 100644 index 000000000..55d109436 --- /dev/null +++ b/src/utils/frontend-integration.ts @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { NestExpressApplication } from '@nestjs/platform-express'; +import { createProxyMiddleware } from 'http-proxy-middleware'; + +import { ConsoleLoggerService } from '../logger/console-logger.service'; +import { useUnless } from './use-unless'; + +export function setupFrontendProxy( + app: NestExpressApplication, + logger: ConsoleLoggerService, +): void { + logger.log( + `Setting up proxy to frontend dev server on port 3001`, + 'setupFrontendProxy', + ); + const frontendProxy = createProxyMiddleware({ + logProvider: () => { + return { + log: (msg) => logger.log(msg, 'FrontendProxy'), + debug: (msg) => logger.debug(msg, 'FrontendProxy'), + info: (msg) => logger.log(msg, 'FrontendProxy'), + warn: (msg) => logger.warn(msg, 'FrontendProxy'), + error: (msg) => logger.error(msg, 'FrontendProxy'), + }; + }, + target: 'http://localhost:3001', + changeOrigin: true, + ws: true, + }); + app.use(useUnless(['/api', '/public'], frontendProxy)); +} diff --git a/src/utils/use-unless.ts b/src/utils/use-unless.ts new file mode 100644 index 000000000..ddf35a21f --- /dev/null +++ b/src/utils/use-unless.ts @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { NextFunction, Request, Response } from 'express'; + +export function useUnless( + paths: string[], + middleware: (req: Request, res: Response, next: NextFunction) => unknown, +) { + return function (req: Request, res: Response, next: NextFunction): unknown { + if (paths.some((path) => req.path.startsWith(path))) { + return next(); + } + return middleware(req, res, next); + }; +} diff --git a/yarn.lock b/yarn.lock index 17bbb5b46..85da90bbc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1582,6 +1582,15 @@ __metadata: languageName: node linkType: hard +"@types/http-proxy@npm:^1.17.5": + version: 1.17.7 + resolution: "@types/http-proxy@npm:1.17.7" + dependencies: + "@types/node": "*" + checksum: 88f9c75ca65378d0287d8d0b1dbeed372c8267f4841fe2f6f2d759522494382d3943bc6cc774bef7dd125464a266bafeda813d3658b17a2d1e74acc4efb6e21c + languageName: node + linkType: hard + "@types/istanbul-lib-coverage@npm:*, @types/istanbul-lib-coverage@npm:^2.0.0, @types/istanbul-lib-coverage@npm:^2.0.1": version: 2.0.3 resolution: "@types/istanbul-lib-coverage@npm:2.0.3" @@ -4172,6 +4181,13 @@ __metadata: languageName: node linkType: hard +"eventemitter3@npm:^4.0.0": + version: 4.0.7 + resolution: "eventemitter3@npm:4.0.7" + checksum: 1875311c42fcfe9c707b2712c32664a245629b42bb0a5a84439762dd0fd637fc54d078155ea83c2af9e0323c9ac13687e03cfba79b03af9f40c89b4960099374 + languageName: node + linkType: hard + "events@npm:^3.0.0, events@npm:^3.2.0": version: 3.3.0 resolution: "events@npm:3.3.0" @@ -4502,7 +4518,7 @@ __metadata: languageName: node linkType: hard -"follow-redirects@npm:^1.14.4": +"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.14.4": version: 1.14.4 resolution: "follow-redirects@npm:1.14.4" peerDependenciesMeta: @@ -4985,6 +5001,7 @@ __metadata: eslint-plugin-prettier: 4.0.0 express-session: 1.17.2 file-type: 16.5.3 + http-proxy-middleware: ^2.0.1 jest: 27.2.5 joi: 17.4.2 minio: 7.0.19 @@ -5077,6 +5094,30 @@ __metadata: languageName: node linkType: hard +"http-proxy-middleware@npm:^2.0.1": + version: 2.0.1 + resolution: "http-proxy-middleware@npm:2.0.1" + dependencies: + "@types/http-proxy": ^1.17.5 + http-proxy: ^1.18.1 + is-glob: ^4.0.1 + is-plain-obj: ^3.0.0 + micromatch: ^4.0.2 + checksum: 0de65bc6644b6efae5d26cd3bec071ceaeb92f26856ffee5ecdde9c702ea1435936e7dfb09da2ac0883eada80fdc993e9925902fc10bf6625565d6365f8cb30f + languageName: node + linkType: hard + +"http-proxy@npm:^1.18.1": + version: 1.18.1 + resolution: "http-proxy@npm:1.18.1" + dependencies: + eventemitter3: ^4.0.0 + follow-redirects: ^1.0.0 + requires-port: ^1.0.0 + checksum: f5bd96bf83e0b1e4226633dbb51f8b056c3e6321917df402deacec31dd7fe433914fc7a2c1831cf7ae21e69c90b3a669b8f434723e9e8b71fd68afe30737b6a5 + languageName: node + linkType: hard + "http-signature@npm:~1.2.0": version: 1.2.0 resolution: "http-signature@npm:1.2.0" @@ -5491,6 +5532,13 @@ __metadata: languageName: node linkType: hard +"is-plain-obj@npm:^3.0.0": + version: 3.0.0 + resolution: "is-plain-obj@npm:3.0.0" + checksum: a6ebdf8e12ab73f33530641972a72a4b8aed6df04f762070d823808303e4f76d87d5ea5bd76f96a7bbe83d93f04ac7764429c29413bd9049853a69cb630fb21c + languageName: node + linkType: hard + "is-potential-custom-element-name@npm:^1.0.1": version: 1.0.1 resolution: "is-potential-custom-element-name@npm:1.0.1" @@ -6641,7 +6689,7 @@ __metadata: languageName: node linkType: hard -"micromatch@npm:^4.0.4": +"micromatch@npm:^4.0.2, micromatch@npm:^4.0.4": version: 4.0.4 resolution: "micromatch@npm:4.0.4" dependencies: @@ -8017,6 +8065,13 @@ __metadata: languageName: node linkType: hard +"requires-port@npm:^1.0.0": + version: 1.0.0 + resolution: "requires-port@npm:1.0.0" + checksum: eee0e303adffb69be55d1a214e415cf42b7441ae858c76dfc5353148644f6fd6e698926fc4643f510d5c126d12a705e7c8ed7e38061113bdf37547ab356797ff + languageName: node + linkType: hard + "resolve-cwd@npm:^3.0.0": version: 3.0.0 resolution: "resolve-cwd@npm:3.0.0"