backend -> backend-legacy, backend-next -> backend

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2024-05-01 15:06:45 +02:00
parent 4eca697bf3
commit f4fcce8cdc
50 changed files with 7791 additions and 7745 deletions
File diff suppressed because it is too large Load Diff
+7 -9
View File
@@ -1,6 +1,6 @@
# example-backend
This package is an EXAMPLE of a Backstage backend.
This package is an EXAMPLE of a Backstage backend using the [new backend system](https://backstage.io/docs/backend-system/).
The main purpose of this package is to provide a test bed for Backstage plugins
that have a backend part. Feel free to experiment locally or within your fork
@@ -16,22 +16,22 @@ To run the example backend, first go to the project root and run
yarn install
```
You should only need to do this once.
This will install all dependencies for the project. You only need to do this once, unless you make changes to the dependency definitions.
After that, go to the `packages/backend` directory and run
You can then start the backend by running the following command in the repo root:
```bash
yarn start
yarn start-backend
```
If you want to override any configuration locally, for example adding any secrets,
you can do so in `app-config.local.yaml`.
you can do so in `app-config.local.yaml`, next to `app-config.yaml`.
The backend starts up on port 7007 per default.
### Debugging
The backend is a node process that can be inspected to allow breakpoints and live debugging. To enable this, pass the `--inspect` flag to [backend:dev](https://backstage.io/docs/local-dev/cli-build-system#backend-development).
The backend is a node process that can be inspected to allow breakpoints and live debugging. To enable this, pass the `--inspect` flag when starting the backend.
To debug the backend in [Visual Studio Code](https://code.visualstudio.com/):
@@ -51,9 +51,7 @@ in `app-config.yaml` under `catalog.locations`. For local development you can ov
## Authentication
We chose [Passport](http://www.passportjs.org/) as authentication platform due to its comprehensive set of supported authentication [strategies](http://www.passportjs.org/packages/).
Read more about the [auth-backend](https://github.com/backstage/backstage/blob/master/plugins/auth-backend/README.md) and [how to add a new provider](https://github.com/backstage/backstage/blob/master/docs/auth/add-auth-provider.md)
The example backend has guest access enabled by default. This means you do not need to configure a real authentication provider, but will instead be logged in as a guest user.
## Documentation
+8 -23
View File
@@ -1,27 +1,12 @@
# Knip report
## Unused dependencies (13)
## Unused dependencies (5)
| Name | Location | Severity |
| :------------------------------------------------- | :----------- | :------- |
| @backstage/plugin-scaffolder-backend-module-gitlab | package.json | error |
| @backstage/plugin-scaffolder-backend-module-rails | package.json | error |
| @backstage/plugin-azure-sites-common | package.json | error |
| @backstage/plugin-tech-insights-node | package.json | error |
| azure-devops-node-api | package.json | error |
| pg-connection-string | package.json | error |
| @gitbeaker/node | package.json | error |
| better-sqlite3 | package.json | error |
| @octokit/rest | package.json | error |
| example-app | package.json | error |
| mysql2 | package.json | error |
| luxon | package.json | error |
| pg | package.json | error |
## Unused devDependencies (2)
| Name | Location | Severity |
| :------------------------------- | :----------- | :------- |
| @types/express-serve-static-core | package.json | error |
| @types/luxon | package.json | error |
| Name | Location | Severity |
| :----------------------------------------------- | :----------- | :------- |
| @backstage/plugin-catalog-backend-module-openapi | package.json | error |
| @backstage/plugin-search-backend-node | package.json | error |
| @backstage/plugin-permission-common | package.json | error |
| @backstage/plugin-permission-node | package.json | error |
| @backstage/backend-tasks | package.json | error |
+15 -42
View File
@@ -1,6 +1,6 @@
{
"name": "example-backend",
"version": "0.2.98-next.1",
"version": "0.0.26-next.1",
"main": "dist/index.cjs.js",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -18,75 +18,48 @@
"backstage"
],
"scripts": {
"start": "backstage-cli package start",
"build": "backstage-cli package build",
"lint": "backstage-cli package lint",
"test": "backstage-cli package test",
"clean": "backstage-cli package clean",
"lint": "backstage-cli package lint",
"start": "backstage-cli package start",
"test": "backstage-cli package test",
"build-image": "docker build ../.. -f Dockerfile --tag example-backend"
},
"dependencies": {
"@backstage/backend-common": "workspace:^",
"@backstage/backend-defaults": "workspace:^",
"@backstage/backend-plugin-api": "workspace:^",
"@backstage/backend-tasks": "workspace:^",
"@backstage/catalog-client": "workspace:^",
"@backstage/catalog-model": "workspace:^",
"@backstage/config": "workspace:^",
"@backstage/integration": "workspace:^",
"@backstage/plugin-app-backend": "workspace:^",
"@backstage/plugin-auth-backend": "workspace:^",
"@backstage/plugin-auth-backend-module-github-provider": "workspace:^",
"@backstage/plugin-auth-backend-module-guest-provider": "workspace:^",
"@backstage/plugin-auth-node": "workspace:^",
"@backstage/plugin-catalog-backend": "workspace:^",
"@backstage/plugin-catalog-backend-module-backstage-openapi": "workspace:^",
"@backstage/plugin-catalog-backend-module-openapi": "workspace:^",
"@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "workspace:^",
"@backstage/plugin-catalog-backend-module-unprocessed": "workspace:^",
"@backstage/plugin-catalog-node": "workspace:^",
"@backstage/plugin-devtools-backend": "workspace:^",
"@backstage/plugin-events-backend": "workspace:^",
"@backstage/plugin-events-node": "workspace:^",
"@backstage/plugin-kubernetes-backend": "workspace:^",
"@backstage/plugin-notifications-backend": "workspace:^",
"@backstage/plugin-permission-backend": "workspace:^",
"@backstage/plugin-permission-backend-module-allow-all-policy": "workspace:^",
"@backstage/plugin-permission-common": "workspace:^",
"@backstage/plugin-permission-node": "workspace:^",
"@backstage/plugin-proxy-backend": "workspace:^",
"@backstage/plugin-scaffolder-backend": "workspace:^",
"@backstage/plugin-scaffolder-backend-module-confluence-to-markdown": "workspace:^",
"@backstage/plugin-scaffolder-backend-module-gitlab": "workspace:^",
"@backstage/plugin-scaffolder-backend-module-notifications": "workspace:^",
"@backstage/plugin-scaffolder-backend-module-rails": "workspace:^",
"@backstage/plugin-scaffolder-backend-module-github": "workspace:^",
"@backstage/plugin-search-backend": "workspace:^",
"@backstage/plugin-search-backend-module-catalog": "workspace:^",
"@backstage/plugin-search-backend-module-elasticsearch": "workspace:^",
"@backstage/plugin-search-backend-module-explore": "workspace:^",
"@backstage/plugin-search-backend-module-pg": "workspace:^",
"@backstage/plugin-search-backend-module-techdocs": "workspace:^",
"@backstage/plugin-search-backend-node": "workspace:^",
"@backstage/plugin-signals-backend": "workspace:^",
"@backstage/plugin-signals-node": "workspace:^",
"@backstage/plugin-techdocs-backend": "workspace:^",
"@gitbeaker/node": "^35.1.0",
"@octokit/rest": "^19.0.3",
"@opentelemetry/api": "^1.4.1",
"@opentelemetry/exporter-prometheus": "^0.50.0",
"@opentelemetry/sdk-metrics": "^1.13.0",
"azure-devops-node-api": "^12.0.0",
"better-sqlite3": "^9.0.0",
"dockerode": "^4.0.0",
"example-app": "link:../app",
"express": "^4.17.1",
"express-prom-bundle": "^7.0.0",
"express-promise-router": "^4.1.0",
"luxon": "^3.0.0",
"mysql2": "^3.0.0",
"pg": "^8.11.3",
"pg-connection-string": "^2.3.0",
"prom-client": "^15.0.0",
"winston": "^3.2.1"
"@backstage/plugin-techdocs-backend": "workspace:^"
},
"devDependencies": {
"@backstage/cli": "workspace:^",
"@types/dockerode": "^3.3.0",
"@types/express": "^4.17.6",
"@types/express-serve-static-core": "^4.17.5",
"@types/luxon": "^3.0.0"
"@backstage/cli": "workspace:^"
},
"files": [
"dist"
-21
View File
@@ -1,21 +0,0 @@
# This is an example configuration file for Prometheus, that is set to scrape
# the metrics endpoint exposed by the example backend.
#
# If you want to run a docker based Prometheus instance locally to try it out,
# run the following command in the Backstage root:
#
# docker run --mount type=bind,source=./packages/backend/prometheus.yml,destination=/etc/prometheus/prometheus.yml --publish published=9090,target=9090,protocol=tcp prom/prometheus
#
# After this, you have a Prometheus instance on http://localhost:9090
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'prometheus'
static_configs:
# This target address specifically works on Docker for MacOS - you may
# need to change it to another host depending on your OS and Docker
# engine.
- targets: ['host.docker.internal:9464']
@@ -0,0 +1,65 @@
/*
* Copyright 2024 The Backstage Authors
*
* 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 { createBackendModule } from '@backstage/backend-plugin-api';
import {
DEFAULT_NAMESPACE,
stringifyEntityRef,
} from '@backstage/catalog-model';
import { githubAuthenticator } from '@backstage/plugin-auth-backend-module-github-provider';
import {
authProvidersExtensionPoint,
createOAuthProviderFactory,
} from '@backstage/plugin-auth-node';
export default createBackendModule({
pluginId: 'auth',
moduleId: 'githubProvider',
register(reg) {
reg.registerInit({
deps: { providers: authProvidersExtensionPoint },
async init({ providers }) {
providers.registerProvider({
providerId: 'github',
factory: createOAuthProviderFactory({
authenticator: githubAuthenticator,
async signInResolver({ result: { fullProfile } }, ctx) {
const userId = fullProfile.username;
if (!userId) {
throw new Error(
`GitHub user profile does not contain a username`,
);
}
const userEntityRef = stringifyEntityRef({
kind: 'User',
name: userId,
namespace: DEFAULT_NAMESPACE,
});
return ctx.issueToken({
claims: {
sub: userEntityRef,
ent: [userEntityRef],
},
});
},
}),
});
},
});
},
});
-24
View File
@@ -1,24 +0,0 @@
/*
* Copyright 2020 The Backstage Authors
*
* 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 { PluginEnvironment } from './types';
describe('test', () => {
it('unbreaks the test runner', () => {
const unbreaker = {} as PluginEnvironment;
expect(unbreaker).toBeTruthy();
});
});
+31 -164
View File
@@ -14,171 +14,38 @@
* limitations under the License.
*/
/*
* Hi!
*
* Note that this is an EXAMPLE Backstage backend. Please check the README.
*
* Happy hacking!
*/
import { createBackend } from '@backstage/backend-defaults';
import Router from 'express-promise-router';
import {
CacheManager,
createServiceBuilder,
DatabaseManager,
getRootLogger,
HostDiscovery,
loadBackendConfig,
notFoundHandler,
ServerTokenManager,
UrlReaders,
useHotMemoize,
} from '@backstage/backend-common';
import { TaskScheduler } from '@backstage/backend-tasks';
import { Config } from '@backstage/config';
import healthcheck from './plugins/healthcheck';
import { metricsHandler, metricsInit } from './metrics';
import auth from './plugins/auth';
import catalog from './plugins/catalog';
import events from './plugins/events';
import kubernetes from './plugins/kubernetes';
import scaffolder from './plugins/scaffolder';
import proxy from './plugins/proxy';
import search from './plugins/search';
import techdocs from './plugins/techdocs';
import app from './plugins/app';
import permission from './plugins/permission';
import signals from './plugins/signals';
import devtools from './plugins/devtools';
import { PluginEnvironment } from './types';
import { ServerPermissionClient } from '@backstage/plugin-permission-node';
import { DefaultIdentityClient } from '@backstage/plugin-auth-node';
import { DefaultEventBroker } from '@backstage/plugin-events-backend';
import { DefaultEventsService } from '@backstage/plugin-events-node';
import { PrometheusExporter } from '@opentelemetry/exporter-prometheus';
import { MeterProvider } from '@opentelemetry/sdk-metrics';
import { metrics } from '@opentelemetry/api';
import { DefaultSignalsService } from '@backstage/plugin-signals-node';
const backend = createBackend();
// Expose opentelemetry metrics using a Prometheus exporter on
// http://localhost:9464/metrics . See prometheus.yml in packages/backend for
// more information on how to scrape it.
const exporter = new PrometheusExporter();
const meterProvider = new MeterProvider();
metrics.setGlobalMeterProvider(meterProvider);
meterProvider.addMetricReader(exporter);
backend.add(import('@backstage/plugin-auth-backend'));
backend.add(import('./authModuleGithubProvider'));
backend.add(import('@backstage/plugin-auth-backend-module-guest-provider'));
function makeCreateEnv(config: Config) {
const root = getRootLogger();
const reader = UrlReaders.default({ logger: root, config });
const discovery = HostDiscovery.fromConfig(config);
const tokenManager = ServerTokenManager.fromConfig(config, { logger: root });
const permissions = ServerPermissionClient.fromConfig(config, {
discovery,
tokenManager,
});
const databaseManager = DatabaseManager.fromConfig(config, { logger: root });
const cacheManager = CacheManager.fromConfig(config);
const taskScheduler = TaskScheduler.fromConfig(config, { databaseManager });
const identity = DefaultIdentityClient.create({
discovery,
});
backend.add(import('@backstage/plugin-app-backend/alpha'));
backend.add(import('@backstage/plugin-catalog-backend-module-unprocessed'));
backend.add(
import('@backstage/plugin-catalog-backend-module-scaffolder-entity-model'),
);
backend.add(import('@backstage/plugin-catalog-backend/alpha'));
backend.add(import('@backstage/plugin-devtools-backend'));
backend.add(import('@backstage/plugin-kubernetes-backend/alpha'));
backend.add(
import('@backstage/plugin-permission-backend-module-allow-all-policy'),
);
backend.add(import('@backstage/plugin-permission-backend/alpha'));
backend.add(import('@backstage/plugin-proxy-backend/alpha'));
backend.add(import('@backstage/plugin-scaffolder-backend/alpha'));
backend.add(import('@backstage/plugin-scaffolder-backend-module-github'));
backend.add(import('@backstage/plugin-search-backend-module-catalog/alpha'));
backend.add(import('@backstage/plugin-search-backend-module-explore/alpha'));
backend.add(import('@backstage/plugin-search-backend-module-techdocs/alpha'));
backend.add(
import('@backstage/plugin-catalog-backend-module-backstage-openapi'),
);
backend.add(import('@backstage/plugin-search-backend/alpha'));
backend.add(import('@backstage/plugin-techdocs-backend/alpha'));
backend.add(import('@backstage/plugin-signals-backend'));
backend.add(import('@backstage/plugin-notifications-backend'));
const eventsService = DefaultEventsService.create({ logger: root });
const eventBroker = new DefaultEventBroker(
root.child({ type: 'plugin' }),
eventsService,
);
const signalsService = DefaultSignalsService.create({
events: eventsService,
});
root.info(`Created UrlReader ${reader}`);
return (plugin: string): PluginEnvironment => {
const logger = root.child({ type: 'plugin', plugin });
const database = databaseManager.forPlugin(plugin);
const cache = cacheManager.forPlugin(plugin);
const scheduler = taskScheduler.forPlugin(plugin);
return {
logger,
cache,
database,
config,
reader,
eventBroker,
events: eventsService,
discovery,
tokenManager,
permissions,
scheduler,
identity,
signals: signalsService,
};
};
}
async function main() {
metricsInit();
const logger = getRootLogger();
logger.info(
`You are running an example backend, which is supposed to be mainly used for contributing back to Backstage. ` +
`Do NOT deploy this to production. Read more here https://backstage.io/docs/getting-started/`,
);
const config = await loadBackendConfig({
argv: process.argv,
logger,
});
const createEnv = makeCreateEnv(config);
const healthcheckEnv = useHotMemoize(module, () => createEnv('healthcheck'));
const catalogEnv = useHotMemoize(module, () => createEnv('catalog'));
const scaffolderEnv = useHotMemoize(module, () => createEnv('scaffolder'));
const authEnv = useHotMemoize(module, () => createEnv('auth'));
const proxyEnv = useHotMemoize(module, () => createEnv('proxy'));
const searchEnv = useHotMemoize(module, () => createEnv('search'));
const techdocsEnv = useHotMemoize(module, () => createEnv('techdocs'));
const kubernetesEnv = useHotMemoize(module, () => createEnv('kubernetes'));
const appEnv = useHotMemoize(module, () => createEnv('app'));
const permissionEnv = useHotMemoize(module, () => createEnv('permission'));
const eventsEnv = useHotMemoize(module, () => createEnv('events'));
const devToolsEnv = useHotMemoize(module, () => createEnv('devtools'));
const signalsEnv = useHotMemoize(module, () => createEnv('signals'));
const apiRouter = Router();
apiRouter.use('/catalog', await catalog(catalogEnv));
apiRouter.use('/events', await events(eventsEnv));
apiRouter.use('/scaffolder', await scaffolder(scaffolderEnv));
apiRouter.use('/auth', await auth(authEnv));
apiRouter.use('/search', await search(searchEnv));
apiRouter.use('/techdocs', await techdocs(techdocsEnv));
apiRouter.use('/kubernetes', await kubernetes(kubernetesEnv));
apiRouter.use('/proxy', await proxy(proxyEnv));
apiRouter.use('/permission', await permission(permissionEnv));
apiRouter.use('/devtools', await devtools(devToolsEnv));
apiRouter.use('/signals', await signals(signalsEnv));
apiRouter.use(notFoundHandler());
const service = createServiceBuilder(module)
.loadConfig(config)
.addRouter('', await healthcheck(healthcheckEnv))
.addRouter('', metricsHandler())
.addRouter('/api', apiRouter)
.addRouter('', await app(appEnv));
await service.start().catch(err => {
logger.error(err);
process.exit(1);
});
}
module.hot?.accept();
main().catch(error => {
console.error('Backend failed to start up', error);
process.exit(1);
});
backend.start();
-63
View File
@@ -1,63 +0,0 @@
/*
* Copyright 2021 The Backstage Authors
*
* 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 { useHotCleanup } from '@backstage/backend-common';
import { RequestHandler, Request } from 'express';
import promBundle from 'express-prom-bundle';
import prom from 'prom-client';
import * as url from 'url';
/**
* Experimental Prometheus metrics used to benchmark the performance of the
* software catalog. Use this at your own risk.
*/
const rootRegEx = new RegExp('^/([^/]*)/.*');
const apiRegEx = new RegExp('^/api/([^/]*)/.*');
function normalizePath(req: Request): string {
const path = url.parse(req.originalUrl || req.url).pathname || '/';
// Capture /api/ and the plugin name
if (apiRegEx.test(path)) {
return path.replace(apiRegEx, '/api/$1');
}
// Only the first path segment at root level
return path.replace(rootRegEx, '/$1');
}
export function metricsInit(): void {
prom.collectDefaultMetrics({ prefix: 'backstage_' });
}
/**
* Adds a /metrics endpoint, register default runtime metrics and instrument the router.
*/
export function metricsHandler(): RequestHandler {
// We can only initialize the metrics once and have to clean them up between hot reloads
useHotCleanup(module, () => prom.register.clear());
return promBundle({
includeMethod: true,
includePath: true,
// Using includePath alone is problematic, as it will include path labels with high
// cardinality (e.g. path params). Instead we would have to template them. However, this
// is difficult, as every backend plugin might use different routes. Instead we only take
// the first directory of the path, to have at least an idea how each plugin performs:
normalizePath,
promClient: { collectDefaultMetrics: {} },
});
}
@@ -1,60 +0,0 @@
/*
* Copyright 2020 The Backstage Authors
*
* 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 {
EntityProvider,
EntityProviderConnection,
} from '@backstage/plugin-catalog-node';
import { EventParams, EventsService } from '@backstage/plugin-events-node';
import { Logger } from 'winston';
export class DemoEventBasedEntityProvider implements EntityProvider {
private readonly logger: Logger;
private readonly events: EventsService;
private readonly topics: string[];
constructor(opts: {
events: EventsService;
logger: Logger;
topics: string[];
}) {
this.events = opts.events;
this.logger = opts.logger;
this.topics = opts.topics;
}
async subscribe() {
await this.events.subscribe({
id: 'DemoEventBasedEntityProvider',
topics: this.topics,
onEvent: async (params: EventParams): Promise<void> => {
this.logger.info(
`onEvent: topic=${params.topic}, metadata=${JSON.stringify(
params.metadata,
)}, payload=${JSON.stringify(params.eventPayload)}`,
);
},
});
}
async connect(_: EntityProviderConnection): Promise<void> {
// not doing anything here
}
getProviderName(): string {
return DemoEventBasedEntityProvider.name;
}
}
-30
View File
@@ -1,30 +0,0 @@
/*
* Copyright 2020 The Backstage Authors
*
* 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 '@backstage/plugin-app-backend';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
return await createRouter({
logger: env.logger,
config: env.config,
database: env.database,
appPackageName: 'example-app',
});
}
-146
View File
@@ -1,146 +0,0 @@
/*
* Copyright 2020 The Backstage Authors
*
* 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 {
DEFAULT_NAMESPACE,
stringifyEntityRef,
} from '@backstage/catalog-model';
import {
createRouter,
providers,
defaultAuthProviderFactories,
} from '@backstage/plugin-auth-backend';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
return await createRouter({
logger: env.logger,
config: env.config,
database: env.database,
discovery: env.discovery,
tokenManager: env.tokenManager,
providerFactories: {
...defaultAuthProviderFactories,
// NOTE: DO NOT add this many resolvers in your own instance!
// It is important that each real user always gets resolved to
// the same sign-in identity. The code below will not do that.
// It is here for demo purposes only.
github: providers.github.create({
signIn: {
async resolver({ result: { fullProfile } }, ctx) {
const userId = fullProfile.username;
if (!userId) {
throw new Error(
`GitHub user profile does not contain a username`,
);
}
const userEntityRef = stringifyEntityRef({
kind: 'User',
name: userId,
namespace: DEFAULT_NAMESPACE,
});
return ctx.issueToken({
claims: {
sub: userEntityRef,
ent: [userEntityRef],
},
});
},
},
}),
gitlab: providers.gitlab.create({
signIn: {
async resolver({ result: { fullProfile } }, ctx) {
return ctx.signInWithCatalogUser({
entityRef: {
name: fullProfile.id,
},
});
},
},
}),
microsoft: providers.microsoft.create({
signIn: {
resolver:
providers.microsoft.resolvers.emailMatchingUserEntityAnnotation(),
},
}),
google: providers.google.create({
signIn: {
resolver:
providers.google.resolvers.emailLocalPartMatchingUserEntityName(),
},
}),
okta: providers.okta.create({
signIn: {
resolver:
providers.okta.resolvers.emailMatchingUserEntityAnnotation(),
},
}),
bitbucket: providers.bitbucket.create({
signIn: {
resolver:
providers.bitbucket.resolvers.usernameMatchingUserEntityAnnotation(),
},
}),
onelogin: providers.onelogin.create({
signIn: {
async resolver({ result: { fullProfile } }, ctx) {
return ctx.signInWithCatalogUser({
entityRef: {
name: fullProfile.id,
},
});
},
},
}),
bitbucketServer: providers.bitbucketServer.create({
signIn: {
resolver:
providers.bitbucketServer.resolvers.emailMatchingUserEntityProfileEmail(),
},
}),
// This is an example of how to configure the OAuth2Proxy provider as well
// as how to sign a user in without a matching user entity in the catalog.
// You can try it out using `<ProxiedSignInPage {...props} provider="myproxy" />`
myproxy: providers.oauth2Proxy.create({
signIn: {
async resolver({ result }, ctx) {
const entityRef = stringifyEntityRef({
kind: 'user',
namespace: DEFAULT_NAMESPACE,
name: result.getHeader('x-forwarded-user')!,
});
return ctx.issueToken({
claims: {
sub: entityRef,
ent: [entityRef],
},
});
},
},
}),
},
});
}
-51
View File
@@ -1,51 +0,0 @@
/*
* Copyright 2020 The Backstage Authors
*
* 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 { CatalogBuilder } from '@backstage/plugin-catalog-backend';
import { ScaffolderEntitiesProcessor } from '@backstage/plugin-catalog-backend-module-scaffolder-entity-model';
import { UnprocessedEntitiesModule } from '@backstage/plugin-catalog-backend-module-unprocessed';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
import { DemoEventBasedEntityProvider } from './DemoEventBasedEntityProvider';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
const builder = CatalogBuilder.create(env);
builder.addProcessor(new ScaffolderEntitiesProcessor());
const demoProvider = new DemoEventBasedEntityProvider({
events: env.events,
logger: env.logger,
topics: ['example'],
});
await demoProvider.subscribe();
builder.addEntityProvider(demoProvider);
const { processingEngine, router } = await builder.build();
const unprocessed = UnprocessedEntitiesModule.create({
database: await env.database.getClient(),
router,
permissions: env.permissions,
discovery: env.discovery,
});
unprocessed.registerRoutes();
await processingEngine.start();
return router;
}
-30
View File
@@ -1,30 +0,0 @@
/*
* Copyright 2022 The Backstage Authors
*
* 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 '@backstage/plugin-devtools-backend';
import { Router } from 'express';
import type { PluginEnvironment } from '../types';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
return createRouter({
logger: env.logger,
config: env.config,
permissions: env.permissions,
discovery: env.discovery,
});
}
-34
View File
@@ -1,34 +0,0 @@
/*
* Copyright 2022 The Backstage Authors
*
* 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 { HttpPostIngressEventPublisher } from '@backstage/plugin-events-backend';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
const eventsRouter = Router();
const http = HttpPostIngressEventPublisher.fromConfig({
config: env.config,
events: env.events,
logger: env.logger,
});
http.bind(eventsRouter);
return eventsRouter;
}
@@ -1,28 +0,0 @@
/*
* Copyright 2020 The Backstage Authors
*
* 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 { createStatusCheckRouter } from '@backstage/backend-common';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
return await createStatusCheckRouter({
logger: env.logger,
path: '/healthcheck',
});
}
@@ -1,34 +0,0 @@
/*
* Copyright 2020 The Backstage Authors
*
* 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 { KubernetesBuilder } from '@backstage/plugin-kubernetes-backend';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
import { CatalogClient } from '@backstage/catalog-client';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
const catalogApi = new CatalogClient({ discoveryApi: env.discovery });
const { router } = await KubernetesBuilder.createBuilder({
logger: env.logger,
config: env.config,
catalogApi,
permissions: env.permissions,
discovery: env.discovery,
}).build();
return router;
}
@@ -1,53 +0,0 @@
/*
* Copyright 2021 The Backstage Authors
*
* 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 { BackstageIdentityResponse } from '@backstage/plugin-auth-node';
import { createRouter } from '@backstage/plugin-permission-backend';
import {
AuthorizeResult,
PolicyDecision,
} from '@backstage/plugin-permission-common';
import {
PermissionPolicy,
PolicyQuery,
} from '@backstage/plugin-permission-node';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
class ExamplePermissionPolicy implements PermissionPolicy {
async handle(
_request: PolicyQuery,
_user?: BackstageIdentityResponse,
): Promise<PolicyDecision> {
// some logic to determine if the user is allowed to access the resource
return {
result: AuthorizeResult.ALLOW,
};
}
}
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
return await createRouter({
config: env.config,
logger: env.logger,
discovery: env.discovery,
policy: new ExamplePermissionPolicy(),
identity: env.identity,
});
}
-29
View File
@@ -1,29 +0,0 @@
/*
* Copyright 2020 The Backstage Authors
*
* 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 '@backstage/plugin-proxy-backend';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
return await createRouter({
logger: env.logger,
config: env.config,
discovery: env.discovery,
});
}
@@ -1,63 +0,0 @@
/*
* Copyright 2020 The Backstage Authors
*
* 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 { CatalogClient } from '@backstage/catalog-client';
import {
createBuiltinActions,
createRouter,
} from '@backstage/plugin-scaffolder-backend';
import { Router } from 'express';
import type { PluginEnvironment } from '../types';
import { ScmIntegrations } from '@backstage/integration';
import { createConfluenceToMarkdownAction } from '@backstage/plugin-scaffolder-backend-module-confluence-to-markdown';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
const catalogClient = new CatalogClient({
discoveryApi: env.discovery,
});
const integrations = ScmIntegrations.fromConfig(env.config);
const builtInActions = createBuiltinActions({
integrations,
config: env.config,
catalogClient,
reader: env.reader,
});
const actions = [
...builtInActions,
createConfluenceToMarkdownAction({
integrations,
config: env.config,
reader: env.reader,
}),
];
return await createRouter({
logger: env.logger,
config: env.config,
database: env.database,
catalogClient: catalogClient,
reader: env.reader,
discovery: env.discovery,
scheduler: env.scheduler,
permissions: env.permissions,
actions,
});
}
-112
View File
@@ -1,112 +0,0 @@
/*
* Copyright 2021 The Backstage Authors
*
* 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 { useHotCleanup } from '@backstage/backend-common';
import { DefaultCatalogCollatorFactory } from '@backstage/plugin-search-backend-module-catalog';
import { ToolDocumentCollatorFactory } from '@backstage/plugin-search-backend-module-explore';
import { createRouter } from '@backstage/plugin-search-backend';
import { ElasticSearchSearchEngine } from '@backstage/plugin-search-backend-module-elasticsearch';
import { PgSearchEngine } from '@backstage/plugin-search-backend-module-pg';
import {
IndexBuilder,
SearchEngine,
LunrSearchEngine,
} from '@backstage/plugin-search-backend-node';
import { DefaultTechDocsCollatorFactory } from '@backstage/plugin-search-backend-module-techdocs';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
async function createSearchEngine(
env: PluginEnvironment,
): Promise<SearchEngine> {
if (env.config.has('search.elasticsearch')) {
return await ElasticSearchSearchEngine.fromConfig({
logger: env.logger,
config: env.config,
});
}
if (await PgSearchEngine.supported(env.database)) {
return await PgSearchEngine.fromConfig(env.config, {
database: env.database,
logger: env.logger,
});
}
return new LunrSearchEngine({ logger: env.logger });
}
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
// Initialize a connection to a search engine.
const searchEngine = await createSearchEngine(env);
const indexBuilder = new IndexBuilder({
logger: env.logger,
searchEngine,
});
const schedule = env.scheduler.createScheduledTaskRunner({
frequency: { minutes: 10 },
timeout: { minutes: 15 },
// A 3 second delay gives the backend server a chance to initialize before
// any collators are executed, which may attempt requests against the API.
initialDelay: { seconds: 3 },
});
// Collators are responsible for gathering documents known to plugins. This
// particular collator gathers entities from the software catalog.
indexBuilder.addCollator({
schedule,
factory: DefaultCatalogCollatorFactory.fromConfig(env.config, {
discovery: env.discovery,
tokenManager: env.tokenManager,
}),
});
indexBuilder.addCollator({
schedule,
factory: DefaultTechDocsCollatorFactory.fromConfig(env.config, {
discovery: env.discovery,
logger: env.logger,
tokenManager: env.tokenManager,
}),
});
indexBuilder.addCollator({
schedule,
factory: ToolDocumentCollatorFactory.fromConfig(env.config, {
discovery: env.discovery,
logger: env.logger,
}),
});
// The scheduler controls when documents are gathered from collators and sent
// to the search engine for indexing.
const { scheduler } = await indexBuilder.build();
scheduler.start();
useHotCleanup(module, () => scheduler.stop());
return await createRouter({
engine: indexBuilder.getSearchEngine(),
types: indexBuilder.getDocumentTypes(),
discovery: env.discovery,
permissions: env.permissions,
config: env.config,
logger: env.logger,
});
}
-29
View File
@@ -1,29 +0,0 @@
/*
* Copyright 2023 The Backstage Authors
*
* 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 { Router } from 'express';
import { createRouter } from '@backstage/plugin-signals-backend';
import { PluginEnvironment } from '../types';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
return await createRouter({
logger: env.logger,
events: env.events,
identity: env.identity,
discovery: env.discovery,
});
}
-67
View File
@@ -1,67 +0,0 @@
/*
* Copyright 2020 The Backstage Authors
*
* 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 { DockerContainerRunner } from '@backstage/backend-common';
import {
createRouter,
Generators,
Preparers,
Publisher,
} from '@backstage/plugin-techdocs-backend';
import Docker from 'dockerode';
import { Router } from 'express';
import { PluginEnvironment } from '../types';
export default async function createPlugin(
env: PluginEnvironment,
): Promise<Router> {
// Preparers are responsible for fetching source files for documentation.
const preparers = await Preparers.fromConfig(env.config, {
logger: env.logger,
reader: env.reader,
});
// Docker client (conditionally) used by the generators, based on techdocs.generators config.
const dockerClient = new Docker();
const containerRunner = new DockerContainerRunner({ dockerClient });
// Generators are used for generating documentation sites.
const generators = await Generators.fromConfig(env.config, {
logger: env.logger,
containerRunner,
});
// Publisher is used for
// 1. Publishing generated files to storage
// 2. Fetching files from storage and passing them to TechDocs frontend.
const publisher = await Publisher.fromConfig(env.config, {
logger: env.logger,
discovery: env.discovery,
});
// checks if the publisher is working and logs the result
await publisher.getReadiness();
return await createRouter({
preparers,
generators,
publisher,
logger: env.logger,
config: env.config,
discovery: env.discovery,
cache: env.cache,
});
}
-49
View File
@@ -1,49 +0,0 @@
/*
* Copyright 2020 The Backstage Authors
*
* 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 { Logger } from 'winston';
import { Config } from '@backstage/config';
import {
PluginCacheManager,
PluginDatabaseManager,
PluginEndpointDiscovery,
TokenManager,
UrlReader,
} from '@backstage/backend-common';
import { PluginTaskScheduler } from '@backstage/backend-tasks';
import { IdentityApi } from '@backstage/plugin-auth-node';
import { PermissionEvaluator } from '@backstage/plugin-permission-common';
import { EventBroker, EventsService } from '@backstage/plugin-events-node';
import { SignalsService } from '@backstage/plugin-signals-node';
export type PluginEnvironment = {
logger: Logger;
cache: PluginCacheManager;
database: PluginDatabaseManager;
config: Config;
reader: UrlReader;
discovery: PluginEndpointDiscovery;
tokenManager: TokenManager;
permissions: PermissionEvaluator;
scheduler: PluginTaskScheduler;
identity: IdentityApi;
/**
* @deprecated use `events` instead
*/
eventBroker: EventBroker;
events: EventsService;
signals: SignalsService;
};