From a05a8f805d32e5da44190229c49d60cd5800119e Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Wed, 8 Jul 2020 09:25:27 +0200 Subject: [PATCH 1/8] feat(proxy): implement proxy backend plugin --- packages/backend/package.json | 1 + packages/backend/src/index.ts | 5 +- packages/backend/src/plugins/proxy.ts | 25 ++++++++++ plugins/proxy-backend/.eslintrc.js | 3 ++ plugins/proxy-backend/README.md | 37 ++++++++++++++ plugins/proxy-backend/package.json | 50 +++++++++++++++++++ plugins/proxy-backend/src/index.ts | 17 +++++++ plugins/proxy-backend/src/run.ts | 33 ++++++++++++ plugins/proxy-backend/src/service/router.ts | 42 ++++++++++++++++ .../src/service/standaloneServer.ts | 48 ++++++++++++++++++ plugins/proxy-backend/src/setupTests.ts | 19 +++++++ yarn.lock | 29 ++++++++--- 12 files changed, 300 insertions(+), 9 deletions(-) create mode 100644 packages/backend/src/plugins/proxy.ts create mode 100644 plugins/proxy-backend/.eslintrc.js create mode 100644 plugins/proxy-backend/README.md create mode 100644 plugins/proxy-backend/package.json create mode 100644 plugins/proxy-backend/src/index.ts create mode 100644 plugins/proxy-backend/src/run.ts create mode 100644 plugins/proxy-backend/src/service/router.ts create mode 100644 plugins/proxy-backend/src/service/standaloneServer.ts create mode 100644 plugins/proxy-backend/src/setupTests.ts diff --git a/packages/backend/package.json b/packages/backend/package.json index 64ca2c3113..54cf1e7e1a 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -25,6 +25,7 @@ "@backstage/plugin-auth-backend": "^0.1.1-alpha.12", "@backstage/plugin-catalog-backend": "^0.1.1-alpha.12", "@backstage/plugin-identity-backend": "^0.1.1-alpha.12", + "@backstage/plugin-proxy-backend": "^0.1.1-alpha.12", "@backstage/plugin-scaffolder-backend": "^0.1.1-alpha.12", "@backstage/plugin-sentry-backend": "^0.1.1-alpha.12", "dockerode": "^3.2.0", diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index 3246c5f6f5..a50b1e38be 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -35,6 +35,7 @@ import catalog from './plugins/catalog'; import identity from './plugins/identity'; import scaffolder from './plugins/scaffolder'; import sentry from './plugins/sentry'; +import proxy from './plugins/proxy'; import { PluginEnvironment } from './types'; function makeCreateEnv(loadedConfigs: AppConfig[]) { @@ -63,6 +64,7 @@ async function main() { const scaffolderEnv = useHotMemoize(module, () => createEnv('scaffolder')); const authEnv = useHotMemoize(module, () => createEnv('auth')); const identityEnv = useHotMemoize(module, () => createEnv('identity')); + const proxyEnv = useHotMemoize(module, () => createEnv('proxy')); const service = createServiceBuilder(module) .loadConfig(configReader) @@ -73,7 +75,8 @@ async function main() { await sentry(getRootLogger().child({ type: 'plugin', plugin: 'sentry' })), ) .addRouter('/auth', await auth(authEnv)) - .addRouter('/identity', await identity(identityEnv)); + .addRouter('/identity', await identity(identityEnv)) + .addRouter('/', await proxy(proxyEnv)); await service.start().catch(err => { console.log(err); diff --git a/packages/backend/src/plugins/proxy.ts b/packages/backend/src/plugins/proxy.ts new file mode 100644 index 0000000000..4964de130e --- /dev/null +++ b/packages/backend/src/plugins/proxy.ts @@ -0,0 +1,25 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// @ts-ignore +import { createRouter } from '@backstage/plugin-proxy-backend'; +import { PluginEnvironment } from '../types'; + +export default async function createPlugin({ + logger, + config, +}: PluginEnvironment) { + return await createRouter({ logger, config }); +} diff --git a/plugins/proxy-backend/.eslintrc.js b/plugins/proxy-backend/.eslintrc.js new file mode 100644 index 0000000000..16a033dbc6 --- /dev/null +++ b/plugins/proxy-backend/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: [require.resolve('@backstage/cli/config/eslint.backend')], +}; diff --git a/plugins/proxy-backend/README.md b/plugins/proxy-backend/README.md new file mode 100644 index 0000000000..315178dac6 --- /dev/null +++ b/plugins/proxy-backend/README.md @@ -0,0 +1,37 @@ +# Proxy backend plugin + +This is the backend plugin that enables proxy definitions to be declared in and read from app-config.yaml. + +Relies on the `http-proxy-middleware` package. + +## Getting Started + +This backend plugin can be started in a standalone mode from directly in this package +with `yarn start`. However, it will have limited functionality and that process is +most convenient when developing the plugin itself. + +To run it within the backend do: + +1. Register the router in `packages/backend/src/index.ts`: + +``` +const proxyEnv = useHotMemoize(module, () => createEnv('proxy')); + +const service = createServiceBuilder(module) + .loadConfig(configReader) + /** several different routers */ + .addRouter('/', await proxy(proxyEnv)); +``` + +2. Start the backend + +```bash +yarn workspace example-backend start +``` + +This will launch the full example backend. + +## Links + +- (http-proxy-middleware)[https://www.npmjs.com/package/http-proxy-middleware] +- (The Backstage homepage)[https://backstage.io] diff --git a/plugins/proxy-backend/package.json b/plugins/proxy-backend/package.json new file mode 100644 index 0000000000..88348b4779 --- /dev/null +++ b/plugins/proxy-backend/package.json @@ -0,0 +1,50 @@ +{ + "name": "@backstage/plugin-proxy-backend", + "version": "0.1.1-alpha.12", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "private": false, + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "scripts": { + "start": "backstage-cli backend:dev", + "build": "backstage-cli backend:build", + "lint": "backstage-cli lint", + "test": "backstage-cli test", + "prepack": "backstage-cli prepack", + "postpack": "backstage-cli postpack", + "clean": "backstage-cli clean" + }, + "dependencies": { + "@backstage/backend-common": "^0.1.1-alpha.12", + "@backstage/config": "^0.1.1-alpha.12", + "@types/express": "^4.17.6", + "@types/http-proxy-middleware": "^0.19.3", + "express": "^4.17.1", + "express-promise-router": "^3.0.3", + "http-proxy-middleware": "^1.0.4", + "morgan": "^1.10.0", + "node-fetch": "^2.6.0", + "uuid": "^8.0.0", + "winston": "^3.2.1", + "yaml": "^1.9.2", + "yn": "^4.0.0", + "yup": "^0.29.1" + }, + "devDependencies": { + "@backstage/cli": "^0.1.1-alpha.12", + "@types/node-fetch": "^2.5.7", + "@types/supertest": "^2.0.8", + "@types/uuid": "^8.0.0", + "@types/yup": "^0.28.2", + "jest-fetch-mock": "^3.0.3", + "supertest": "^4.0.2" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/proxy-backend/src/index.ts b/plugins/proxy-backend/src/index.ts new file mode 100644 index 0000000000..7612c392a2 --- /dev/null +++ b/plugins/proxy-backend/src/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './service/router'; diff --git a/plugins/proxy-backend/src/run.ts b/plugins/proxy-backend/src/run.ts new file mode 100644 index 0000000000..b96989e4b8 --- /dev/null +++ b/plugins/proxy-backend/src/run.ts @@ -0,0 +1,33 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { getRootLogger } from '@backstage/backend-common'; +import yn from 'yn'; +import { startStandaloneServer } from './service/standaloneServer'; + +const port = process.env.PLUGIN_PORT ? Number(process.env.PLUGIN_PORT) : 7000; +const enableCors = yn(process.env.PLUGIN_CORS, { default: false }); +const logger = getRootLogger(); + +startStandaloneServer({ port, enableCors, logger }).catch(err => { + logger.error(err); + process.exit(1); +}); + +process.on('SIGINT', () => { + logger.info('CTRL+C pressed; exiting.'); + process.exit(0); +}); diff --git a/plugins/proxy-backend/src/service/router.ts b/plugins/proxy-backend/src/service/router.ts new file mode 100644 index 0000000000..0f5cd27502 --- /dev/null +++ b/plugins/proxy-backend/src/service/router.ts @@ -0,0 +1,42 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { errorHandler } from '@backstage/backend-common'; +import { AppConfig } from '@backstage/config'; +import express from 'express'; +import Router from 'express-promise-router'; +import { createProxyMiddleware } from 'http-proxy-middleware'; +import { Logger } from 'winston'; + +export interface RouterOptions { + logger: Logger; + config: AppConfig; +} + +export async function createRouter( + options: RouterOptions, +): Promise { + const router = Router(); + router.use(express.json()); + + const proxyConfig = options.config.data.proxy ?? {}; + Object.entries(proxyConfig).forEach(([route, proxyRouteConfig]) => { + router.use(createProxyMiddleware(route, proxyRouteConfig)); + }); + + router.use(errorHandler()); + return router; +} diff --git a/plugins/proxy-backend/src/service/standaloneServer.ts b/plugins/proxy-backend/src/service/standaloneServer.ts new file mode 100644 index 0000000000..22b64aaaf5 --- /dev/null +++ b/plugins/proxy-backend/src/service/standaloneServer.ts @@ -0,0 +1,48 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { createServiceBuilder } from '@backstage/backend-common'; +import { Server } from 'http'; +import { Logger } from 'winston'; +import { createRouter } from './router'; + +export interface ServerOptions { + port: number; + enableCors: boolean; + logger: Logger; +} + +export async function startStandaloneServer( + options: ServerOptions, +): Promise { + const logger = options.logger.child({ service: 'catalog-backend' }); + + logger.debug('Creating application...'); + + logger.debug('Starting application server...'); + const router = await createRouter({ + logger, + }); + const service = createServiceBuilder(module) + .enableCors({ origin: 'http://localhost:3000' }) + .addRouter('/proxy', router); + return await service.start().catch(err => { + logger.error(err); + process.exit(1); + }); +} + +module.hot?.accept(); diff --git a/plugins/proxy-backend/src/setupTests.ts b/plugins/proxy-backend/src/setupTests.ts new file mode 100644 index 0000000000..f7b6ca962d --- /dev/null +++ b/plugins/proxy-backend/src/setupTests.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +require('jest-fetch-mock').enableMocks(); + +export {}; diff --git a/yarn.lock b/yarn.lock index 8f5d9e8cec..a26245c3b4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3594,7 +3594,7 @@ resolved "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.6.3.tgz#619a55768eab98299e8f76747339f3373f134e69" integrity sha512-4KCE/agIcoQ9bIfa4sBxbZdnORzRjIw8JNQPLfqoNv7wQl/8f8mRbW68Q8wBsQFoJkPUHGlQYZ9sqi5WpfGSEQ== -"@types/http-proxy-middleware@*": +"@types/http-proxy-middleware@*", "@types/http-proxy-middleware@^0.19.3": version "0.19.3" resolved "https://registry.npmjs.org/@types/http-proxy-middleware/-/http-proxy-middleware-0.19.3.tgz#b2eb96fbc0f9ac7250b5d9c4c53aade049497d03" integrity sha512-lnBTx6HCOUeIJMLbI/LaL5EmdKLhczJY5oeXZpX/cXE4rRqb3RmV7VcMpiEfYkmTjipv3h7IAyIINe4plEv7cA== @@ -3960,13 +3960,6 @@ "@types/node" "*" rollup "^0.63.4" -"@types/sanitize-html@^1.23.3": - version "1.23.3" - resolved "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-1.23.3.tgz#26527783aba3bf195ad8a3c3e51bd3713526fc0d" - integrity sha512-Isg8N0ifKdDq6/kaNlIcWfapDXxxquMSk2XC5THsOICRyOIhQGds95XH75/PL/g9mExi4bL8otIqJM/Wo96WxA== - dependencies: - htmlparser2 "^4.1.0" - "@types/serve-static@*": version "1.13.3" resolved "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.3.tgz#eb7e1c41c4468272557e897e9171ded5e2ded9d1" @@ -10118,6 +10111,17 @@ http-proxy-middleware@0.19.1: lodash "^4.17.11" micromatch "^3.1.10" +http-proxy-middleware@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-1.0.4.tgz#425ea177986a0cda34f9c81ec961c719adb6c2a9" + integrity sha512-8wiqujNWlsZNbeTSSWMLUl/u70xbJ5VYRwPR8RcAbvsNxzAZbgwLzRvT96btbm3fAitZUmo5i8LY6WKGyHDgvA== + dependencies: + "@types/http-proxy" "^1.17.4" + http-proxy "^1.18.1" + is-glob "^4.0.1" + lodash "^4.17.15" + micromatch "^4.0.2" + http-proxy@^1.17.0: version "1.18.0" resolved "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.0.tgz#dbe55f63e75a347db7f3d99974f2692a314a6a3a" @@ -10127,6 +10131,15 @@ http-proxy@^1.17.0: follow-redirects "^1.0.0" requires-port "^1.0.0" +http-proxy@^1.18.1: + version "1.18.1" + resolved "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + http-signature@~1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" From 237502140ba7de938bfb8da58cfbc47ecdcd1c97 Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Wed, 8 Jul 2020 09:25:56 +0200 Subject: [PATCH 2/8] feat(proxy): start using proxy for circleci --- app-config.yaml | 7 +++++++ packages/app/src/apis.ts | 2 +- plugins/circleci/package.json | 9 --------- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/app-config.yaml b/app-config.yaml index 39527c20bd..b0b7666f24 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -10,5 +10,12 @@ backend: methods: [GET, POST, PUT, DELETE] credentials: true +proxy: + '/circleci/api': + target: 'https://circleci.com/api/v1.1' + changeOrigin: true + pathRewrite: + '^/circleci/api/': '/' + organization: name: Spotify diff --git a/packages/app/src/apis.ts b/packages/app/src/apis.ts index bc7170c93a..3f9765f5e2 100644 --- a/packages/app/src/apis.ts +++ b/packages/app/src/apis.ts @@ -72,7 +72,7 @@ export const apis = (config: ConfigApi) => { ); builder.add(storageApiRef, WebStorage.create({ errorApi })); - builder.add(circleCIApiRef, new CircleCIApi()); + builder.add(circleCIApiRef, new CircleCIApi(`${backendUrl}/circleci/api`)); builder.add(featureFlagsApiRef, new FeatureFlags()); builder.add(lighthouseApiRef, new LighthouseRestApi('http://localhost:3003')); diff --git a/plugins/circleci/package.json b/plugins/circleci/package.json index 89f6335dcc..a27cd6b81c 100644 --- a/plugins/circleci/package.json +++ b/plugins/circleci/package.json @@ -10,15 +10,6 @@ "main": "dist/index.esm.js", "types": "dist/index.d.ts" }, - "proxy": { - "/circleci/api": { - "target": "https://circleci.com/api/v1.1", - "changeOrigin": true, - "pathRewrite": { - "^/circleci/api/": "/" - } - } - }, "scripts": { "build": "backstage-cli plugin:build", "lint": "backstage-cli lint", From 14537751b3f22a2f6cdd5ff6d6fb78917dc4dd5b Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Wed, 8 Jul 2020 09:36:11 +0200 Subject: [PATCH 3/8] fix(proxy): private package for now --- plugins/proxy-backend/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/proxy-backend/package.json b/plugins/proxy-backend/package.json index 88348b4779..6eb8dab99d 100644 --- a/plugins/proxy-backend/package.json +++ b/plugins/proxy-backend/package.json @@ -1,10 +1,10 @@ { "name": "@backstage/plugin-proxy-backend", - "version": "0.1.1-alpha.12", + "version": "0.1.1-alpha.13", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", - "private": false, + "private": true, "publishConfig": { "access": "public", "main": "dist/index.cjs.js", From ed0bd8f6165c1ebb060e50201bff51df6845d8d5 Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Thu, 9 Jul 2020 01:54:31 +0200 Subject: [PATCH 4/8] fix(proxy): types and versions --- plugins/proxy-backend/package.json | 13 ++++++++++--- plugins/proxy-backend/src/service/router.ts | 6 +++--- .../proxy-backend/src/service/standaloneServer.ts | 10 ++++++++-- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/plugins/proxy-backend/package.json b/plugins/proxy-backend/package.json index 6eb8dab99d..521f190eba 100644 --- a/plugins/proxy-backend/package.json +++ b/plugins/proxy-backend/package.json @@ -20,8 +20,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.1.1-alpha.12", - "@backstage/config": "^0.1.1-alpha.12", + "@backstage/backend-common": "^0.1.1-alpha.13", + "@backstage/config": "^0.1.1-alpha.13", + "@backstage/config-loader": "^0.1.1-alpha.13", "@types/express": "^4.17.6", "@types/http-proxy-middleware": "^0.19.3", "express": "^4.17.1", @@ -36,7 +37,7 @@ "yup": "^0.29.1" }, "devDependencies": { - "@backstage/cli": "^0.1.1-alpha.12", + "@backstage/cli": "^0.1.1-alpha.13", "@types/node-fetch": "^2.5.7", "@types/supertest": "^2.0.8", "@types/uuid": "^8.0.0", @@ -44,6 +45,12 @@ "jest-fetch-mock": "^3.0.3", "supertest": "^4.0.2" }, + "workspaces": { + "nohoist": [ + "http-proxy-middleware", + "@types/http-proxy-middleware" + ] + }, "files": [ "dist" ] diff --git a/plugins/proxy-backend/src/service/router.ts b/plugins/proxy-backend/src/service/router.ts index 0f5cd27502..a8fd70c10d 100644 --- a/plugins/proxy-backend/src/service/router.ts +++ b/plugins/proxy-backend/src/service/router.ts @@ -15,7 +15,7 @@ */ import { errorHandler } from '@backstage/backend-common'; -import { AppConfig } from '@backstage/config'; +import { Config } from '@backstage/config'; import express from 'express'; import Router from 'express-promise-router'; import { createProxyMiddleware } from 'http-proxy-middleware'; @@ -23,7 +23,7 @@ import { Logger } from 'winston'; export interface RouterOptions { logger: Logger; - config: AppConfig; + config: Config; } export async function createRouter( @@ -32,7 +32,7 @@ export async function createRouter( const router = Router(); router.use(express.json()); - const proxyConfig = options.config.data.proxy ?? {}; + const proxyConfig = options.config.get('proxy') ?? {}; Object.entries(proxyConfig).forEach(([route, proxyRouteConfig]) => { router.use(createProxyMiddleware(route, proxyRouteConfig)); }); diff --git a/plugins/proxy-backend/src/service/standaloneServer.ts b/plugins/proxy-backend/src/service/standaloneServer.ts index 22b64aaaf5..84c803eeac 100644 --- a/plugins/proxy-backend/src/service/standaloneServer.ts +++ b/plugins/proxy-backend/src/service/standaloneServer.ts @@ -18,6 +18,8 @@ import { createServiceBuilder } from '@backstage/backend-common'; import { Server } from 'http'; import { Logger } from 'winston'; import { createRouter } from './router'; +import { ConfigReader } from '@backstage/config'; +import { loadConfig } from '@backstage/config-loader'; export interface ServerOptions { port: number; @@ -28,17 +30,21 @@ export interface ServerOptions { export async function startStandaloneServer( options: ServerOptions, ): Promise { - const logger = options.logger.child({ service: 'catalog-backend' }); + const logger = options.logger.child({ service: 'proxy-backend' }); logger.debug('Creating application...'); - logger.debug('Starting application server...'); + const config = ConfigReader.fromConfigs(await loadConfig()); const router = await createRouter({ + config, logger, }); const service = createServiceBuilder(module) .enableCors({ origin: 'http://localhost:3000' }) .addRouter('/proxy', router); + + logger.debug('Starting application server...'); + return await service.start().catch(err => { logger.error(err); process.exit(1); From 4a990863fdea57163a5b3077fd504b5813ceead0 Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Fri, 10 Jul 2020 00:56:40 +0200 Subject: [PATCH 5/8] fix(proxy): add test --- .../proxy-backend/src/service/router.test.ts | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 plugins/proxy-backend/src/service/router.test.ts diff --git a/plugins/proxy-backend/src/service/router.test.ts b/plugins/proxy-backend/src/service/router.test.ts new file mode 100644 index 0000000000..699ed9b99d --- /dev/null +++ b/plugins/proxy-backend/src/service/router.test.ts @@ -0,0 +1,32 @@ +/* + * Copyright 2020 Spotify AB + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { createRouter } from './router'; +import winston from 'winston'; +import { ConfigReader } from '@backstage/config'; +import { loadConfig } from '@backstage/config-loader'; + +describe('createRouter', () => { + it('works', async () => { + const logger = winston.createLogger(); + const config = ConfigReader.fromConfigs(await loadConfig()); + const router = await createRouter({ + config, + logger, + }); + expect(router).toBeDefined(); + }); +}); From 021b03669a712afacb210a3ec13a2711dd9be1ed Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Mon, 13 Jul 2020 11:19:20 +0200 Subject: [PATCH 6/8] docs(proxy): add link to plugin creation docs --- docs/getting-started/structure-of-a-plugin.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/getting-started/structure-of-a-plugin.md b/docs/getting-started/structure-of-a-plugin.md index fcfb2c0d46..a277628b6c 100644 --- a/docs/getting-started/structure-of-a-plugin.md +++ b/docs/getting-started/structure-of-a-plugin.md @@ -95,4 +95,13 @@ There are two things needed for a Backstage app to start making use of a plugin. Luckily these two steps happen automatically when you create a plugin with the Backstage CLI. +## Talking to the outside world + +If your plugin needs to communicate with services outside the backstage +environment you will probably face challenges like CORS policies and/or +backend-side authorization. To smooth this process out you can use proxy - +either the one you already have (like nginx/haproxy/etc) or the proxy-backend +plugin that we provide for the backstage backend. +[Read more](../../plugins/proxy-backend/README.md) + [Back to Getting Started](README.md) From ac27ab635bf7a2d01cbd760e752c52da106e7ed7 Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Mon, 13 Jul 2020 11:21:43 +0200 Subject: [PATCH 7/8] fix(proxy): versions, private --- plugins/proxy-backend/package.json | 10 +--------- plugins/proxy-backend/src/service/router.ts | 6 +----- yarn.lock | 17 ++++++++--------- 3 files changed, 10 insertions(+), 23 deletions(-) diff --git a/plugins/proxy-backend/package.json b/plugins/proxy-backend/package.json index 521f190eba..9a9352ba83 100644 --- a/plugins/proxy-backend/package.json +++ b/plugins/proxy-backend/package.json @@ -4,7 +4,6 @@ "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", - "private": true, "publishConfig": { "access": "public", "main": "dist/index.cjs.js", @@ -24,10 +23,9 @@ "@backstage/config": "^0.1.1-alpha.13", "@backstage/config-loader": "^0.1.1-alpha.13", "@types/express": "^4.17.6", - "@types/http-proxy-middleware": "^0.19.3", "express": "^4.17.1", "express-promise-router": "^3.0.3", - "http-proxy-middleware": "^1.0.4", + "http-proxy-middleware": "^0.19.1", "morgan": "^1.10.0", "node-fetch": "^2.6.0", "uuid": "^8.0.0", @@ -45,12 +43,6 @@ "jest-fetch-mock": "^3.0.3", "supertest": "^4.0.2" }, - "workspaces": { - "nohoist": [ - "http-proxy-middleware", - "@types/http-proxy-middleware" - ] - }, "files": [ "dist" ] diff --git a/plugins/proxy-backend/src/service/router.ts b/plugins/proxy-backend/src/service/router.ts index a8fd70c10d..2c8af574bd 100644 --- a/plugins/proxy-backend/src/service/router.ts +++ b/plugins/proxy-backend/src/service/router.ts @@ -14,11 +14,10 @@ * limitations under the License. */ -import { errorHandler } from '@backstage/backend-common'; import { Config } from '@backstage/config'; import express from 'express'; import Router from 'express-promise-router'; -import { createProxyMiddleware } from 'http-proxy-middleware'; +import createProxyMiddleware from 'http-proxy-middleware'; import { Logger } from 'winston'; export interface RouterOptions { @@ -30,13 +29,10 @@ export async function createRouter( options: RouterOptions, ): Promise { const router = Router(); - router.use(express.json()); - const proxyConfig = options.config.get('proxy') ?? {}; Object.entries(proxyConfig).forEach(([route, proxyRouteConfig]) => { router.use(createProxyMiddleware(route, proxyRouteConfig)); }); - router.use(errorHandler()); return router; } diff --git a/yarn.lock b/yarn.lock index 35b06e3d21..ca6bdad85b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3599,7 +3599,7 @@ resolved "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.6.3.tgz#619a55768eab98299e8f76747339f3373f134e69" integrity sha512-4KCE/agIcoQ9bIfa4sBxbZdnORzRjIw8JNQPLfqoNv7wQl/8f8mRbW68Q8wBsQFoJkPUHGlQYZ9sqi5WpfGSEQ== -"@types/http-proxy-middleware@*", "@types/http-proxy-middleware@^0.19.3": +"@types/http-proxy-middleware@*": version "0.19.3" resolved "https://registry.npmjs.org/@types/http-proxy-middleware/-/http-proxy-middleware-0.19.3.tgz#b2eb96fbc0f9ac7250b5d9c4c53aade049497d03" integrity sha512-lnBTx6HCOUeIJMLbI/LaL5EmdKLhczJY5oeXZpX/cXE4rRqb3RmV7VcMpiEfYkmTjipv3h7IAyIINe4plEv7cA== @@ -10173,16 +10173,15 @@ http-proxy-middleware@0.19.1: lodash "^4.17.11" micromatch "^3.1.10" -http-proxy-middleware@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-1.0.4.tgz#425ea177986a0cda34f9c81ec961c719adb6c2a9" - integrity sha512-8wiqujNWlsZNbeTSSWMLUl/u70xbJ5VYRwPR8RcAbvsNxzAZbgwLzRvT96btbm3fAitZUmo5i8LY6WKGyHDgvA== +http-proxy-middleware@^0.19.1: + version "0.19.2" + resolved "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.2.tgz#ee73dcc8348165afefe8de2ff717751d181608ee" + integrity sha512-aYk1rTKqLTus23X3L96LGNCGNgWpG4cG0XoZIT1GUPhhulEHX/QalnO6Vbo+WmKWi4AL2IidjuC0wZtbpg0yhQ== dependencies: - "@types/http-proxy" "^1.17.4" http-proxy "^1.18.1" - is-glob "^4.0.1" - lodash "^4.17.15" - micromatch "^4.0.2" + is-glob "^4.0.0" + lodash "^4.17.11" + micromatch "^3.1.10" http-proxy@^1.17.0: version "1.18.0" From f1b479c772e17ca70220c1574cc5197e82434d86 Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Mon, 13 Jul 2020 11:22:08 +0200 Subject: [PATCH 8/8] fix(proxy): mount on /proxy --- packages/app/src/apis.ts | 5 ++++- packages/backend/src/index.ts | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/app/src/apis.ts b/packages/app/src/apis.ts index 9ab4a125a6..3d8a381635 100644 --- a/packages/app/src/apis.ts +++ b/packages/app/src/apis.ts @@ -74,7 +74,10 @@ export const apis = (config: ConfigApi) => { ); builder.add(storageApiRef, WebStorage.create({ errorApi })); - builder.add(circleCIApiRef, new CircleCIApi(`${backendUrl}/circleci/api`)); + builder.add( + circleCIApiRef, + new CircleCIApi(`${backendUrl}/proxy/circleci/api`), + ); builder.add(featureFlagsApiRef, new FeatureFlags()); builder.add(lighthouseApiRef, new LighthouseRestApi('http://localhost:3003')); diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index 7c9de7c3bb..ea5142da19 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -86,7 +86,7 @@ async function main() { .addRouter('/auth', await auth(authEnv)) .addRouter('/identity', await identity(identityEnv)) .addRouter('/techdocs', await techdocs(techdocsEnv)) - .addRouter('/', await proxy(proxyEnv)); + .addRouter('/proxy', await proxy(proxyEnv)); await service.start().catch(err => { console.log(err);