Update Backend Dynamic Feature Service to provide the router for frontend plugins
Signed-off-by: David Festal <dfestal@redhat.com>
This commit is contained in:
@@ -38,6 +38,9 @@
|
||||
"scripts": {
|
||||
"build": "backstage-cli package build",
|
||||
"clean": "backstage-cli package clean",
|
||||
"diff": "backstage-repo-tools package schema openapi diff",
|
||||
"fuzz": "backstage-repo-tools package schema openapi fuzz --exclude-checks response_schema_conformance",
|
||||
"generate": "backstage-repo-tools package schema openapi generate --server --client-package packages/frontend-dynamic-feature-loader",
|
||||
"lint": "backstage-cli package lint",
|
||||
"prepack": "backstage-cli package prepack",
|
||||
"postpack": "backstage-cli package postpack",
|
||||
@@ -46,6 +49,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/backend-defaults": "workspace:^",
|
||||
"@backstage/backend-openapi-utils": "workspace:^",
|
||||
"@backstage/backend-plugin-api": "workspace:^",
|
||||
"@backstage/cli-common": "workspace:^",
|
||||
"@backstage/cli-node": "workspace:^",
|
||||
@@ -64,9 +68,11 @@
|
||||
"@backstage/plugin-search-common": "workspace:^",
|
||||
"@backstage/types": "workspace:^",
|
||||
"@manypkg/get-packages": "^1.1.3",
|
||||
"@module-federation/sdk": "^0.9.0",
|
||||
"@types/express": "^4.17.6",
|
||||
"chokidar": "^3.5.3",
|
||||
"express": "^4.17.1",
|
||||
"express-promise-router": "^4.1.0",
|
||||
"fs-extra": "^11.2.0",
|
||||
"lodash": "^4.17.21",
|
||||
"winston": "^3.2.1"
|
||||
@@ -76,7 +82,9 @@
|
||||
"@backstage/backend-test-utils": "workspace:^",
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@backstage/plugin-app-backend": "workspace:^",
|
||||
"@backstage/repo-tools": "workspace:^",
|
||||
"triple-beam": "^1.4.1",
|
||||
"wait-for-expect": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"configSchema": "config.d.ts"
|
||||
}
|
||||
|
||||
+2
-21
@@ -31,27 +31,8 @@ const testPlugin = backendPluginApi.createBackendPlugin({
|
||||
metadata,
|
||||
}) {
|
||||
logger.info("This secret value should be hidden by the dynamic-plugin-aware logger: AVerySecretValue");
|
||||
const externalBaseUrl = await discovery.getExternalBaseUrl(metadata.getId());
|
||||
const router = express.Router();
|
||||
const frontendPluginsIndexPath = privateDep.frontendPluginsIndexPath;
|
||||
const frontendPluginManifests = Object.fromEntries(dynamicPlugins.frontendPlugins().map(fp => {
|
||||
const pluginScannedPackage = dynamicPlugins.getScannedPackage(fp);
|
||||
const pkgDistLocation = path.resolve(
|
||||
url.fileURLToPath(pluginScannedPackage.location),
|
||||
'dist',
|
||||
);
|
||||
router.use(`/${frontendPluginsIndexPath}/${fp.name}`, express.static(pkgDistLocation))
|
||||
return [fp.name, `${externalBaseUrl}/${frontendPluginsIndexPath}/${fp.name}/mf-manifest.json`]
|
||||
}));
|
||||
router.get(`/${frontendPluginsIndexPath}`, (req, res) => {
|
||||
res.status(200).json(frontendPluginManifests);
|
||||
});
|
||||
http.use(router);
|
||||
http.addAuthPolicy({
|
||||
path: `/`,
|
||||
allow: 'unauthenticated',
|
||||
});
|
||||
|
||||
const messageFromPrivateDep = privateDep.message;
|
||||
logger.info(messageFromPrivateDep);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+2
-2
@@ -2,6 +2,6 @@
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
const frontendPluginsIndexPath = 'frontend-plugins';
|
||||
const message = 'This message comes from a plugin private dependency';
|
||||
|
||||
exports.frontendPluginsIndexPath = frontendPluginsIndexPath;
|
||||
exports.message = message;
|
||||
|
||||
@@ -389,15 +389,15 @@ Require stack:
|
||||
});
|
||||
|
||||
const list = await fetch(
|
||||
`http://localhost:${server.port()}/api/test/frontend-plugins`,
|
||||
`http://localhost:${server.port()}/api/core.dynamicplugins.frontendRemotes/manifests`,
|
||||
);
|
||||
expect(list.ok).toBe(true);
|
||||
expect(await list.json()).toEqual({
|
||||
'plugin-test-dynamic': `http://localhost:${server.port()}/api/test/frontend-plugins/plugin-test-dynamic/mf-manifest.json`,
|
||||
'plugin-test-dynamic': `http://localhost:${server.port()}/api/core.dynamicplugins.frontendRemotes/remotes/plugin-test-dynamic/mf-manifest.json`,
|
||||
});
|
||||
|
||||
const manifest = await fetch(
|
||||
`http://localhost:${server.port()}/api/test/frontend-plugins/plugin-test-dynamic/mf-manifest.json`,
|
||||
`http://localhost:${server.port()}/api/core.dynamicplugins.frontendRemotes/remotes/plugin-test-dynamic/mf-manifest.json`,
|
||||
);
|
||||
expect(manifest.ok).toBe(true);
|
||||
expect(await manifest.json()).toMatchObject({
|
||||
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
dynamicPluginsRootLoggerServiceFactory,
|
||||
dynamicPluginsSchemasServiceFactory,
|
||||
} from '../schemas';
|
||||
import frontendRemotesServerPlugin from '../server';
|
||||
|
||||
/**
|
||||
* @public
|
||||
@@ -64,6 +65,7 @@ const dynamicPluginsFeatureLoaderWithOptions = (
|
||||
yield* [
|
||||
dynamicPluginsRootLoggerServiceFactory(rootLoggerOptions),
|
||||
dynamicPluginsFrontendSchemas,
|
||||
frontendRemotesServerPlugin,
|
||||
dynamicPluginsFeatureDiscoveryLoader,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: core.dynamicplugins.frontendRemotes
|
||||
version: '1'
|
||||
description: The Backstage backend plugin that serves the frontend plugins module federation manifests and assets
|
||||
license:
|
||||
name: Apache-2.0
|
||||
url: http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
contact: {}
|
||||
servers:
|
||||
- url: /
|
||||
components:
|
||||
examples: {}
|
||||
headers: {}
|
||||
parameters: {}
|
||||
requestBodies: {}
|
||||
responses:
|
||||
ErrorResponse:
|
||||
description: An error response from the backend.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/Error'
|
||||
schemas:
|
||||
Error:
|
||||
type: object
|
||||
properties:
|
||||
error:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
stack:
|
||||
type: string
|
||||
code:
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- message
|
||||
request:
|
||||
type: object
|
||||
properties:
|
||||
method:
|
||||
type: string
|
||||
url:
|
||||
type: string
|
||||
required:
|
||||
- method
|
||||
- url
|
||||
response:
|
||||
type: object
|
||||
properties:
|
||||
statusCode:
|
||||
type: number
|
||||
required:
|
||||
- statusCode
|
||||
required:
|
||||
- error
|
||||
- response
|
||||
additionalProperties: {}
|
||||
securitySchemes:
|
||||
JWT:
|
||||
type: http
|
||||
scheme: bearer
|
||||
bearerFormat: JWT
|
||||
paths:
|
||||
/manifests:
|
||||
get:
|
||||
operationId: GetManifests
|
||||
description: Get the Module Federation manifest files of dynamic frontend plugins.
|
||||
responses:
|
||||
'200':
|
||||
description: ''
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties: {}
|
||||
additionalProperties:
|
||||
type: string
|
||||
'400':
|
||||
$ref: '#/components/responses/ErrorResponse'
|
||||
default:
|
||||
$ref: '#/components/responses/ErrorResponse'
|
||||
security:
|
||||
- {}
|
||||
- JWT: []
|
||||
parameters: []
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type GetManifests = {
|
||||
response: { [key: string]: string } | Error | Error;
|
||||
};
|
||||
|
||||
/**
|
||||
* no description
|
||||
*/
|
||||
|
||||
export type EndpointMap = {
|
||||
'#get|/manifests': GetManifests;
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export * from './DefaultApi.server';
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export * from './apis';
|
||||
export * from './router';
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface ErrorError {
|
||||
name: string;
|
||||
message: string;
|
||||
stack?: string;
|
||||
code?: string;
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface ErrorRequest {
|
||||
method: string;
|
||||
url: string;
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface ErrorResponse {
|
||||
statusCode: number;
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { ErrorError } from '../models/ErrorError.model';
|
||||
import { ErrorRequest } from '../models/ErrorRequest.model';
|
||||
import { ErrorResponse } from '../models/ErrorResponse.model';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface ModelError {
|
||||
[key: string]: any;
|
||||
|
||||
error: ErrorError;
|
||||
request?: ErrorRequest;
|
||||
response: ErrorResponse;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export * from '../models/ErrorError.model';
|
||||
export * from '../models/ErrorRequest.model';
|
||||
export * from '../models/ErrorResponse.model';
|
||||
export * from '../models/ModelError.model';
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// ******************************************************************
|
||||
// * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. *
|
||||
// ******************************************************************
|
||||
import { createValidatedOpenApiRouterFromGeneratedEndpointMap } from '@backstage/backend-openapi-utils';
|
||||
import { EndpointMap } from './';
|
||||
|
||||
export const spec = {
|
||||
openapi: '3.0.3',
|
||||
info: {
|
||||
title: 'core.dynamicplugins.frontendRemotes',
|
||||
version: '1',
|
||||
description:
|
||||
'The Backstage backend plugin that serves the frontend plugins module federation manifests and assets',
|
||||
license: {
|
||||
name: 'Apache-2.0',
|
||||
url: 'http://www.apache.org/licenses/LICENSE-2.0.html',
|
||||
},
|
||||
contact: {},
|
||||
},
|
||||
servers: [
|
||||
{
|
||||
url: '/',
|
||||
},
|
||||
],
|
||||
components: {
|
||||
examples: {},
|
||||
headers: {},
|
||||
parameters: {},
|
||||
requestBodies: {},
|
||||
responses: {
|
||||
ErrorResponse: {
|
||||
description: 'An error response from the backend.',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
$ref: '#/components/schemas/Error',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
schemas: {
|
||||
Error: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
error: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string',
|
||||
},
|
||||
message: {
|
||||
type: 'string',
|
||||
},
|
||||
stack: {
|
||||
type: 'string',
|
||||
},
|
||||
code: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
required: ['name', 'message'],
|
||||
},
|
||||
request: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
method: {
|
||||
type: 'string',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
required: ['method', 'url'],
|
||||
},
|
||||
response: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
statusCode: {
|
||||
type: 'number',
|
||||
},
|
||||
},
|
||||
required: ['statusCode'],
|
||||
},
|
||||
},
|
||||
required: ['error', 'response'],
|
||||
additionalProperties: {},
|
||||
},
|
||||
},
|
||||
securitySchemes: {
|
||||
JWT: {
|
||||
type: 'http',
|
||||
scheme: 'bearer',
|
||||
bearerFormat: 'JWT',
|
||||
},
|
||||
},
|
||||
},
|
||||
paths: {
|
||||
'/manifests': {
|
||||
get: {
|
||||
operationId: 'GetManifests',
|
||||
description:
|
||||
'Get the Module Federation manifest files of dynamic frontend plugins.',
|
||||
responses: {
|
||||
'200': {
|
||||
description: '',
|
||||
content: {
|
||||
'application/json': {
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
additionalProperties: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'400': {
|
||||
$ref: '#/components/responses/ErrorResponse',
|
||||
},
|
||||
default: {
|
||||
$ref: '#/components/responses/ErrorResponse',
|
||||
},
|
||||
},
|
||||
security: [
|
||||
{},
|
||||
{
|
||||
JWT: [],
|
||||
},
|
||||
],
|
||||
parameters: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
export const createOpenApiRouter = async (
|
||||
options?: Parameters<
|
||||
typeof createValidatedOpenApiRouterFromGeneratedEndpointMap
|
||||
>['1'],
|
||||
) =>
|
||||
createValidatedOpenApiRouterFromGeneratedEndpointMap<EndpointMap>(
|
||||
spec,
|
||||
options,
|
||||
);
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export * from './generated';
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
export { frontendRemotesServerPlugin as default } from './plugin';
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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 {
|
||||
coreServices,
|
||||
createBackendPlugin,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { createRouter } from './router';
|
||||
import { dynamicPluginsServiceRef } from '@backstage/backend-dynamic-feature-service';
|
||||
|
||||
/**
|
||||
* frontendRemotesServerPlugin backend plugin
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export const frontendRemotesServerPlugin = createBackendPlugin({
|
||||
pluginId: 'core.dynamicplugins.frontendRemotes',
|
||||
register(env) {
|
||||
env.registerInit({
|
||||
deps: {
|
||||
logger: coreServices.logger,
|
||||
auth: coreServices.auth,
|
||||
httpAuth: coreServices.httpAuth,
|
||||
httpRouter: coreServices.httpRouter,
|
||||
discovery: coreServices.discovery,
|
||||
metadata: coreServices.pluginMetadata,
|
||||
dynamicPlugins: dynamicPluginsServiceRef,
|
||||
},
|
||||
async init({ logger, httpRouter, discovery, metadata, dynamicPlugins }) {
|
||||
httpRouter.use(
|
||||
await createRouter({
|
||||
logger,
|
||||
discovery,
|
||||
metadata,
|
||||
dynamicPlugins,
|
||||
}),
|
||||
);
|
||||
httpRouter.addAuthPolicy({
|
||||
path: `/`,
|
||||
allow: 'unauthenticated',
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* 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 {
|
||||
DiscoveryService,
|
||||
LoggerService,
|
||||
PluginMetadataService,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import express from 'express';
|
||||
import { createOpenApiRouter } from '../schema/openapi';
|
||||
import { DynamicPluginProvider } from '@backstage/backend-dynamic-feature-service';
|
||||
import { ManifestFileName } from '@module-federation/sdk';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as url from 'url';
|
||||
|
||||
export async function createRouter({
|
||||
logger,
|
||||
discovery,
|
||||
metadata,
|
||||
dynamicPlugins,
|
||||
}: {
|
||||
logger: LoggerService;
|
||||
discovery: DiscoveryService;
|
||||
metadata: PluginMetadataService;
|
||||
dynamicPlugins: DynamicPluginProvider;
|
||||
}): Promise<express.Router> {
|
||||
const typedRouter = await createOpenApiRouter();
|
||||
|
||||
const externalBaseUrl = await discovery.getExternalBaseUrl(metadata.getId());
|
||||
|
||||
const frontendPluginManifests: {
|
||||
[key: string]: string;
|
||||
} = {};
|
||||
|
||||
for (const plugin of dynamicPlugins.frontendPlugins()) {
|
||||
const pluginScannedPackage = dynamicPlugins.getScannedPackage(plugin);
|
||||
const pkgDistLocation = path.resolve(
|
||||
url.fileURLToPath(pluginScannedPackage.location),
|
||||
'dist',
|
||||
);
|
||||
|
||||
const pkgManifestLocation = path.resolve(pkgDistLocation, ManifestFileName);
|
||||
if (!fs.existsSync(pkgManifestLocation)) {
|
||||
logger.warn(
|
||||
`Could not find '${pkgManifestLocation}' for frontend plugin ${plugin.name}@${plugin.version}`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
let moduleName: string | undefined;
|
||||
try {
|
||||
const pkgManifest = JSON.parse(
|
||||
fs.readFileSync(pkgManifestLocation).toString(),
|
||||
);
|
||||
moduleName = pkgManifest.name;
|
||||
if (!moduleName) {
|
||||
logger.error(
|
||||
`Dynamic frontend plugin module name not found in manifest for plugin ${plugin.name}@${plugin.version}`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`Dynamic frontend plugin manifest could not be loaded for plugin ${plugin.name}@${plugin.version}`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const remoteAssetsPrefix = `/remotes/${plugin.name}`;
|
||||
typedRouter.use(remoteAssetsPrefix, express.static(pkgDistLocation));
|
||||
|
||||
logger.info(
|
||||
`Exposed dynamic frontend plugin '${plugin.name}' from '${pluginScannedPackage.location}' `,
|
||||
);
|
||||
|
||||
frontendPluginManifests[
|
||||
plugin.name
|
||||
] = `${externalBaseUrl}/remotes/${plugin.name}/${ManifestFileName}`;
|
||||
}
|
||||
|
||||
typedRouter.get('/manifests', (_, res) => {
|
||||
res.status(200).json(frontendPluginManifests);
|
||||
});
|
||||
|
||||
return typedRouter;
|
||||
}
|
||||
@@ -3335,16 +3335,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.20.6, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.4.4, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.0, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2":
|
||||
version: 7.26.7
|
||||
resolution: "@babel/runtime@npm:7.26.7"
|
||||
dependencies:
|
||||
regenerator-runtime: "npm:^0.14.0"
|
||||
checksum: 10/c7a661a6836b332d9d2e047cba77ba1862c1e4f78cec7146db45808182ef7636d8a7170be9797e5d8fd513180bffb9fa16f6ca1c69341891efec56113cf22bfc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/runtime@npm:^7.26.10":
|
||||
"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.17.8, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.20.6, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.26.10, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.4.4, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.0, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2":
|
||||
version: 7.27.0
|
||||
resolution: "@babel/runtime@npm:7.27.0"
|
||||
dependencies:
|
||||
@@ -3665,6 +3656,7 @@ __metadata:
|
||||
dependencies:
|
||||
"@backstage/backend-app-api": "workspace:^"
|
||||
"@backstage/backend-defaults": "workspace:^"
|
||||
"@backstage/backend-openapi-utils": "workspace:^"
|
||||
"@backstage/backend-plugin-api": "workspace:^"
|
||||
"@backstage/backend-test-utils": "workspace:^"
|
||||
"@backstage/cli": "workspace:^"
|
||||
@@ -3684,11 +3676,14 @@ __metadata:
|
||||
"@backstage/plugin-scaffolder-node": "workspace:^"
|
||||
"@backstage/plugin-search-backend-node": "workspace:^"
|
||||
"@backstage/plugin-search-common": "workspace:^"
|
||||
"@backstage/repo-tools": "workspace:^"
|
||||
"@backstage/types": "workspace:^"
|
||||
"@manypkg/get-packages": "npm:^1.1.3"
|
||||
"@module-federation/sdk": "npm:^0.9.0"
|
||||
"@types/express": "npm:^4.17.6"
|
||||
chokidar: "npm:^3.5.3"
|
||||
express: "npm:^4.17.1"
|
||||
express-promise-router: "npm:^4.1.0"
|
||||
fs-extra: "npm:^11.2.0"
|
||||
lodash: "npm:^4.17.21"
|
||||
triple-beam: "npm:^1.4.1"
|
||||
@@ -20558,16 +20553,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/node@npm:*, @types/node@npm:>=13.7.0, @types/node@npm:^22.0.0":
|
||||
version: 22.13.9
|
||||
resolution: "@types/node@npm:22.13.9"
|
||||
dependencies:
|
||||
undici-types: "npm:~6.20.0"
|
||||
checksum: 10/23560df3ee99c907179c688754486b969a72144f2e2bdefe974d320dddc5ca8f93365842966ecbd5c5bba34e919fc1a5a6627712beb8e7f71d71347dcf414a35
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/node@npm:>=12, @types/node@npm:>=12.0.0, @types/node@npm:>=18.0.0":
|
||||
"@types/node@npm:*, @types/node@npm:>=12, @types/node@npm:>=12.0.0, @types/node@npm:>=13.7.0, @types/node@npm:>=18.0.0, @types/node@npm:^22.0.0":
|
||||
version: 22.13.10
|
||||
resolution: "@types/node@npm:22.13.10"
|
||||
dependencies:
|
||||
@@ -30842,25 +30828,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6":
|
||||
version: 1.2.6
|
||||
resolution: "get-intrinsic@npm:1.2.6"
|
||||
dependencies:
|
||||
call-bind-apply-helpers: "npm:^1.0.1"
|
||||
dunder-proto: "npm:^1.0.0"
|
||||
es-define-property: "npm:^1.0.1"
|
||||
es-errors: "npm:^1.3.0"
|
||||
es-object-atoms: "npm:^1.0.0"
|
||||
function-bind: "npm:^1.1.2"
|
||||
gopd: "npm:^1.2.0"
|
||||
has-symbols: "npm:^1.1.0"
|
||||
hasown: "npm:^2.0.2"
|
||||
math-intrinsics: "npm:^1.0.0"
|
||||
checksum: 10/a1ffae6d7893a6fa0f4d1472adbc85095edd6b3b0943ead97c3738539cecb19d422ff4d48009eed8c3c27ad678c2b1e38a83b1a1e96b691d13ed8ecefca1068d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"get-intrinsic@npm:^1.2.1":
|
||||
"get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6":
|
||||
version: 1.3.0
|
||||
resolution: "get-intrinsic@npm:1.3.0"
|
||||
dependencies:
|
||||
@@ -31924,7 +31892,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"html-entities@npm:^2.1.0, html-entities@npm:^2.4.0, html-entities@npm:^2.5.2":
|
||||
"html-entities@npm:^2.1.0, html-entities@npm:^2.5.2":
|
||||
version: 2.5.2
|
||||
resolution: "html-entities@npm:2.5.2"
|
||||
checksum: 10/4ec12ebdf2d5ba8192c68e1aef3c1e4a4f36b29246a0a88464fe278a54517d0196d3489af46a3145c7ecacb4fc5fd50497be19eb713b810acab3f0efcf36fdc2
|
||||
@@ -32125,7 +32093,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"http-proxy-middleware@npm:^2.0.0, http-proxy-middleware@npm:^2.0.3, http-proxy-middleware@npm:^2.0.6, http-proxy-middleware@npm:^2.0.7":
|
||||
"http-proxy-middleware@npm:^2.0.0, http-proxy-middleware@npm:^2.0.6, http-proxy-middleware@npm:^2.0.7":
|
||||
version: 2.0.7
|
||||
resolution: "http-proxy-middleware@npm:2.0.7"
|
||||
dependencies:
|
||||
@@ -36389,7 +36357,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"math-intrinsics@npm:^1.0.0, math-intrinsics@npm:^1.1.0":
|
||||
"math-intrinsics@npm:^1.1.0":
|
||||
version: 1.1.0
|
||||
resolution: "math-intrinsics@npm:1.1.0"
|
||||
checksum: 10/11df2eda46d092a6035479632e1ec865b8134bdfc4bd9e571a656f4191525404f13a283a515938c3a8de934dbfd9c09674d9da9fa831e6eb7e22b50b197d2edd
|
||||
@@ -47769,7 +47737,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"webpack-dev-server@npm:5.2.0":
|
||||
"webpack-dev-server@npm:5.2.0, webpack-dev-server@npm:^5.0.0":
|
||||
version: 5.2.0
|
||||
resolution: "webpack-dev-server@npm:5.2.0"
|
||||
dependencies:
|
||||
@@ -47813,51 +47781,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"webpack-dev-server@npm:^5.0.0":
|
||||
version: 5.1.0
|
||||
resolution: "webpack-dev-server@npm:5.1.0"
|
||||
dependencies:
|
||||
"@types/bonjour": "npm:^3.5.13"
|
||||
"@types/connect-history-api-fallback": "npm:^1.5.4"
|
||||
"@types/express": "npm:^4.17.21"
|
||||
"@types/serve-index": "npm:^1.9.4"
|
||||
"@types/serve-static": "npm:^1.15.5"
|
||||
"@types/sockjs": "npm:^0.3.36"
|
||||
"@types/ws": "npm:^8.5.10"
|
||||
ansi-html-community: "npm:^0.0.8"
|
||||
bonjour-service: "npm:^1.2.1"
|
||||
chokidar: "npm:^3.6.0"
|
||||
colorette: "npm:^2.0.10"
|
||||
compression: "npm:^1.7.4"
|
||||
connect-history-api-fallback: "npm:^2.0.0"
|
||||
express: "npm:^4.19.2"
|
||||
graceful-fs: "npm:^4.2.6"
|
||||
html-entities: "npm:^2.4.0"
|
||||
http-proxy-middleware: "npm:^2.0.3"
|
||||
ipaddr.js: "npm:^2.1.0"
|
||||
launch-editor: "npm:^2.6.1"
|
||||
open: "npm:^10.0.3"
|
||||
p-retry: "npm:^6.2.0"
|
||||
schema-utils: "npm:^4.2.0"
|
||||
selfsigned: "npm:^2.4.1"
|
||||
serve-index: "npm:^1.9.1"
|
||||
sockjs: "npm:^0.3.24"
|
||||
spdy: "npm:^4.0.2"
|
||||
webpack-dev-middleware: "npm:^7.4.2"
|
||||
ws: "npm:^8.18.0"
|
||||
peerDependencies:
|
||||
webpack: ^5.0.0
|
||||
peerDependenciesMeta:
|
||||
webpack:
|
||||
optional: true
|
||||
webpack-cli:
|
||||
optional: true
|
||||
bin:
|
||||
webpack-dev-server: bin/webpack-dev-server.js
|
||||
checksum: 10/f23255681cc5e2c2709b23ca7b2185aeed83b1c9912657d4512eda8685625a46d7a103a92446494a55fe2afdfab936f9bd4f037d20b52f7fdfff303e7e7199c7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"webpack-hot-middleware@npm:^2.25.1":
|
||||
version: 2.26.1
|
||||
resolution: "webpack-hot-middleware@npm:2.26.1"
|
||||
|
||||
Reference in New Issue
Block a user