@backstage/backend-defaults 0.6.0-next.2 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +74 -0
- package/config.d.ts +13 -0
- package/dist/entrypoints/httpRouter/http/createLifecycleMiddleware.cjs.js +8 -1
- package/dist/entrypoints/httpRouter/http/createLifecycleMiddleware.cjs.js.map +1 -1
- package/dist/entrypoints/httpRouter/httpRouterServiceFactory.cjs.js +4 -3
- package/dist/entrypoints/httpRouter/httpRouterServiceFactory.cjs.js.map +1 -1
- package/dist/entrypoints/rootHttpRouter/http/createHttpServer.cjs.js +5 -0
- package/dist/entrypoints/rootHttpRouter/http/createHttpServer.cjs.js.map +1 -1
- package/dist/entrypoints/rootHttpRouter/rootHttpRouterServiceFactory.cjs.js +15 -23
- package/dist/entrypoints/rootHttpRouter/rootHttpRouterServiceFactory.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/AzureBlobStorageUrlReader.cjs.js +2 -2
- package/dist/entrypoints/urlReader/lib/AzureBlobStorageUrlReader.cjs.js.map +1 -1
- package/dist/httpRouter.d.ts +1 -7
- package/dist/package.json.cjs.js +1 -1
- package/dist/rootHttpRouter.d.ts +0 -1
- package/package.json +18 -18
- package/dist/entrypoints/rootHttpRouter/createLifecycleMiddleware.cjs.js +0 -64
- package/dist/entrypoints/rootHttpRouter/createLifecycleMiddleware.cjs.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,79 @@
|
|
|
1
1
|
# @backstage/backend-defaults
|
|
2
2
|
|
|
3
|
+
## 0.6.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 2a825a6: Exclude `@backstage/backend-common` from schema collection if `@backstage/backend-defaults` is present
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @backstage/config-loader@1.9.4
|
|
10
|
+
|
|
11
|
+
## 0.6.0
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- fd5d337: Added a new `backend.health.headers` configuration that can be used to set additional headers to include in health check responses.
|
|
16
|
+
|
|
17
|
+
**BREAKING CONSUMERS**: As part of this change the `createHealthRouter` function exported from `@backstage/backend-defaults/rootHttpRouter` now requires the root config service to be passed through the `config` option.
|
|
18
|
+
|
|
19
|
+
- 3f34ea9: Throttles Bitbucket Server API calls
|
|
20
|
+
- de6f280: **BREAKING** Upgraded @keyv/redis and keyv packages to resolve a bug related to incorrect resolution of cache keys.
|
|
21
|
+
|
|
22
|
+
This is a breaking change for clients using the `redis` store for cache with `useRedisSets` option set to false since cache keys will be calculated differently (without the sets:namespace: prefix). For clients with default configuration (or useRedisSets set to false) the cache keys will stay the same, but since @keyv/redis library no longer supports redis sets they won't be utilised anymore.
|
|
23
|
+
|
|
24
|
+
If you were using `useRedisSets` option in configuration make sure to remove it from `app-config.yaml`:
|
|
25
|
+
|
|
26
|
+
```diff
|
|
27
|
+
backend:
|
|
28
|
+
cache:
|
|
29
|
+
store: redis
|
|
30
|
+
connection: redis://user:pass@cache.example.com:6379
|
|
31
|
+
- useRedisSets: false
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
- 29180ec: **BREAKING PRODUCERS**: The `LifecycleMiddlewareOptions.startupRequestPauseTimeout` has been removed. Use the `backend.lifecycle.startupRequestPauseTimeout` setting in your `app-config.yaml` file to customize how the `createLifecycleMiddleware` function should behave. Also the root config service is required as an option when calling the `createLifecycleMiddleware` function:
|
|
35
|
+
|
|
36
|
+
```diff
|
|
37
|
+
- createLifecycleMiddleware({ lifecycle, startupRequestPauseTimeout })
|
|
38
|
+
+ createLifecycleMiddleware({ config, lifecycle })
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
- 277092a: Implemented `AzureBlobStorageUrlReader` to read from the url of committed location from the entity provider
|
|
42
|
+
- 18a2c00: All middleware used by the default `coreServices.http` is now exported for use by custom implementations.
|
|
43
|
+
|
|
44
|
+
### Patch Changes
|
|
45
|
+
|
|
46
|
+
- dfc8b41: Updated dependency `@opentelemetry/api` to `^1.9.0`.
|
|
47
|
+
- 5b1e68c: Immediately close all connections when shutting down in local development.
|
|
48
|
+
- 8863b38: Export `PluginTokenHandler` and `pluginTokenHandlerDecoratorServiceRef` to allow for custom decoration of the plugin token handler without having to re-implement the entire handler.
|
|
49
|
+
- 29180ec: Fix server response time by moving the lifecycle startup hooks back to the plugin lifecycle service.
|
|
50
|
+
- 57e0b11: The user and plugin token verification in the default `AuthService` implementation will no longer forward verification errors to the caller, and instead log them as warnings.
|
|
51
|
+
- 97c6837: Export `DefaultHttpAuthService` to allow for custom token extraction logic.
|
|
52
|
+
- e5255f1: Log request and response metadata so it can be used for filtering log messages.
|
|
53
|
+
The format of the request date was also changed from `clf` to `utc`.
|
|
54
|
+
- 57e0b11: The default `authServiceFactory` now correctly depends on the plugin scoped `Logger` services rather than the root scoped one.
|
|
55
|
+
- fe87fbf: Add task metrics as two gauges that track the last start and end timestamps as epoch seconds.
|
|
56
|
+
- 1ac6b72: Support `connection.type: cloudsql` in database client for usage with `@google-cloud/cloud-sql-connector` and `iam` auth
|
|
57
|
+
- 0e9c9fa: Implements the `DefaultRootLifecycleService.addBeforeShutdownHook` method, and updates `DefaultRootHttpRouterService` and `DefaultRootHealthService` to listen to that event to stop accepting traffic and close service connections.
|
|
58
|
+
- d0cbd82: Remove use of the `stoppable` library on the `DefaultRootHttpRouterService` as Node's native http server [close](https://nodejs.org/api/http.html#serverclosecallback) method already drains requests.
|
|
59
|
+
- 5c9cc05: Use native fetch instead of node-fetch
|
|
60
|
+
- cf627c6: Fixed an issue in the WinstonLogger where Errors thrown and given to logger.error with field values that could not be cast to a string would throw a TypeError
|
|
61
|
+
- Updated dependencies
|
|
62
|
+
- @backstage/integration@1.16.0
|
|
63
|
+
- @backstage/plugin-auth-node@0.5.5
|
|
64
|
+
- @backstage/backend-plugin-api@1.1.0
|
|
65
|
+
- @backstage/backend-app-api@1.1.0
|
|
66
|
+
- @backstage/plugin-events-node@0.4.6
|
|
67
|
+
- @backstage/cli-node@0.2.11
|
|
68
|
+
- @backstage/plugin-permission-node@0.8.6
|
|
69
|
+
- @backstage/config-loader@1.9.3
|
|
70
|
+
- @backstage/errors@1.2.6
|
|
71
|
+
- @backstage/backend-dev-utils@0.1.5
|
|
72
|
+
- @backstage/cli-common@0.1.15
|
|
73
|
+
- @backstage/config@1.3.1
|
|
74
|
+
- @backstage/integration-aws-node@0.1.14
|
|
75
|
+
- @backstage/types@1.2.0
|
|
76
|
+
|
|
3
77
|
## 0.6.0-next.2
|
|
4
78
|
|
|
5
79
|
### Minor Changes
|
package/config.d.ts
CHANGED
|
@@ -86,6 +86,19 @@ export interface Config {
|
|
|
86
86
|
* Options used by the default auth, httpAuth and userInfo services.
|
|
87
87
|
*/
|
|
88
88
|
auth?: {
|
|
89
|
+
/**
|
|
90
|
+
* Keys shared by all backends for signing and validating backend tokens.
|
|
91
|
+
* @deprecated this will be removed when the backwards compatibility is no longer needed with backend-common
|
|
92
|
+
*/
|
|
93
|
+
keys?: {
|
|
94
|
+
/**
|
|
95
|
+
* Secret for generating tokens. Should be a base64 string, recommended
|
|
96
|
+
* length is 24 bytes.
|
|
97
|
+
*
|
|
98
|
+
* @visibility secret
|
|
99
|
+
*/
|
|
100
|
+
secret: string;
|
|
101
|
+
}[];
|
|
89
102
|
/**
|
|
90
103
|
* This disables the otherwise default auth policy, which requires all
|
|
91
104
|
* requests to be authenticated with either user or service credentials.
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var config = require('@backstage/config');
|
|
3
4
|
var errors = require('@backstage/errors');
|
|
4
5
|
var types = require('@backstage/types');
|
|
5
6
|
|
|
6
7
|
const DEFAULT_TIMEOUT = { seconds: 5 };
|
|
7
8
|
function createLifecycleMiddleware(options) {
|
|
8
|
-
const {
|
|
9
|
+
const { config: config$1, lifecycle } = options;
|
|
9
10
|
let state = "init";
|
|
10
11
|
const waiting = /* @__PURE__ */ new Set();
|
|
11
12
|
lifecycle.addStartupHook(async () => {
|
|
@@ -26,6 +27,12 @@ function createLifecycleMiddleware(options) {
|
|
|
26
27
|
}
|
|
27
28
|
waiting.clear();
|
|
28
29
|
});
|
|
30
|
+
let startupRequestPauseTimeout = DEFAULT_TIMEOUT;
|
|
31
|
+
if (config$1.has("backend.lifecycle.startupRequestPauseTimeout")) {
|
|
32
|
+
startupRequestPauseTimeout = config.readDurationFromConfig(config$1, {
|
|
33
|
+
key: "backend.lifecycle.startupRequestPauseTimeout"
|
|
34
|
+
});
|
|
35
|
+
}
|
|
29
36
|
const timeoutMs = types.durationToMilliseconds(startupRequestPauseTimeout);
|
|
30
37
|
return (_req, _res, next) => {
|
|
31
38
|
if (state === "up") {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createLifecycleMiddleware.cjs.js","sources":["../../../../src/entrypoints/httpRouter/http/createLifecycleMiddleware.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {
|
|
1
|
+
{"version":3,"file":"createLifecycleMiddleware.cjs.js","sources":["../../../../src/entrypoints/httpRouter/http/createLifecycleMiddleware.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n RootConfigService,\n LifecycleService,\n} from '@backstage/backend-plugin-api';\nimport { readDurationFromConfig } from '@backstage/config';\nimport { ServiceUnavailableError } from '@backstage/errors';\nimport { HumanDuration, durationToMilliseconds } from '@backstage/types';\nimport { RequestHandler } from 'express';\n\nexport const DEFAULT_TIMEOUT = { seconds: 5 };\n\n/**\n * Options for {@link createLifecycleMiddleware}.\n * @public\n */\nexport interface LifecycleMiddlewareOptions {\n config: RootConfigService;\n lifecycle: LifecycleService;\n}\n\n/**\n * Creates a middleware that pauses requests until the service has started.\n *\n * @remarks\n *\n * Requests that arrive before the service has started will be paused until startup is complete.\n * If the service does not start within the provided timeout, the request will be rejected with a\n * {@link @backstage/errors#ServiceUnavailableError}.\n *\n * If the service is shutting down, all requests will be rejected with a\n * {@link @backstage/errors#ServiceUnavailableError}.\n *\n * @public\n */\nexport function createLifecycleMiddleware(\n options: LifecycleMiddlewareOptions,\n): RequestHandler {\n const { config, lifecycle } = options;\n\n let state: 'init' | 'up' | 'down' = 'init';\n const waiting = new Set<{\n next: (err?: Error) => void;\n timeout: NodeJS.Timeout;\n }>();\n\n lifecycle.addStartupHook(async () => {\n if (state === 'init') {\n state = 'up';\n for (const item of waiting) {\n clearTimeout(item.timeout);\n item.next();\n }\n waiting.clear();\n }\n });\n\n lifecycle.addShutdownHook(async () => {\n state = 'down';\n\n for (const item of waiting) {\n clearTimeout(item.timeout);\n item.next(new ServiceUnavailableError('Service is shutting down'));\n }\n waiting.clear();\n });\n\n let startupRequestPauseTimeout: HumanDuration = DEFAULT_TIMEOUT;\n\n if (config.has('backend.lifecycle.startupRequestPauseTimeout')) {\n startupRequestPauseTimeout = readDurationFromConfig(config, {\n key: 'backend.lifecycle.startupRequestPauseTimeout',\n });\n }\n\n const timeoutMs = durationToMilliseconds(startupRequestPauseTimeout);\n\n return (_req, _res, next) => {\n if (state === 'up') {\n next();\n return;\n } else if (state === 'down') {\n next(new ServiceUnavailableError('Service is shutting down'));\n return;\n }\n\n const item = {\n next,\n timeout: setTimeout(() => {\n if (waiting.delete(item)) {\n next(new ServiceUnavailableError('Service has not started up yet'));\n }\n }, timeoutMs),\n };\n\n waiting.add(item);\n };\n}\n"],"names":["config","ServiceUnavailableError","readDurationFromConfig","durationToMilliseconds"],"mappings":";;;;;;AAyBa,MAAA,eAAA,GAAkB,EAAE,OAAA,EAAS,CAAE;AAyBrC,SAAS,0BACd,OACgB,EAAA;AAChB,EAAM,MAAA,UAAEA,QAAQ,EAAA,SAAA,EAAc,GAAA,OAAA;AAE9B,EAAA,IAAI,KAAgC,GAAA,MAAA;AACpC,EAAM,MAAA,OAAA,uBAAc,GAGjB,EAAA;AAEH,EAAA,SAAA,CAAU,eAAe,YAAY;AACnC,IAAA,IAAI,UAAU,MAAQ,EAAA;AACpB,MAAQ,KAAA,GAAA,IAAA;AACR,MAAA,KAAA,MAAW,QAAQ,OAAS,EAAA;AAC1B,QAAA,YAAA,CAAa,KAAK,OAAO,CAAA;AACzB,QAAA,IAAA,CAAK,IAAK,EAAA;AAAA;AAEZ,MAAA,OAAA,CAAQ,KAAM,EAAA;AAAA;AAChB,GACD,CAAA;AAED,EAAA,SAAA,CAAU,gBAAgB,YAAY;AACpC,IAAQ,KAAA,GAAA,MAAA;AAER,IAAA,KAAA,MAAW,QAAQ,OAAS,EAAA;AAC1B,MAAA,YAAA,CAAa,KAAK,OAAO,CAAA;AACzB,MAAA,IAAA,CAAK,IAAK,CAAA,IAAIC,8BAAwB,CAAA,0BAA0B,CAAC,CAAA;AAAA;AAEnE,IAAA,OAAA,CAAQ,KAAM,EAAA;AAAA,GACf,CAAA;AAED,EAAA,IAAI,0BAA4C,GAAA,eAAA;AAEhD,EAAI,IAAAD,QAAA,CAAO,GAAI,CAAA,8CAA8C,CAAG,EAAA;AAC9D,IAAA,0BAAA,GAA6BE,8BAAuBF,QAAQ,EAAA;AAAA,MAC1D,GAAK,EAAA;AAAA,KACN,CAAA;AAAA;AAGH,EAAM,MAAA,SAAA,GAAYG,6BAAuB,0BAA0B,CAAA;AAEnE,EAAO,OAAA,CAAC,IAAM,EAAA,IAAA,EAAM,IAAS,KAAA;AAC3B,IAAA,IAAI,UAAU,IAAM,EAAA;AAClB,MAAK,IAAA,EAAA;AACL,MAAA;AAAA,KACF,MAAA,IAAW,UAAU,MAAQ,EAAA;AAC3B,MAAK,IAAA,CAAA,IAAIF,8BAAwB,CAAA,0BAA0B,CAAC,CAAA;AAC5D,MAAA;AAAA;AAGF,IAAA,MAAM,IAAO,GAAA;AAAA,MACX,IAAA;AAAA,MACA,OAAA,EAAS,WAAW,MAAM;AACxB,QAAI,IAAA,OAAA,CAAQ,MAAO,CAAA,IAAI,CAAG,EAAA;AACxB,UAAK,IAAA,CAAA,IAAIA,8BAAwB,CAAA,gCAAgC,CAAC,CAAA;AAAA;AACpE,SACC,SAAS;AAAA,KACd;AAEA,IAAA,OAAA,CAAQ,IAAI,IAAI,CAAA;AAAA,GAClB;AACF;;;;;"}
|
|
@@ -4,8 +4,7 @@ var Router = require('express-promise-router');
|
|
|
4
4
|
var backendPluginApi = require('@backstage/backend-plugin-api');
|
|
5
5
|
var createAuthIntegrationRouter = require('./http/createAuthIntegrationRouter.cjs.js');
|
|
6
6
|
var createCredentialsBarrier = require('./http/createCredentialsBarrier.cjs.js');
|
|
7
|
-
require('
|
|
8
|
-
require('@backstage/types');
|
|
7
|
+
var createLifecycleMiddleware = require('./http/createLifecycleMiddleware.cjs.js');
|
|
9
8
|
var createCookieAuthRefreshMiddleware = require('./http/createCookieAuthRefreshMiddleware.cjs.js');
|
|
10
9
|
|
|
11
10
|
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
@@ -18,11 +17,12 @@ const httpRouterServiceFactory = backendPluginApi.createServiceFactory({
|
|
|
18
17
|
deps: {
|
|
19
18
|
plugin: backendPluginApi.coreServices.pluginMetadata,
|
|
20
19
|
config: backendPluginApi.coreServices.rootConfig,
|
|
20
|
+
lifecycle: backendPluginApi.coreServices.lifecycle,
|
|
21
21
|
rootHttpRouter: backendPluginApi.coreServices.rootHttpRouter,
|
|
22
22
|
auth: backendPluginApi.coreServices.auth,
|
|
23
23
|
httpAuth: backendPluginApi.coreServices.httpAuth
|
|
24
24
|
},
|
|
25
|
-
async factory({ auth, httpAuth, config, plugin, rootHttpRouter }) {
|
|
25
|
+
async factory({ auth, httpAuth, config, plugin, rootHttpRouter, lifecycle }) {
|
|
26
26
|
const router = Router__default.default();
|
|
27
27
|
rootHttpRouter.use(`/api/${plugin.getId()}`, router);
|
|
28
28
|
const credentialsBarrier = createCredentialsBarrier.createCredentialsBarrier({
|
|
@@ -30,6 +30,7 @@ const httpRouterServiceFactory = backendPluginApi.createServiceFactory({
|
|
|
30
30
|
config
|
|
31
31
|
});
|
|
32
32
|
router.use(createAuthIntegrationRouter.createAuthIntegrationRouter({ auth }));
|
|
33
|
+
router.use(createLifecycleMiddleware.createLifecycleMiddleware({ config, lifecycle }));
|
|
33
34
|
router.use(credentialsBarrier.middleware);
|
|
34
35
|
router.use(createCookieAuthRefreshMiddleware.createCookieAuthRefreshMiddleware({ auth, httpAuth }));
|
|
35
36
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"httpRouterServiceFactory.cjs.js","sources":["../../../src/entrypoints/httpRouter/httpRouterServiceFactory.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Handler } from 'express';\nimport PromiseRouter from 'express-promise-router';\nimport {\n coreServices,\n createServiceFactory,\n HttpRouterServiceAuthPolicy,\n} from '@backstage/backend-plugin-api';\nimport {\n createCookieAuthRefreshMiddleware,\n createCredentialsBarrier,\n createAuthIntegrationRouter,\n} from './http';\n\n/**\n * HTTP route registration for plugins.\n *\n * See {@link @backstage/code-plugin-api#HttpRouterService}\n * and {@link https://backstage.io/docs/backend-system/core-services/http-router | the service docs}\n * for more information.\n *\n * @public\n */\nexport const httpRouterServiceFactory = createServiceFactory({\n service: coreServices.httpRouter,\n initialization: 'always',\n deps: {\n plugin: coreServices.pluginMetadata,\n config: coreServices.rootConfig,\n rootHttpRouter: coreServices.rootHttpRouter,\n auth: coreServices.auth,\n httpAuth: coreServices.httpAuth,\n },\n async factory({ auth, httpAuth, config, plugin, rootHttpRouter }) {\n const router = PromiseRouter();\n\n rootHttpRouter.use(`/api/${plugin.getId()}`, router);\n\n const credentialsBarrier = createCredentialsBarrier({\n httpAuth,\n config,\n });\n\n router.use(createAuthIntegrationRouter({ auth }));\n router.use(credentialsBarrier.middleware);\n router.use(createCookieAuthRefreshMiddleware({ auth, httpAuth }));\n\n return {\n use(handler: Handler): void {\n router.use(handler);\n },\n addAuthPolicy(policy: HttpRouterServiceAuthPolicy): void {\n credentialsBarrier.addAuthPolicy(policy);\n },\n };\n },\n});\n"],"names":["createServiceFactory","coreServices","PromiseRouter","createCredentialsBarrier","createAuthIntegrationRouter","createCookieAuthRefreshMiddleware"],"mappings":"
|
|
1
|
+
{"version":3,"file":"httpRouterServiceFactory.cjs.js","sources":["../../../src/entrypoints/httpRouter/httpRouterServiceFactory.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Handler } from 'express';\nimport PromiseRouter from 'express-promise-router';\nimport {\n coreServices,\n createServiceFactory,\n HttpRouterServiceAuthPolicy,\n} from '@backstage/backend-plugin-api';\nimport {\n createLifecycleMiddleware,\n createCookieAuthRefreshMiddleware,\n createCredentialsBarrier,\n createAuthIntegrationRouter,\n} from './http';\n\n/**\n * HTTP route registration for plugins.\n *\n * See {@link @backstage/code-plugin-api#HttpRouterService}\n * and {@link https://backstage.io/docs/backend-system/core-services/http-router | the service docs}\n * for more information.\n *\n * @public\n */\nexport const httpRouterServiceFactory = createServiceFactory({\n service: coreServices.httpRouter,\n initialization: 'always',\n deps: {\n plugin: coreServices.pluginMetadata,\n config: coreServices.rootConfig,\n lifecycle: coreServices.lifecycle,\n rootHttpRouter: coreServices.rootHttpRouter,\n auth: coreServices.auth,\n httpAuth: coreServices.httpAuth,\n },\n async factory({ auth, httpAuth, config, plugin, rootHttpRouter, lifecycle }) {\n const router = PromiseRouter();\n\n rootHttpRouter.use(`/api/${plugin.getId()}`, router);\n\n const credentialsBarrier = createCredentialsBarrier({\n httpAuth,\n config,\n });\n\n router.use(createAuthIntegrationRouter({ auth }));\n router.use(createLifecycleMiddleware({ config, lifecycle }));\n router.use(credentialsBarrier.middleware);\n router.use(createCookieAuthRefreshMiddleware({ auth, httpAuth }));\n\n return {\n use(handler: Handler): void {\n router.use(handler);\n },\n addAuthPolicy(policy: HttpRouterServiceAuthPolicy): void {\n credentialsBarrier.addAuthPolicy(policy);\n },\n };\n },\n});\n"],"names":["createServiceFactory","coreServices","PromiseRouter","createCredentialsBarrier","createAuthIntegrationRouter","createLifecycleMiddleware","createCookieAuthRefreshMiddleware"],"mappings":";;;;;;;;;;;;;AAuCO,MAAM,2BAA2BA,qCAAqB,CAAA;AAAA,EAC3D,SAASC,6BAAa,CAAA,UAAA;AAAA,EACtB,cAAgB,EAAA,QAAA;AAAA,EAChB,IAAM,EAAA;AAAA,IACJ,QAAQA,6BAAa,CAAA,cAAA;AAAA,IACrB,QAAQA,6BAAa,CAAA,UAAA;AAAA,IACrB,WAAWA,6BAAa,CAAA,SAAA;AAAA,IACxB,gBAAgBA,6BAAa,CAAA,cAAA;AAAA,IAC7B,MAAMA,6BAAa,CAAA,IAAA;AAAA,IACnB,UAAUA,6BAAa,CAAA;AAAA,GACzB;AAAA,EACA,MAAM,QAAQ,EAAE,IAAA,EAAM,UAAU,MAAQ,EAAA,MAAA,EAAQ,cAAgB,EAAA,SAAA,EAAa,EAAA;AAC3E,IAAA,MAAM,SAASC,uBAAc,EAAA;AAE7B,IAAA,cAAA,CAAe,IAAI,CAAQ,KAAA,EAAA,MAAA,CAAO,KAAM,EAAC,IAAI,MAAM,CAAA;AAEnD,IAAA,MAAM,qBAAqBC,iDAAyB,CAAA;AAAA,MAClD,QAAA;AAAA,MACA;AAAA,KACD,CAAA;AAED,IAAA,MAAA,CAAO,GAAI,CAAAC,uDAAA,CAA4B,EAAE,IAAA,EAAM,CAAC,CAAA;AAChD,IAAA,MAAA,CAAO,IAAIC,mDAA0B,CAAA,EAAE,MAAQ,EAAA,SAAA,EAAW,CAAC,CAAA;AAC3D,IAAO,MAAA,CAAA,GAAA,CAAI,mBAAmB,UAAU,CAAA;AACxC,IAAA,MAAA,CAAO,IAAIC,mEAAkC,CAAA,EAAE,IAAM,EAAA,QAAA,EAAU,CAAC,CAAA;AAEhE,IAAO,OAAA;AAAA,MACL,IAAI,OAAwB,EAAA;AAC1B,QAAA,MAAA,CAAO,IAAI,OAAO,CAAA;AAAA,OACpB;AAAA,MACA,cAAc,MAA2C,EAAA;AACvD,QAAA,kBAAA,CAAmB,cAAc,MAAM,CAAA;AAAA;AACzC,KACF;AAAA;AAEJ,CAAC;;;;"}
|
|
@@ -45,6 +45,11 @@ async function createHttpServer(listener, options, deps) {
|
|
|
45
45
|
},
|
|
46
46
|
stop() {
|
|
47
47
|
return new Promise((resolve, reject) => {
|
|
48
|
+
if (process.env.NODE_ENV === "development") {
|
|
49
|
+
server.closeAllConnections();
|
|
50
|
+
} else {
|
|
51
|
+
server.closeIdleConnections();
|
|
52
|
+
}
|
|
48
53
|
server.close((error) => {
|
|
49
54
|
if (error) {
|
|
50
55
|
reject(error);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createHttpServer.cjs.js","sources":["../../../../src/entrypoints/rootHttpRouter/http/createHttpServer.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as http from 'http';\nimport * as https from 'https';\nimport { RequestListener } from 'http';\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport { HttpServerOptions, ExtendedHttpServer } from './types';\nimport { getGeneratedCertificate } from './getGeneratedCertificate';\n\n/**\n * Creates a Node.js HTTP or HTTPS server instance.\n *\n * @public\n */\nexport async function createHttpServer(\n listener: RequestListener,\n options: HttpServerOptions,\n deps: { logger: LoggerService },\n): Promise<ExtendedHttpServer> {\n const server = await createServer(listener, options, deps);\n return Object.assign(server, {\n start() {\n return new Promise<void>((resolve, reject) => {\n const handleStartupError = (error: Error) => {\n server.close();\n reject(error);\n };\n\n server.on('error', handleStartupError);\n\n const { host, port } = options.listen;\n server.listen(port, host, () => {\n server.off('error', handleStartupError);\n deps.logger.info(`Listening on ${host}:${port}`);\n resolve();\n });\n });\n },\n\n stop() {\n return new Promise<void>((resolve, reject) => {\n server.close(error => {\n if (error) {\n reject(error);\n } else {\n resolve();\n }\n });\n });\n },\n\n port() {\n const address = server.address();\n if (typeof address === 'string' || address === null) {\n throw new Error(`Unexpected server address '${address}'`);\n }\n return address.port;\n },\n });\n}\n\nasync function createServer(\n listener: RequestListener,\n options: HttpServerOptions,\n deps: { logger: LoggerService },\n): Promise<http.Server> {\n if (options.https) {\n const { certificate } = options.https;\n if (certificate.type === 'generated') {\n const credentials = await getGeneratedCertificate(\n certificate.hostname,\n deps.logger,\n );\n return https.createServer(credentials, listener);\n }\n return https.createServer(certificate, listener);\n }\n\n return http.createServer(listener);\n}\n"],"names":["getGeneratedCertificate","https","http"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BsB,eAAA,gBAAA,CACpB,QACA,EAAA,OAAA,EACA,IAC6B,EAAA;AAC7B,EAAA,MAAM,MAAS,GAAA,MAAM,YAAa,CAAA,QAAA,EAAU,SAAS,IAAI,CAAA;AACzD,EAAO,OAAA,MAAA,CAAO,OAAO,MAAQ,EAAA;AAAA,IAC3B,KAAQ,GAAA;AACN,MAAA,OAAO,IAAI,OAAA,CAAc,CAAC,OAAA,EAAS,MAAW,KAAA;AAC5C,QAAM,MAAA,kBAAA,GAAqB,CAAC,KAAiB,KAAA;AAC3C,UAAA,MAAA,CAAO,KAAM,EAAA;AACb,UAAA,MAAA,CAAO,KAAK,CAAA;AAAA,SACd;AAEA,QAAO,MAAA,CAAA,EAAA,CAAG,SAAS,kBAAkB,CAAA;AAErC,QAAA,MAAM,EAAE,IAAA,EAAM,IAAK,EAAA,GAAI,OAAQ,CAAA,MAAA;AAC/B,QAAO,MAAA,CAAA,MAAA,CAAO,IAAM,EAAA,IAAA,EAAM,MAAM;AAC9B,UAAO,MAAA,CAAA,GAAA,CAAI,SAAS,kBAAkB,CAAA;AACtC,UAAA,IAAA,CAAK,OAAO,IAAK,CAAA,CAAA,aAAA,EAAgB,IAAI,CAAA,CAAA,EAAI,IAAI,CAAE,CAAA,CAAA;AAC/C,UAAQ,OAAA,EAAA;AAAA,SACT,CAAA;AAAA,OACF,CAAA;AAAA,KACH;AAAA,IAEA,IAAO,GAAA;AACL,MAAA,OAAO,IAAI,OAAA,CAAc,CAAC,OAAA,EAAS,MAAW,KAAA;AAC5C,QAAA,MAAA,CAAO,MAAM,CAAS,KAAA,KAAA;AACpB,UAAA,IAAI,KAAO,EAAA;AACT,YAAA,MAAA,CAAO,KAAK,CAAA;AAAA,WACP,MAAA;AACL,YAAQ,OAAA,EAAA;AAAA;AACV,SACD,CAAA;AAAA,OACF,CAAA;AAAA,KACH;AAAA,IAEA,IAAO,GAAA;AACL,MAAM,MAAA,OAAA,GAAU,OAAO,OAAQ,EAAA;AAC/B,MAAA,IAAI,OAAO,OAAA,KAAY,QAAY,IAAA,OAAA,KAAY,IAAM,EAAA;AACnD,QAAA,MAAM,IAAI,KAAA,CAAM,CAA8B,2BAAA,EAAA,OAAO,CAAG,CAAA,CAAA,CAAA;AAAA;AAE1D,MAAA,OAAO,OAAQ,CAAA,IAAA;AAAA;AACjB,GACD,CAAA;AACH;AAEA,eAAe,YAAA,CACb,QACA,EAAA,OAAA,EACA,IACsB,EAAA;AACtB,EAAA,IAAI,QAAQ,KAAO,EAAA;AACjB,IAAM,MAAA,EAAE,WAAY,EAAA,GAAI,OAAQ,CAAA,KAAA;AAChC,IAAI,IAAA,WAAA,CAAY,SAAS,WAAa,EAAA;AACpC,MAAA,MAAM,cAAc,MAAMA,+CAAA;AAAA,QACxB,WAAY,CAAA,QAAA;AAAA,QACZ,IAAK,CAAA;AAAA,OACP;AACA,MAAO,OAAAC,gBAAA,CAAM,YAAa,CAAA,WAAA,EAAa,QAAQ,CAAA;AAAA;AAEjD,IAAO,OAAAA,gBAAA,CAAM,YAAa,CAAA,WAAA,EAAa,QAAQ,CAAA;AAAA;AAGjD,EAAO,OAAAC,eAAA,CAAK,aAAa,QAAQ,CAAA;AACnC;;;;"}
|
|
1
|
+
{"version":3,"file":"createHttpServer.cjs.js","sources":["../../../../src/entrypoints/rootHttpRouter/http/createHttpServer.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as http from 'http';\nimport * as https from 'https';\nimport { RequestListener } from 'http';\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport { HttpServerOptions, ExtendedHttpServer } from './types';\nimport { getGeneratedCertificate } from './getGeneratedCertificate';\n\n/**\n * Creates a Node.js HTTP or HTTPS server instance.\n *\n * @public\n */\nexport async function createHttpServer(\n listener: RequestListener,\n options: HttpServerOptions,\n deps: { logger: LoggerService },\n): Promise<ExtendedHttpServer> {\n const server = await createServer(listener, options, deps);\n return Object.assign(server, {\n start() {\n return new Promise<void>((resolve, reject) => {\n const handleStartupError = (error: Error) => {\n server.close();\n reject(error);\n };\n\n server.on('error', handleStartupError);\n\n const { host, port } = options.listen;\n server.listen(port, host, () => {\n server.off('error', handleStartupError);\n deps.logger.info(`Listening on ${host}:${port}`);\n resolve();\n });\n });\n },\n\n stop() {\n return new Promise<void>((resolve, reject) => {\n if (process.env.NODE_ENV === 'development') {\n // Ensure that various polling connections are shut down fast in development\n server.closeAllConnections();\n } else {\n server.closeIdleConnections();\n }\n server.close(error => {\n if (error) {\n reject(error);\n } else {\n resolve();\n }\n });\n });\n },\n\n port() {\n const address = server.address();\n if (typeof address === 'string' || address === null) {\n throw new Error(`Unexpected server address '${address}'`);\n }\n return address.port;\n },\n });\n}\n\nasync function createServer(\n listener: RequestListener,\n options: HttpServerOptions,\n deps: { logger: LoggerService },\n): Promise<http.Server> {\n if (options.https) {\n const { certificate } = options.https;\n if (certificate.type === 'generated') {\n const credentials = await getGeneratedCertificate(\n certificate.hostname,\n deps.logger,\n );\n return https.createServer(credentials, listener);\n }\n return https.createServer(certificate, listener);\n }\n\n return http.createServer(listener);\n}\n"],"names":["getGeneratedCertificate","https","http"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BsB,eAAA,gBAAA,CACpB,QACA,EAAA,OAAA,EACA,IAC6B,EAAA;AAC7B,EAAA,MAAM,MAAS,GAAA,MAAM,YAAa,CAAA,QAAA,EAAU,SAAS,IAAI,CAAA;AACzD,EAAO,OAAA,MAAA,CAAO,OAAO,MAAQ,EAAA;AAAA,IAC3B,KAAQ,GAAA;AACN,MAAA,OAAO,IAAI,OAAA,CAAc,CAAC,OAAA,EAAS,MAAW,KAAA;AAC5C,QAAM,MAAA,kBAAA,GAAqB,CAAC,KAAiB,KAAA;AAC3C,UAAA,MAAA,CAAO,KAAM,EAAA;AACb,UAAA,MAAA,CAAO,KAAK,CAAA;AAAA,SACd;AAEA,QAAO,MAAA,CAAA,EAAA,CAAG,SAAS,kBAAkB,CAAA;AAErC,QAAA,MAAM,EAAE,IAAA,EAAM,IAAK,EAAA,GAAI,OAAQ,CAAA,MAAA;AAC/B,QAAO,MAAA,CAAA,MAAA,CAAO,IAAM,EAAA,IAAA,EAAM,MAAM;AAC9B,UAAO,MAAA,CAAA,GAAA,CAAI,SAAS,kBAAkB,CAAA;AACtC,UAAA,IAAA,CAAK,OAAO,IAAK,CAAA,CAAA,aAAA,EAAgB,IAAI,CAAA,CAAA,EAAI,IAAI,CAAE,CAAA,CAAA;AAC/C,UAAQ,OAAA,EAAA;AAAA,SACT,CAAA;AAAA,OACF,CAAA;AAAA,KACH;AAAA,IAEA,IAAO,GAAA;AACL,MAAA,OAAO,IAAI,OAAA,CAAc,CAAC,OAAA,EAAS,MAAW,KAAA;AAC5C,QAAI,IAAA,OAAA,CAAQ,GAAI,CAAA,QAAA,KAAa,aAAe,EAAA;AAE1C,UAAA,MAAA,CAAO,mBAAoB,EAAA;AAAA,SACtB,MAAA;AACL,UAAA,MAAA,CAAO,oBAAqB,EAAA;AAAA;AAE9B,QAAA,MAAA,CAAO,MAAM,CAAS,KAAA,KAAA;AACpB,UAAA,IAAI,KAAO,EAAA;AACT,YAAA,MAAA,CAAO,KAAK,CAAA;AAAA,WACP,MAAA;AACL,YAAQ,OAAA,EAAA;AAAA;AACV,SACD,CAAA;AAAA,OACF,CAAA;AAAA,KACH;AAAA,IAEA,IAAO,GAAA;AACL,MAAM,MAAA,OAAA,GAAU,OAAO,OAAQ,EAAA;AAC/B,MAAA,IAAI,OAAO,OAAA,KAAY,QAAY,IAAA,OAAA,KAAY,IAAM,EAAA;AACnD,QAAA,MAAM,IAAI,KAAA,CAAM,CAA8B,2BAAA,EAAA,OAAO,CAAG,CAAA,CAAA,CAAA;AAAA;AAE1D,MAAA,OAAO,OAAQ,CAAA,IAAA;AAAA;AACjB,GACD,CAAA;AACH;AAEA,eAAe,YAAA,CACb,QACA,EAAA,OAAA,EACA,IACsB,EAAA;AACtB,EAAA,IAAI,QAAQ,KAAO,EAAA;AACjB,IAAM,MAAA,EAAE,WAAY,EAAA,GAAI,OAAQ,CAAA,KAAA;AAChC,IAAI,IAAA,WAAA,CAAY,SAAS,WAAa,EAAA;AACpC,MAAA,MAAM,cAAc,MAAMA,+CAAA;AAAA,QACxB,WAAY,CAAA,QAAA;AAAA,QACZ,IAAK,CAAA;AAAA,OACP;AACA,MAAO,OAAAC,gBAAA,CAAM,YAAa,CAAA,WAAA,EAAa,QAAQ,CAAA;AAAA;AAEjD,IAAO,OAAAA,gBAAA,CAAM,YAAa,CAAA,WAAA,EAAa,QAAQ,CAAA;AAAA;AAGjD,EAAO,OAAAC,eAAA,CAAK,aAAa,QAAQ,CAAA;AACnC;;;;"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
var backendPluginApi = require('@backstage/backend-plugin-api');
|
|
4
4
|
var express = require('express');
|
|
5
|
-
var config
|
|
5
|
+
var config = require('./http/config.cjs.js');
|
|
6
6
|
var createHttpServer = require('./http/createHttpServer.cjs.js');
|
|
7
7
|
var MiddlewareFactory = require('./http/MiddlewareFactory.cjs.js');
|
|
8
8
|
require('minimatch');
|
|
@@ -10,8 +10,8 @@ require('helmet');
|
|
|
10
10
|
require('lodash/kebabCase');
|
|
11
11
|
var DefaultRootHttpRouter = require('./DefaultRootHttpRouter.cjs.js');
|
|
12
12
|
var createHealthRouter = require('./createHealthRouter.cjs.js');
|
|
13
|
-
var
|
|
14
|
-
var config = require('@backstage/config');
|
|
13
|
+
var types = require('@backstage/types');
|
|
14
|
+
var config$1 = require('@backstage/config');
|
|
15
15
|
|
|
16
16
|
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
17
17
|
|
|
@@ -36,26 +36,9 @@ const rootHttpRouterServiceFactoryWithOptions = (options) => backendPluginApi.cr
|
|
|
36
36
|
const middleware = MiddlewareFactory.MiddlewareFactory.create({ config: config$2, logger });
|
|
37
37
|
const routes = router.handler();
|
|
38
38
|
const healthRouter = createHealthRouter.createHealthRouter({ config: config$2, health });
|
|
39
|
-
let startupRequestPauseTimeout;
|
|
40
|
-
if (config$2.has("backend.lifecycle.startupRequestPauseTimeout")) {
|
|
41
|
-
startupRequestPauseTimeout = config.readDurationFromConfig(config$2, {
|
|
42
|
-
key: "backend.lifecycle.startupRequestPauseTimeout"
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
let serverShutdownDelay;
|
|
46
|
-
if (config$2.has("backend.lifecycle.serverShutdownDelay")) {
|
|
47
|
-
serverShutdownDelay = config.readDurationFromConfig(config$2, {
|
|
48
|
-
key: "backend.lifecycle.serverShutdownDelay"
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
const lifecycleMiddleware = createLifecycleMiddleware.createLifecycleMiddleware({
|
|
52
|
-
lifecycle,
|
|
53
|
-
startupRequestPauseTimeout,
|
|
54
|
-
serverShutdownDelay
|
|
55
|
-
});
|
|
56
39
|
const server = await createHttpServer.createHttpServer(
|
|
57
40
|
app,
|
|
58
|
-
config
|
|
41
|
+
config.readHttpServerOptions(config$2.getOptionalConfig("backend")),
|
|
59
42
|
{ logger }
|
|
60
43
|
);
|
|
61
44
|
configure({
|
|
@@ -67,7 +50,6 @@ const rootHttpRouterServiceFactoryWithOptions = (options) => backendPluginApi.cr
|
|
|
67
50
|
logger,
|
|
68
51
|
lifecycle,
|
|
69
52
|
healthRouter,
|
|
70
|
-
lifecycleMiddleware,
|
|
71
53
|
applyDefaults() {
|
|
72
54
|
if (process.env.NODE_ENV === "development") {
|
|
73
55
|
app.set("json spaces", 2);
|
|
@@ -77,12 +59,22 @@ const rootHttpRouterServiceFactoryWithOptions = (options) => backendPluginApi.cr
|
|
|
77
59
|
app.use(middleware.compression());
|
|
78
60
|
app.use(middleware.logging());
|
|
79
61
|
app.use(healthRouter);
|
|
80
|
-
app.use(lifecycleMiddleware);
|
|
81
62
|
app.use(routes);
|
|
82
63
|
app.use(middleware.notFound());
|
|
83
64
|
app.use(middleware.error());
|
|
84
65
|
}
|
|
85
66
|
});
|
|
67
|
+
if (config$2.has("backend.lifecycle.serverShutdownDelay")) {
|
|
68
|
+
const serverShutdownDelay = config$1.readDurationFromConfig(config$2, {
|
|
69
|
+
key: "backend.lifecycle.serverShutdownDelay"
|
|
70
|
+
});
|
|
71
|
+
lifecycle.addBeforeShutdownHook(async () => {
|
|
72
|
+
const timeoutMs = types.durationToMilliseconds(serverShutdownDelay);
|
|
73
|
+
return await new Promise((resolve) => {
|
|
74
|
+
setTimeout(resolve, timeoutMs);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
}
|
|
86
78
|
lifecycle.addShutdownHook(() => server.stop());
|
|
87
79
|
await server.start();
|
|
88
80
|
return router;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rootHttpRouterServiceFactory.cjs.js","sources":["../../../src/entrypoints/rootHttpRouter/rootHttpRouterServiceFactory.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n RootConfigService,\n coreServices,\n createServiceFactory,\n LifecycleService,\n LoggerService,\n} from '@backstage/backend-plugin-api';\nimport express, { RequestHandler, Express } from 'express';\nimport type { Server } from 'node:http';\nimport {\n createHttpServer,\n MiddlewareFactory,\n readHttpServerOptions,\n} from './http';\nimport { DefaultRootHttpRouter } from './DefaultRootHttpRouter';\nimport { createHealthRouter } from './createHealthRouter';\nimport {
|
|
1
|
+
{"version":3,"file":"rootHttpRouterServiceFactory.cjs.js","sources":["../../../src/entrypoints/rootHttpRouter/rootHttpRouterServiceFactory.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n RootConfigService,\n coreServices,\n createServiceFactory,\n LifecycleService,\n LoggerService,\n} from '@backstage/backend-plugin-api';\nimport express, { RequestHandler, Express } from 'express';\nimport type { Server } from 'node:http';\nimport {\n createHttpServer,\n MiddlewareFactory,\n readHttpServerOptions,\n} from './http';\nimport { DefaultRootHttpRouter } from './DefaultRootHttpRouter';\nimport { createHealthRouter } from './createHealthRouter';\nimport { durationToMilliseconds } from '@backstage/types';\nimport { readDurationFromConfig } from '@backstage/config';\n\n/**\n * @public\n */\nexport interface RootHttpRouterConfigureContext {\n app: Express;\n server: Server;\n middleware: MiddlewareFactory;\n routes: RequestHandler;\n config: RootConfigService;\n logger: LoggerService;\n lifecycle: LifecycleService;\n healthRouter: RequestHandler;\n applyDefaults: () => void;\n}\n\n/**\n * HTTP route registration for root services.\n *\n * See {@link @backstage/code-plugin-api#RootHttpRouterService}\n * and {@link https://backstage.io/docs/backend-system/core-services/root-http-router | the service docs}\n * for more information.\n *\n * @public\n */\nexport type RootHttpRouterFactoryOptions = {\n /**\n * The path to forward all unmatched requests to. Defaults to '/api/app' if\n * not given. Disables index path behavior if false is given.\n */\n indexPath?: string | false;\n\n configure?(context: RootHttpRouterConfigureContext): void;\n};\n\nfunction defaultConfigure({ applyDefaults }: RootHttpRouterConfigureContext) {\n applyDefaults();\n}\n\nconst rootHttpRouterServiceFactoryWithOptions = (\n options?: RootHttpRouterFactoryOptions,\n) =>\n createServiceFactory({\n service: coreServices.rootHttpRouter,\n deps: {\n config: coreServices.rootConfig,\n rootLogger: coreServices.rootLogger,\n lifecycle: coreServices.rootLifecycle,\n health: coreServices.rootHealth,\n },\n async factory({ config, rootLogger, lifecycle, health }) {\n const { indexPath, configure = defaultConfigure } = options ?? {};\n const logger = rootLogger.child({ service: 'rootHttpRouter' });\n const app = express();\n\n const router = DefaultRootHttpRouter.create({ indexPath });\n const middleware = MiddlewareFactory.create({ config, logger });\n const routes = router.handler();\n\n const healthRouter = createHealthRouter({ config, health });\n\n const server = await createHttpServer(\n app,\n readHttpServerOptions(config.getOptionalConfig('backend')),\n { logger },\n );\n\n configure({\n app,\n server,\n routes,\n middleware,\n config,\n logger,\n lifecycle,\n healthRouter,\n applyDefaults() {\n if (process.env.NODE_ENV === 'development') {\n app.set('json spaces', 2);\n }\n app.use(middleware.helmet());\n app.use(middleware.cors());\n app.use(middleware.compression());\n app.use(middleware.logging());\n app.use(healthRouter);\n app.use(routes);\n app.use(middleware.notFound());\n app.use(middleware.error());\n },\n });\n\n if (config.has('backend.lifecycle.serverShutdownDelay')) {\n const serverShutdownDelay = readDurationFromConfig(config, {\n key: 'backend.lifecycle.serverShutdownDelay',\n });\n lifecycle.addBeforeShutdownHook(async () => {\n const timeoutMs = durationToMilliseconds(serverShutdownDelay);\n return await new Promise(resolve => {\n setTimeout(resolve, timeoutMs);\n });\n });\n }\n\n lifecycle.addShutdownHook(() => server.stop());\n\n await server.start();\n\n return router;\n },\n });\n\n/** @public */\nexport const rootHttpRouterServiceFactory = Object.assign(\n rootHttpRouterServiceFactoryWithOptions,\n rootHttpRouterServiceFactoryWithOptions(),\n);\n"],"names":["createServiceFactory","coreServices","config","express","DefaultRootHttpRouter","MiddlewareFactory","createHealthRouter","createHttpServer","readHttpServerOptions","readDurationFromConfig","durationToMilliseconds"],"mappings":";;;;;;;;;;;;;;;;;;;AAqEA,SAAS,gBAAA,CAAiB,EAAE,aAAA,EAAiD,EAAA;AAC3E,EAAc,aAAA,EAAA;AAChB;AAEA,MAAM,uCAAA,GAA0C,CAC9C,OAAA,KAEAA,qCAAqB,CAAA;AAAA,EACnB,SAASC,6BAAa,CAAA,cAAA;AAAA,EACtB,IAAM,EAAA;AAAA,IACJ,QAAQA,6BAAa,CAAA,UAAA;AAAA,IACrB,YAAYA,6BAAa,CAAA,UAAA;AAAA,IACzB,WAAWA,6BAAa,CAAA,aAAA;AAAA,IACxB,QAAQA,6BAAa,CAAA;AAAA,GACvB;AAAA,EACA,MAAM,OAAQ,CAAA,UAAEC,UAAQ,UAAY,EAAA,SAAA,EAAW,QAAU,EAAA;AACvD,IAAA,MAAM,EAAE,SAAW,EAAA,SAAA,GAAY,gBAAiB,EAAA,GAAI,WAAW,EAAC;AAChE,IAAA,MAAM,SAAS,UAAW,CAAA,KAAA,CAAM,EAAE,OAAA,EAAS,kBAAkB,CAAA;AAC7D,IAAA,MAAM,MAAMC,wBAAQ,EAAA;AAEpB,IAAA,MAAM,MAAS,GAAAC,2CAAA,CAAsB,MAAO,CAAA,EAAE,WAAW,CAAA;AACzD,IAAA,MAAM,aAAaC,mCAAkB,CAAA,MAAA,CAAO,UAAEH,QAAA,EAAQ,QAAQ,CAAA;AAC9D,IAAM,MAAA,MAAA,GAAS,OAAO,OAAQ,EAAA;AAE9B,IAAA,MAAM,YAAe,GAAAI,qCAAA,CAAmB,UAAEJ,QAAA,EAAQ,QAAQ,CAAA;AAE1D,IAAA,MAAM,SAAS,MAAMK,iCAAA;AAAA,MACnB,GAAA;AAAA,MACAC,4BAAsB,CAAAN,QAAA,CAAO,iBAAkB,CAAA,SAAS,CAAC,CAAA;AAAA,MACzD,EAAE,MAAO;AAAA,KACX;AAEA,IAAU,SAAA,CAAA;AAAA,MACR,GAAA;AAAA,MACA,MAAA;AAAA,MACA,MAAA;AAAA,MACA,UAAA;AAAA,cACAA,QAAA;AAAA,MACA,MAAA;AAAA,MACA,SAAA;AAAA,MACA,YAAA;AAAA,MACA,aAAgB,GAAA;AACd,QAAI,IAAA,OAAA,CAAQ,GAAI,CAAA,QAAA,KAAa,aAAe,EAAA;AAC1C,UAAI,GAAA,CAAA,GAAA,CAAI,eAAe,CAAC,CAAA;AAAA;AAE1B,QAAI,GAAA,CAAA,GAAA,CAAI,UAAW,CAAA,MAAA,EAAQ,CAAA;AAC3B,QAAI,GAAA,CAAA,GAAA,CAAI,UAAW,CAAA,IAAA,EAAM,CAAA;AACzB,QAAI,GAAA,CAAA,GAAA,CAAI,UAAW,CAAA,WAAA,EAAa,CAAA;AAChC,QAAI,GAAA,CAAA,GAAA,CAAI,UAAW,CAAA,OAAA,EAAS,CAAA;AAC5B,QAAA,GAAA,CAAI,IAAI,YAAY,CAAA;AACpB,QAAA,GAAA,CAAI,IAAI,MAAM,CAAA;AACd,QAAI,GAAA,CAAA,GAAA,CAAI,UAAW,CAAA,QAAA,EAAU,CAAA;AAC7B,QAAI,GAAA,CAAA,GAAA,CAAI,UAAW,CAAA,KAAA,EAAO,CAAA;AAAA;AAC5B,KACD,CAAA;AAED,IAAI,IAAAA,QAAA,CAAO,GAAI,CAAA,uCAAuC,CAAG,EAAA;AACvD,MAAM,MAAA,mBAAA,GAAsBO,gCAAuBP,QAAQ,EAAA;AAAA,QACzD,GAAK,EAAA;AAAA,OACN,CAAA;AACD,MAAA,SAAA,CAAU,sBAAsB,YAAY;AAC1C,QAAM,MAAA,SAAA,GAAYQ,6BAAuB,mBAAmB,CAAA;AAC5D,QAAO,OAAA,MAAM,IAAI,OAAA,CAAQ,CAAW,OAAA,KAAA;AAClC,UAAA,UAAA,CAAW,SAAS,SAAS,CAAA;AAAA,SAC9B,CAAA;AAAA,OACF,CAAA;AAAA;AAGH,IAAA,SAAA,CAAU,eAAgB,CAAA,MAAM,MAAO,CAAA,IAAA,EAAM,CAAA;AAE7C,IAAA,MAAM,OAAO,KAAM,EAAA;AAEnB,IAAO,OAAA,MAAA;AAAA;AAEX,CAAC,CAAA;AAGI,MAAM,+BAA+B,MAAO,CAAA,MAAA;AAAA,EACjD,uCAAA;AAAA,EACA,uCAAwC;AAC1C;;;;"}
|
|
@@ -10,7 +10,7 @@ var integration = require('@backstage/integration');
|
|
|
10
10
|
function parseUrl(url) {
|
|
11
11
|
const parsedUrl = new URL(url);
|
|
12
12
|
const pathSegments = parsedUrl.pathname.split("/").filter(Boolean);
|
|
13
|
-
if (pathSegments.length <
|
|
13
|
+
if (pathSegments.length < 1) {
|
|
14
14
|
throw new Error(`Invalid Azure Blob Storage URL format: ${url}`);
|
|
15
15
|
}
|
|
16
16
|
const container = pathSegments[0];
|
|
@@ -101,7 +101,7 @@ class AzureBlobStorageUrlReader {
|
|
|
101
101
|
}
|
|
102
102
|
);
|
|
103
103
|
} catch (e) {
|
|
104
|
-
if (e
|
|
104
|
+
if (e.statusCode === 304) {
|
|
105
105
|
throw new errors.NotModifiedError();
|
|
106
106
|
}
|
|
107
107
|
throw new errors.ForwardedError(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AzureBlobStorageUrlReader.cjs.js","sources":["../../../../src/entrypoints/urlReader/lib/AzureBlobStorageUrlReader.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n BlobDownloadOptions,\n BlobServiceClient,\n ContainerClient,\n StorageSharedKeyCredential,\n} from '@azure/storage-blob';\nimport { ReaderFactory, ReadTreeResponseFactory } from './types';\nimport { ForwardedError, NotModifiedError } from '@backstage/errors';\nimport { Readable } from 'stream';\nimport { relative } from 'path/posix';\nimport { ReadUrlResponseFactory } from './ReadUrlResponseFactory';\nimport {\n AzureBlobStorageIntergation,\n AzureCredentialsManager,\n DefaultAzureCredentialsManager,\n ScmIntegrations,\n} from '@backstage/integration';\nimport {\n UrlReaderService,\n UrlReaderServiceReadTreeOptions,\n UrlReaderServiceReadTreeResponse,\n UrlReaderServiceReadUrlOptions,\n UrlReaderServiceReadUrlResponse,\n UrlReaderServiceSearchResponse,\n} from '@backstage/backend-plugin-api';\n\nexport function parseUrl(url: string): { path: string; container: string } {\n const parsedUrl = new URL(url);\n const pathSegments = parsedUrl.pathname.split('/').filter(Boolean);\n\n if (pathSegments.length < 2) {\n throw new Error(`Invalid Azure Blob Storage URL format: ${url}`);\n }\n\n // First segment is the container name, rest is the blob path\n const container = pathSegments[0];\n const path = pathSegments.slice(1).join('/');\n\n return { path, container };\n}\n\n/**\n * Implements a {@link @backstage/backend-plugin-api#UrlReaderService} for Azure storage accounts urls.\n *\n * @public\n */\nexport class AzureBlobStorageUrlReader implements UrlReaderService {\n static factory: ReaderFactory = ({ config, treeResponseFactory }) => {\n const integrations = ScmIntegrations.fromConfig(config);\n\n const credsManager =\n DefaultAzureCredentialsManager.fromIntegrations(integrations);\n\n return integrations.azureBlobStorage.list().map(integrationConfig => {\n const reader = new AzureBlobStorageUrlReader(\n credsManager,\n integrationConfig,\n {\n treeResponseFactory,\n },\n );\n\n const predicate = (url: URL) =>\n url.host.endsWith(\n `${integrationConfig.config.accountName}.${integrationConfig.config.host}`,\n );\n return { reader, predicate };\n });\n };\n\n // private readonly blobServiceClient: BlobServiceClient;\n\n constructor(\n private readonly credsManager: AzureCredentialsManager,\n private readonly integration: AzureBlobStorageIntergation,\n private readonly deps: {\n treeResponseFactory: ReadTreeResponseFactory;\n },\n ) {}\n\n private async createContainerClient(\n containerName: string,\n ): Promise<ContainerClient> {\n const accountName = this.integration.config.accountName; // Use the account name from the integration config\n const accountKey = this.integration.config.accountKey; // Get the account key if it exists\n\n if (accountKey && accountName) {\n const creds = new StorageSharedKeyCredential(accountName, accountKey);\n const blobServiceClient = new BlobServiceClient(\n `https://${accountName}.${this.integration.config.host}`,\n creds,\n );\n return blobServiceClient.getContainerClient(containerName);\n }\n // Use the credentials manager to get the correct credentials\n const credential = await this.credsManager.getCredentials(\n accountName as string,\n );\n\n let blobServiceClientUrl: string;\n\n if (this.integration.config.endpoint) {\n if (this.integration.config.sasToken) {\n blobServiceClientUrl = `${this.integration.config.endpoint}?${this.integration.config.sasToken}`;\n } else {\n blobServiceClientUrl = `${this.integration.config.endpoint}`;\n }\n } else {\n blobServiceClientUrl = `https://${this.integration.config.accountName}.${this.integration.config.host}`;\n }\n\n const blobServiceClient = new BlobServiceClient(\n blobServiceClientUrl,\n credential,\n );\n return blobServiceClient.getContainerClient(containerName);\n }\n\n async read(url: string): Promise<Buffer> {\n const response = await this.readUrl(url);\n return response.buffer();\n }\n\n async readUrl(\n url: string,\n options?: UrlReaderServiceReadUrlOptions,\n ): Promise<UrlReaderServiceReadUrlResponse> {\n const { etag, lastModifiedAfter } = options ?? {};\n\n try {\n const { path, container } = parseUrl(url);\n\n const containerClient = await this.createContainerClient(container);\n const blobClient = containerClient.getBlobClient(path);\n\n const getBlobOptions: BlobDownloadOptions = {\n abortSignal: options?.signal,\n conditions: {\n ...(etag && { ifNoneMatch: etag }),\n ...(lastModifiedAfter && { ifModifiedSince: lastModifiedAfter }),\n },\n };\n\n const downloadBlockBlobResponse = await blobClient.download(\n 0,\n undefined,\n getBlobOptions,\n );\n\n return ReadUrlResponseFactory.fromReadable(\n downloadBlockBlobResponse.readableStreamBody as Readable,\n {\n etag: downloadBlockBlobResponse.etag,\n lastModifiedAt: downloadBlockBlobResponse.lastModified,\n },\n );\n } catch (e) {\n if (e.$metadata && e.$metadata.httpStatusCode === 304) {\n throw new NotModifiedError();\n }\n\n throw new ForwardedError(\n 'Could not retrieve file from Azure Blob Storage',\n e,\n );\n }\n }\n\n async readTree(\n url: string,\n options?: UrlReaderServiceReadTreeOptions,\n ): Promise<UrlReaderServiceReadTreeResponse> {\n try {\n const { path, container } = parseUrl(url);\n\n const containerClient = await this.createContainerClient(container);\n\n const blobs = containerClient.listBlobsFlat({ prefix: path });\n\n const responses = [];\n\n for await (const blob of blobs) {\n const blobClient = containerClient.getBlobClient(blob.name);\n const downloadBlockBlobResponse = await blobClient.download(\n undefined,\n undefined,\n { abortSignal: options?.signal },\n );\n\n responses.push({\n data: Readable.from(\n downloadBlockBlobResponse.readableStreamBody as Readable,\n ),\n path: relative(path, blob.name),\n lastModifiedAt: blob.properties.lastModified,\n });\n }\n\n return this.deps.treeResponseFactory.fromReadableArray(responses);\n } catch (e) {\n throw new ForwardedError(\n 'Could not retrieve file tree from Azure Blob Storage',\n e,\n );\n }\n }\n\n async search(): Promise<UrlReaderServiceSearchResponse> {\n throw new Error('AzureBlobStorageUrlReader does not implement search');\n }\n\n toString() {\n const accountName = this.integration.config.accountName;\n const accountKey = this.integration.config.accountKey;\n return `azureBlobStorage{accountName=${accountName},authed=${Boolean(\n accountKey,\n )}}`;\n }\n}\n"],"names":["ScmIntegrations","DefaultAzureCredentialsManager","StorageSharedKeyCredential","blobServiceClient","BlobServiceClient","ReadUrlResponseFactory","NotModifiedError","ForwardedError","Readable","relative"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AzureBlobStorageUrlReader.cjs.js","sources":["../../../../src/entrypoints/urlReader/lib/AzureBlobStorageUrlReader.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n BlobDownloadOptions,\n BlobServiceClient,\n ContainerClient,\n StorageSharedKeyCredential,\n} from '@azure/storage-blob';\nimport { ReaderFactory, ReadTreeResponseFactory } from './types';\nimport { ForwardedError, NotModifiedError } from '@backstage/errors';\nimport { Readable } from 'stream';\nimport { relative } from 'path/posix';\nimport { ReadUrlResponseFactory } from './ReadUrlResponseFactory';\nimport {\n AzureBlobStorageIntergation,\n AzureCredentialsManager,\n DefaultAzureCredentialsManager,\n ScmIntegrations,\n} from '@backstage/integration';\nimport {\n UrlReaderService,\n UrlReaderServiceReadTreeOptions,\n UrlReaderServiceReadTreeResponse,\n UrlReaderServiceReadUrlOptions,\n UrlReaderServiceReadUrlResponse,\n UrlReaderServiceSearchResponse,\n} from '@backstage/backend-plugin-api';\n\nexport function parseUrl(url: string): { path: string; container: string } {\n const parsedUrl = new URL(url);\n const pathSegments = parsedUrl.pathname.split('/').filter(Boolean);\n\n if (pathSegments.length < 1) {\n throw new Error(`Invalid Azure Blob Storage URL format: ${url}`);\n }\n\n // First segment is the container name, rest is the blob path\n const container = pathSegments[0];\n const path = pathSegments.slice(1).join('/');\n\n return { path, container };\n}\n\n/**\n * Implements a {@link @backstage/backend-plugin-api#UrlReaderService} for Azure storage accounts urls.\n *\n * @public\n */\nexport class AzureBlobStorageUrlReader implements UrlReaderService {\n static factory: ReaderFactory = ({ config, treeResponseFactory }) => {\n const integrations = ScmIntegrations.fromConfig(config);\n\n const credsManager =\n DefaultAzureCredentialsManager.fromIntegrations(integrations);\n\n return integrations.azureBlobStorage.list().map(integrationConfig => {\n const reader = new AzureBlobStorageUrlReader(\n credsManager,\n integrationConfig,\n {\n treeResponseFactory,\n },\n );\n\n const predicate = (url: URL) =>\n url.host.endsWith(\n `${integrationConfig.config.accountName}.${integrationConfig.config.host}`,\n );\n return { reader, predicate };\n });\n };\n\n // private readonly blobServiceClient: BlobServiceClient;\n\n constructor(\n private readonly credsManager: AzureCredentialsManager,\n private readonly integration: AzureBlobStorageIntergation,\n private readonly deps: {\n treeResponseFactory: ReadTreeResponseFactory;\n },\n ) {}\n\n private async createContainerClient(\n containerName: string,\n ): Promise<ContainerClient> {\n const accountName = this.integration.config.accountName; // Use the account name from the integration config\n const accountKey = this.integration.config.accountKey; // Get the account key if it exists\n\n if (accountKey && accountName) {\n const creds = new StorageSharedKeyCredential(accountName, accountKey);\n const blobServiceClient = new BlobServiceClient(\n `https://${accountName}.${this.integration.config.host}`,\n creds,\n );\n return blobServiceClient.getContainerClient(containerName);\n }\n // Use the credentials manager to get the correct credentials\n const credential = await this.credsManager.getCredentials(\n accountName as string,\n );\n\n let blobServiceClientUrl: string;\n\n if (this.integration.config.endpoint) {\n if (this.integration.config.sasToken) {\n blobServiceClientUrl = `${this.integration.config.endpoint}?${this.integration.config.sasToken}`;\n } else {\n blobServiceClientUrl = `${this.integration.config.endpoint}`;\n }\n } else {\n blobServiceClientUrl = `https://${this.integration.config.accountName}.${this.integration.config.host}`;\n }\n\n const blobServiceClient = new BlobServiceClient(\n blobServiceClientUrl,\n credential,\n );\n return blobServiceClient.getContainerClient(containerName);\n }\n\n async read(url: string): Promise<Buffer> {\n const response = await this.readUrl(url);\n return response.buffer();\n }\n\n async readUrl(\n url: string,\n options?: UrlReaderServiceReadUrlOptions,\n ): Promise<UrlReaderServiceReadUrlResponse> {\n const { etag, lastModifiedAfter } = options ?? {};\n\n try {\n const { path, container } = parseUrl(url);\n\n const containerClient = await this.createContainerClient(container);\n const blobClient = containerClient.getBlobClient(path);\n\n const getBlobOptions: BlobDownloadOptions = {\n abortSignal: options?.signal,\n conditions: {\n ...(etag && { ifNoneMatch: etag }),\n ...(lastModifiedAfter && { ifModifiedSince: lastModifiedAfter }),\n },\n };\n\n const downloadBlockBlobResponse = await blobClient.download(\n 0,\n undefined,\n getBlobOptions,\n );\n\n return ReadUrlResponseFactory.fromReadable(\n downloadBlockBlobResponse.readableStreamBody as Readable,\n {\n etag: downloadBlockBlobResponse.etag,\n lastModifiedAt: downloadBlockBlobResponse.lastModified,\n },\n );\n } catch (e) {\n if (e.statusCode === 304) {\n throw new NotModifiedError();\n }\n\n throw new ForwardedError(\n 'Could not retrieve file from Azure Blob Storage',\n e,\n );\n }\n }\n\n async readTree(\n url: string,\n options?: UrlReaderServiceReadTreeOptions,\n ): Promise<UrlReaderServiceReadTreeResponse> {\n try {\n const { path, container } = parseUrl(url);\n\n const containerClient = await this.createContainerClient(container);\n\n const blobs = containerClient.listBlobsFlat({ prefix: path });\n\n const responses = [];\n\n for await (const blob of blobs) {\n const blobClient = containerClient.getBlobClient(blob.name);\n const downloadBlockBlobResponse = await blobClient.download(\n undefined,\n undefined,\n { abortSignal: options?.signal },\n );\n\n responses.push({\n data: Readable.from(\n downloadBlockBlobResponse.readableStreamBody as Readable,\n ),\n path: relative(path, blob.name),\n lastModifiedAt: blob.properties.lastModified,\n });\n }\n\n return this.deps.treeResponseFactory.fromReadableArray(responses);\n } catch (e) {\n throw new ForwardedError(\n 'Could not retrieve file tree from Azure Blob Storage',\n e,\n );\n }\n }\n\n async search(): Promise<UrlReaderServiceSearchResponse> {\n throw new Error('AzureBlobStorageUrlReader does not implement search');\n }\n\n toString() {\n const accountName = this.integration.config.accountName;\n const accountKey = this.integration.config.accountKey;\n return `azureBlobStorage{accountName=${accountName},authed=${Boolean(\n accountKey,\n )}}`;\n }\n}\n"],"names":["ScmIntegrations","DefaultAzureCredentialsManager","StorageSharedKeyCredential","blobServiceClient","BlobServiceClient","ReadUrlResponseFactory","NotModifiedError","ForwardedError","Readable","relative"],"mappings":";;;;;;;;;AA0CO,SAAS,SAAS,GAAkD,EAAA;AACzE,EAAM,MAAA,SAAA,GAAY,IAAI,GAAA,CAAI,GAAG,CAAA;AAC7B,EAAA,MAAM,eAAe,SAAU,CAAA,QAAA,CAAS,MAAM,GAAG,CAAA,CAAE,OAAO,OAAO,CAAA;AAEjE,EAAI,IAAA,YAAA,CAAa,SAAS,CAAG,EAAA;AAC3B,IAAA,MAAM,IAAI,KAAA,CAAM,CAA0C,uCAAA,EAAA,GAAG,CAAE,CAAA,CAAA;AAAA;AAIjE,EAAM,MAAA,SAAA,GAAY,aAAa,CAAC,CAAA;AAChC,EAAA,MAAM,OAAO,YAAa,CAAA,KAAA,CAAM,CAAC,CAAA,CAAE,KAAK,GAAG,CAAA;AAE3C,EAAO,OAAA,EAAE,MAAM,SAAU,EAAA;AAC3B;AAOO,MAAM,yBAAsD,CAAA;AAAA;AAAA,EA0BjE,WAAA,CACmB,YACA,EAAA,WAAA,EACA,IAGjB,EAAA;AALiB,IAAA,IAAA,CAAA,YAAA,GAAA,YAAA;AACA,IAAA,IAAA,CAAA,WAAA,GAAA,WAAA;AACA,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AAAA;AAGhB,EA/BH,OAAO,OAAyB,GAAA,CAAC,EAAE,MAAA,EAAQ,qBAA0B,KAAA;AACnE,IAAM,MAAA,YAAA,GAAeA,2BAAgB,CAAA,UAAA,CAAW,MAAM,CAAA;AAEtD,IAAM,MAAA,YAAA,GACJC,0CAA+B,CAAA,gBAAA,CAAiB,YAAY,CAAA;AAE9D,IAAA,OAAO,YAAa,CAAA,gBAAA,CAAiB,IAAK,EAAA,CAAE,IAAI,CAAqB,iBAAA,KAAA;AACnE,MAAA,MAAM,SAAS,IAAI,yBAAA;AAAA,QACjB,YAAA;AAAA,QACA,iBAAA;AAAA,QACA;AAAA,UACE;AAAA;AACF,OACF;AAEA,MAAA,MAAM,SAAY,GAAA,CAAC,GACjB,KAAA,GAAA,CAAI,IAAK,CAAA,QAAA;AAAA,QACP,GAAG,iBAAkB,CAAA,MAAA,CAAO,WAAW,CAAI,CAAA,EAAA,iBAAA,CAAkB,OAAO,IAAI,CAAA;AAAA,OAC1E;AACF,MAAO,OAAA,EAAE,QAAQ,SAAU,EAAA;AAAA,KAC5B,CAAA;AAAA,GACH;AAAA,EAYA,MAAc,sBACZ,aAC0B,EAAA;AAC1B,IAAM,MAAA,WAAA,GAAc,IAAK,CAAA,WAAA,CAAY,MAAO,CAAA,WAAA;AAC5C,IAAM,MAAA,UAAA,GAAa,IAAK,CAAA,WAAA,CAAY,MAAO,CAAA,UAAA;AAE3C,IAAA,IAAI,cAAc,WAAa,EAAA;AAC7B,MAAA,MAAM,KAAQ,GAAA,IAAIC,sCAA2B,CAAA,WAAA,EAAa,UAAU,CAAA;AACpE,MAAA,MAAMC,qBAAoB,IAAIC,6BAAA;AAAA,QAC5B,WAAW,WAAW,CAAA,CAAA,EAAI,IAAK,CAAA,WAAA,CAAY,OAAO,IAAI,CAAA,CAAA;AAAA,QACtD;AAAA,OACF;AACA,MAAOD,OAAAA,kBAAAA,CAAkB,mBAAmB,aAAa,CAAA;AAAA;AAG3D,IAAM,MAAA,UAAA,GAAa,MAAM,IAAA,CAAK,YAAa,CAAA,cAAA;AAAA,MACzC;AAAA,KACF;AAEA,IAAI,IAAA,oBAAA;AAEJ,IAAI,IAAA,IAAA,CAAK,WAAY,CAAA,MAAA,CAAO,QAAU,EAAA;AACpC,MAAI,IAAA,IAAA,CAAK,WAAY,CAAA,MAAA,CAAO,QAAU,EAAA;AACpC,QAAuB,oBAAA,GAAA,CAAA,EAAG,KAAK,WAAY,CAAA,MAAA,CAAO,QAAQ,CAAI,CAAA,EAAA,IAAA,CAAK,WAAY,CAAA,MAAA,CAAO,QAAQ,CAAA,CAAA;AAAA,OACzF,MAAA;AACL,QAAA,oBAAA,GAAuB,CAAG,EAAA,IAAA,CAAK,WAAY,CAAA,MAAA,CAAO,QAAQ,CAAA,CAAA;AAAA;AAC5D,KACK,MAAA;AACL,MAAuB,oBAAA,GAAA,CAAA,QAAA,EAAW,KAAK,WAAY,CAAA,MAAA,CAAO,WAAW,CAAI,CAAA,EAAA,IAAA,CAAK,WAAY,CAAA,MAAA,CAAO,IAAI,CAAA,CAAA;AAAA;AAGvG,IAAA,MAAM,oBAAoB,IAAIC,6BAAA;AAAA,MAC5B,oBAAA;AAAA,MACA;AAAA,KACF;AACA,IAAO,OAAA,iBAAA,CAAkB,mBAAmB,aAAa,CAAA;AAAA;AAC3D,EAEA,MAAM,KAAK,GAA8B,EAAA;AACvC,IAAA,MAAM,QAAW,GAAA,MAAM,IAAK,CAAA,OAAA,CAAQ,GAAG,CAAA;AACvC,IAAA,OAAO,SAAS,MAAO,EAAA;AAAA;AACzB,EAEA,MAAM,OACJ,CAAA,GAAA,EACA,OAC0C,EAAA;AAC1C,IAAA,MAAM,EAAE,IAAA,EAAM,iBAAkB,EAAA,GAAI,WAAW,EAAC;AAEhD,IAAI,IAAA;AACF,MAAA,MAAM,EAAE,IAAA,EAAM,SAAU,EAAA,GAAI,SAAS,GAAG,CAAA;AAExC,MAAA,MAAM,eAAkB,GAAA,MAAM,IAAK,CAAA,qBAAA,CAAsB,SAAS,CAAA;AAClE,MAAM,MAAA,UAAA,GAAa,eAAgB,CAAA,aAAA,CAAc,IAAI,CAAA;AAErD,MAAA,MAAM,cAAsC,GAAA;AAAA,QAC1C,aAAa,OAAS,EAAA,MAAA;AAAA,QACtB,UAAY,EAAA;AAAA,UACV,GAAI,IAAA,IAAQ,EAAE,WAAA,EAAa,IAAK,EAAA;AAAA,UAChC,GAAI,iBAAA,IAAqB,EAAE,eAAA,EAAiB,iBAAkB;AAAA;AAChE,OACF;AAEA,MAAM,MAAA,yBAAA,GAA4B,MAAM,UAAW,CAAA,QAAA;AAAA,QACjD,CAAA;AAAA,QACA,KAAA,CAAA;AAAA,QACA;AAAA,OACF;AAEA,MAAA,OAAOC,6CAAuB,CAAA,YAAA;AAAA,QAC5B,yBAA0B,CAAA,kBAAA;AAAA,QAC1B;AAAA,UACE,MAAM,yBAA0B,CAAA,IAAA;AAAA,UAChC,gBAAgB,yBAA0B,CAAA;AAAA;AAC5C,OACF;AAAA,aACO,CAAG,EAAA;AACV,MAAI,IAAA,CAAA,CAAE,eAAe,GAAK,EAAA;AACxB,QAAA,MAAM,IAAIC,uBAAiB,EAAA;AAAA;AAG7B,MAAA,MAAM,IAAIC,qBAAA;AAAA,QACR,iDAAA;AAAA,QACA;AAAA,OACF;AAAA;AACF;AACF,EAEA,MAAM,QACJ,CAAA,GAAA,EACA,OAC2C,EAAA;AAC3C,IAAI,IAAA;AACF,MAAA,MAAM,EAAE,IAAA,EAAM,SAAU,EAAA,GAAI,SAAS,GAAG,CAAA;AAExC,MAAA,MAAM,eAAkB,GAAA,MAAM,IAAK,CAAA,qBAAA,CAAsB,SAAS,CAAA;AAElE,MAAA,MAAM,QAAQ,eAAgB,CAAA,aAAA,CAAc,EAAE,MAAA,EAAQ,MAAM,CAAA;AAE5D,MAAA,MAAM,YAAY,EAAC;AAEnB,MAAA,WAAA,MAAiB,QAAQ,KAAO,EAAA;AAC9B,QAAA,MAAM,UAAa,GAAA,eAAA,CAAgB,aAAc,CAAA,IAAA,CAAK,IAAI,CAAA;AAC1D,QAAM,MAAA,yBAAA,GAA4B,MAAM,UAAW,CAAA,QAAA;AAAA,UACjD,KAAA,CAAA;AAAA,UACA,KAAA,CAAA;AAAA,UACA,EAAE,WAAa,EAAA,OAAA,EAAS,MAAO;AAAA,SACjC;AAEA,QAAA,SAAA,CAAU,IAAK,CAAA;AAAA,UACb,MAAMC,eAAS,CAAA,IAAA;AAAA,YACb,yBAA0B,CAAA;AAAA,WAC5B;AAAA,UACA,IAAM,EAAAC,cAAA,CAAS,IAAM,EAAA,IAAA,CAAK,IAAI,CAAA;AAAA,UAC9B,cAAA,EAAgB,KAAK,UAAW,CAAA;AAAA,SACjC,CAAA;AAAA;AAGH,MAAA,OAAO,IAAK,CAAA,IAAA,CAAK,mBAAoB,CAAA,iBAAA,CAAkB,SAAS,CAAA;AAAA,aACzD,CAAG,EAAA;AACV,MAAA,MAAM,IAAIF,qBAAA;AAAA,QACR,sDAAA;AAAA,QACA;AAAA,OACF;AAAA;AACF;AACF,EAEA,MAAM,MAAkD,GAAA;AACtD,IAAM,MAAA,IAAI,MAAM,qDAAqD,CAAA;AAAA;AACvE,EAEA,QAAW,GAAA;AACT,IAAM,MAAA,WAAA,GAAc,IAAK,CAAA,WAAA,CAAY,MAAO,CAAA,WAAA;AAC5C,IAAM,MAAA,UAAA,GAAa,IAAK,CAAA,WAAA,CAAY,MAAO,CAAA,UAAA;AAC3C,IAAO,OAAA,CAAA,6BAAA,EAAgC,WAAW,CAAW,QAAA,EAAA,OAAA;AAAA,MAC3D;AAAA,KACD,CAAA,CAAA,CAAA;AAAA;AAEL;;;;;"}
|
package/dist/httpRouter.d.ts
CHANGED
|
@@ -3,7 +3,6 @@ import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
|
|
|
3
3
|
import { AuthService, HttpAuthService, RootConfigService, HttpRouterServiceAuthPolicy, LifecycleService } from '@backstage/backend-plugin-api';
|
|
4
4
|
import * as express from 'express';
|
|
5
5
|
import express__default, { RequestHandler } from 'express';
|
|
6
|
-
import { HumanDuration } from '@backstage/types';
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* HTTP route registration for plugins.
|
|
@@ -39,13 +38,8 @@ declare function createCredentialsBarrier(options: {
|
|
|
39
38
|
* @public
|
|
40
39
|
*/
|
|
41
40
|
interface LifecycleMiddlewareOptions {
|
|
41
|
+
config: RootConfigService;
|
|
42
42
|
lifecycle: LifecycleService;
|
|
43
|
-
/**
|
|
44
|
-
* The maximum time that paused requests will wait for the service to start, before returning an error.
|
|
45
|
-
*
|
|
46
|
-
* Defaults to 5 seconds.
|
|
47
|
-
*/
|
|
48
|
-
startupRequestPauseTimeout?: HumanDuration;
|
|
49
43
|
}
|
|
50
44
|
/**
|
|
51
45
|
* Creates a middleware that pauses requests until the service has started.
|
package/dist/package.json.cjs.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var name = "@backstage/backend-defaults";
|
|
6
|
-
var version = "0.6.
|
|
6
|
+
var version = "0.6.1";
|
|
7
7
|
var description = "Backend defaults used by Backstage backend apps";
|
|
8
8
|
var backstage = {
|
|
9
9
|
role: "node-library"
|
package/dist/rootHttpRouter.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/backend-defaults",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "Backend defaults used by Backstage backend apps",
|
|
5
5
|
"backstage": {
|
|
6
6
|
"role": "node-library"
|
|
@@ -193,20 +193,20 @@
|
|
|
193
193
|
"@aws-sdk/types": "^3.347.0",
|
|
194
194
|
"@azure/identity": "^4.0.0",
|
|
195
195
|
"@azure/storage-blob": "^12.5.0",
|
|
196
|
-
"@backstage/backend-app-api": "1.1.0
|
|
197
|
-
"@backstage/backend-dev-utils": "0.1.5",
|
|
198
|
-
"@backstage/backend-plugin-api": "1.1.0
|
|
199
|
-
"@backstage/cli-common": "0.1.15",
|
|
200
|
-
"@backstage/cli-node": "0.2.11
|
|
201
|
-
"@backstage/config": "1.3.1
|
|
202
|
-
"@backstage/config-loader": "1.9.
|
|
203
|
-
"@backstage/errors": "1.2.6
|
|
204
|
-
"@backstage/integration": "1.16.0
|
|
205
|
-
"@backstage/integration-aws-node": "0.1.14
|
|
206
|
-
"@backstage/plugin-auth-node": "0.5.5
|
|
207
|
-
"@backstage/plugin-events-node": "0.4.6
|
|
208
|
-
"@backstage/plugin-permission-node": "0.8.6
|
|
209
|
-
"@backstage/types": "1.2.0",
|
|
196
|
+
"@backstage/backend-app-api": "^1.1.0",
|
|
197
|
+
"@backstage/backend-dev-utils": "^0.1.5",
|
|
198
|
+
"@backstage/backend-plugin-api": "^1.1.0",
|
|
199
|
+
"@backstage/cli-common": "^0.1.15",
|
|
200
|
+
"@backstage/cli-node": "^0.2.11",
|
|
201
|
+
"@backstage/config": "^1.3.1",
|
|
202
|
+
"@backstage/config-loader": "^1.9.4",
|
|
203
|
+
"@backstage/errors": "^1.2.6",
|
|
204
|
+
"@backstage/integration": "^1.16.0",
|
|
205
|
+
"@backstage/integration-aws-node": "^0.1.14",
|
|
206
|
+
"@backstage/plugin-auth-node": "^0.5.5",
|
|
207
|
+
"@backstage/plugin-events-node": "^0.4.6",
|
|
208
|
+
"@backstage/plugin-permission-node": "^0.8.6",
|
|
209
|
+
"@backstage/types": "^1.2.0",
|
|
210
210
|
"@google-cloud/storage": "^7.0.0",
|
|
211
211
|
"@keyv/memcache": "^2.0.1",
|
|
212
212
|
"@keyv/redis": "^4.0.1",
|
|
@@ -267,9 +267,9 @@
|
|
|
267
267
|
},
|
|
268
268
|
"devDependencies": {
|
|
269
269
|
"@aws-sdk/util-stream-node": "^3.350.0",
|
|
270
|
-
"@backstage/backend-plugin-api": "1.1.0
|
|
271
|
-
"@backstage/backend-test-utils": "1.2.0
|
|
272
|
-
"@backstage/cli": "0.29.
|
|
270
|
+
"@backstage/backend-plugin-api": "^1.1.0",
|
|
271
|
+
"@backstage/backend-test-utils": "^1.2.0",
|
|
272
|
+
"@backstage/cli": "^0.29.4",
|
|
273
273
|
"@google-cloud/cloud-sql-connector": "^1.4.0",
|
|
274
274
|
"@types/archiver": "^6.0.0",
|
|
275
275
|
"@types/base64-stream": "^1.0.2",
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var errors = require('@backstage/errors');
|
|
4
|
-
var types = require('@backstage/types');
|
|
5
|
-
|
|
6
|
-
const DEFAULT_STARTUP_REQUEST_PAUSE_TIMEOUT = { seconds: 5 };
|
|
7
|
-
const DEFAULT_SERVER_SHUTDOWN_TIMEOUT = { seconds: 0 };
|
|
8
|
-
function createLifecycleMiddleware(options) {
|
|
9
|
-
const { lifecycle, startupRequestPauseTimeout, serverShutdownDelay } = options;
|
|
10
|
-
let state = "init";
|
|
11
|
-
const waiting = /* @__PURE__ */ new Set();
|
|
12
|
-
lifecycle.addStartupHook(async () => {
|
|
13
|
-
if (state === "init") {
|
|
14
|
-
state = "up";
|
|
15
|
-
for (const item of waiting) {
|
|
16
|
-
clearTimeout(item.timeout);
|
|
17
|
-
item.next();
|
|
18
|
-
}
|
|
19
|
-
waiting.clear();
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
lifecycle.addBeforeShutdownHook(async () => {
|
|
23
|
-
const timeoutMs = types.durationToMilliseconds(
|
|
24
|
-
serverShutdownDelay ?? DEFAULT_SERVER_SHUTDOWN_TIMEOUT
|
|
25
|
-
);
|
|
26
|
-
return await new Promise((resolve) => {
|
|
27
|
-
setTimeout(resolve, timeoutMs);
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
lifecycle.addShutdownHook(async () => {
|
|
31
|
-
state = "down";
|
|
32
|
-
for (const item of waiting) {
|
|
33
|
-
clearTimeout(item.timeout);
|
|
34
|
-
item.next(new errors.ServiceUnavailableError("Service is shutting down"));
|
|
35
|
-
}
|
|
36
|
-
waiting.clear();
|
|
37
|
-
});
|
|
38
|
-
return (_req, _res, next) => {
|
|
39
|
-
if (state === "up") {
|
|
40
|
-
next();
|
|
41
|
-
return;
|
|
42
|
-
} else if (state === "down") {
|
|
43
|
-
next(new errors.ServiceUnavailableError("Service is shutting down"));
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
const timeoutMs = types.durationToMilliseconds(
|
|
47
|
-
startupRequestPauseTimeout ?? DEFAULT_STARTUP_REQUEST_PAUSE_TIMEOUT
|
|
48
|
-
);
|
|
49
|
-
const item = {
|
|
50
|
-
next,
|
|
51
|
-
timeout: setTimeout(() => {
|
|
52
|
-
if (waiting.delete(item)) {
|
|
53
|
-
next(new errors.ServiceUnavailableError("Service has not started up yet"));
|
|
54
|
-
}
|
|
55
|
-
}, timeoutMs)
|
|
56
|
-
};
|
|
57
|
-
waiting.add(item);
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
exports.DEFAULT_SERVER_SHUTDOWN_TIMEOUT = DEFAULT_SERVER_SHUTDOWN_TIMEOUT;
|
|
62
|
-
exports.DEFAULT_STARTUP_REQUEST_PAUSE_TIMEOUT = DEFAULT_STARTUP_REQUEST_PAUSE_TIMEOUT;
|
|
63
|
-
exports.createLifecycleMiddleware = createLifecycleMiddleware;
|
|
64
|
-
//# sourceMappingURL=createLifecycleMiddleware.cjs.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"createLifecycleMiddleware.cjs.js","sources":["../../../src/entrypoints/rootHttpRouter/createLifecycleMiddleware.ts"],"sourcesContent":["/*\n * Copyright 2022 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { RootLifecycleService } from '@backstage/backend-plugin-api';\nimport { ServiceUnavailableError } from '@backstage/errors';\nimport { HumanDuration, durationToMilliseconds } from '@backstage/types';\nimport { RequestHandler } from 'express';\n\nexport const DEFAULT_STARTUP_REQUEST_PAUSE_TIMEOUT = { seconds: 5 };\nexport const DEFAULT_SERVER_SHUTDOWN_TIMEOUT = { seconds: 0 };\n\n/**\n * Options for {@link createLifecycleMiddleware}.\n * @public\n */\nexport interface LifecycleMiddlewareOptions {\n lifecycle: RootLifecycleService;\n /**\n * The maximum time that paused requests will wait for the service to start, before returning an error.\n *\n * Defaults to 5 seconds.\n */\n startupRequestPauseTimeout?: HumanDuration;\n /**\n * The maximum time that the server will wait for stop accepting traffic, before returning an error.\n *\n * Defaults to 0 seconds.\n */\n serverShutdownDelay?: HumanDuration;\n}\n\n/**\n * Creates a middleware that pauses requests until the service has started.\n *\n * @remarks\n *\n * Requests that arrive before the service has started will be paused until startup is complete.\n * If the service does not start within the provided timeout, the request will be rejected with a\n * {@link @backstage/errors#ServiceUnavailableError}.\n *\n * If the service is shutting down, all requests will be rejected with a\n * {@link @backstage/errors#ServiceUnavailableError}.\n *\n * @public\n */\nexport function createLifecycleMiddleware(\n options: LifecycleMiddlewareOptions,\n): RequestHandler {\n const { lifecycle, startupRequestPauseTimeout, serverShutdownDelay } =\n options;\n\n let state: 'init' | 'up' | 'down' = 'init';\n const waiting = new Set<{\n next: (err?: Error) => void;\n timeout: NodeJS.Timeout;\n }>();\n\n lifecycle.addStartupHook(async () => {\n if (state === 'init') {\n state = 'up';\n for (const item of waiting) {\n clearTimeout(item.timeout);\n item.next();\n }\n waiting.clear();\n }\n });\n\n lifecycle.addBeforeShutdownHook(async () => {\n const timeoutMs = durationToMilliseconds(\n serverShutdownDelay ?? DEFAULT_SERVER_SHUTDOWN_TIMEOUT,\n );\n return await new Promise(resolve => {\n setTimeout(resolve, timeoutMs);\n });\n });\n\n lifecycle.addShutdownHook(async () => {\n state = 'down';\n for (const item of waiting) {\n clearTimeout(item.timeout);\n item.next(new ServiceUnavailableError('Service is shutting down'));\n }\n waiting.clear();\n });\n\n return (_req, _res, next) => {\n if (state === 'up') {\n next();\n return;\n } else if (state === 'down') {\n next(new ServiceUnavailableError('Service is shutting down'));\n return;\n }\n\n const timeoutMs = durationToMilliseconds(\n startupRequestPauseTimeout ?? DEFAULT_STARTUP_REQUEST_PAUSE_TIMEOUT,\n );\n\n const item = {\n next,\n timeout: setTimeout(() => {\n if (waiting.delete(item)) {\n next(new ServiceUnavailableError('Service has not started up yet'));\n }\n }, timeoutMs),\n };\n\n waiting.add(item);\n };\n}\n"],"names":["durationToMilliseconds","ServiceUnavailableError"],"mappings":";;;;;AAqBa,MAAA,qCAAA,GAAwC,EAAE,OAAA,EAAS,CAAE;AACrD,MAAA,+BAAA,GAAkC,EAAE,OAAA,EAAS,CAAE;AAoCrD,SAAS,0BACd,OACgB,EAAA;AAChB,EAAA,MAAM,EAAE,SAAA,EAAW,0BAA4B,EAAA,mBAAA,EAC7C,GAAA,OAAA;AAEF,EAAA,IAAI,KAAgC,GAAA,MAAA;AACpC,EAAM,MAAA,OAAA,uBAAc,GAGjB,EAAA;AAEH,EAAA,SAAA,CAAU,eAAe,YAAY;AACnC,IAAA,IAAI,UAAU,MAAQ,EAAA;AACpB,MAAQ,KAAA,GAAA,IAAA;AACR,MAAA,KAAA,MAAW,QAAQ,OAAS,EAAA;AAC1B,QAAA,YAAA,CAAa,KAAK,OAAO,CAAA;AACzB,QAAA,IAAA,CAAK,IAAK,EAAA;AAAA;AAEZ,MAAA,OAAA,CAAQ,KAAM,EAAA;AAAA;AAChB,GACD,CAAA;AAED,EAAA,SAAA,CAAU,sBAAsB,YAAY;AAC1C,IAAA,MAAM,SAAY,GAAAA,4BAAA;AAAA,MAChB,mBAAuB,IAAA;AAAA,KACzB;AACA,IAAO,OAAA,MAAM,IAAI,OAAA,CAAQ,CAAW,OAAA,KAAA;AAClC,MAAA,UAAA,CAAW,SAAS,SAAS,CAAA;AAAA,KAC9B,CAAA;AAAA,GACF,CAAA;AAED,EAAA,SAAA,CAAU,gBAAgB,YAAY;AACpC,IAAQ,KAAA,GAAA,MAAA;AACR,IAAA,KAAA,MAAW,QAAQ,OAAS,EAAA;AAC1B,MAAA,YAAA,CAAa,KAAK,OAAO,CAAA;AACzB,MAAA,IAAA,CAAK,IAAK,CAAA,IAAIC,8BAAwB,CAAA,0BAA0B,CAAC,CAAA;AAAA;AAEnE,IAAA,OAAA,CAAQ,KAAM,EAAA;AAAA,GACf,CAAA;AAED,EAAO,OAAA,CAAC,IAAM,EAAA,IAAA,EAAM,IAAS,KAAA;AAC3B,IAAA,IAAI,UAAU,IAAM,EAAA;AAClB,MAAK,IAAA,EAAA;AACL,MAAA;AAAA,KACF,MAAA,IAAW,UAAU,MAAQ,EAAA;AAC3B,MAAK,IAAA,CAAA,IAAIA,8BAAwB,CAAA,0BAA0B,CAAC,CAAA;AAC5D,MAAA;AAAA;AAGF,IAAA,MAAM,SAAY,GAAAD,4BAAA;AAAA,MAChB,0BAA8B,IAAA;AAAA,KAChC;AAEA,IAAA,MAAM,IAAO,GAAA;AAAA,MACX,IAAA;AAAA,MACA,OAAA,EAAS,WAAW,MAAM;AACxB,QAAI,IAAA,OAAA,CAAQ,MAAO,CAAA,IAAI,CAAG,EAAA;AACxB,UAAK,IAAA,CAAA,IAAIC,8BAAwB,CAAA,gCAAgC,CAAC,CAAA;AAAA;AACpE,SACC,SAAS;AAAA,KACd;AAEA,IAAA,OAAA,CAAQ,IAAI,IAAI,CAAA;AAAA,GAClB;AACF;;;;;;"}
|