@backstage/backend-defaults 0.17.2-next.0 → 0.17.2
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 +2 -10
- package/dist/entrypoints/urlReader/lib/AzureBlobStorageUrlReader.cjs.js.map +1 -1
- package/dist/entrypoints/urlReader/lib/GitlabUrlReader.cjs.js +4 -0
- package/dist/entrypoints/urlReader/lib/GitlabUrlReader.cjs.js.map +1 -1
- package/dist/lib/rateLimitMiddleware.cjs.js +4 -2
- package/dist/lib/rateLimitMiddleware.cjs.js.map +1 -1
- package/dist/package.json.cjs.js +1 -1
- package/dist/urlReader.d.ts +2 -2
- package/package.json +17 -17
package/CHANGELOG.md
CHANGED
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
# @backstage/backend-defaults
|
|
2
2
|
|
|
3
|
-
## 0.17.2
|
|
3
|
+
## 0.17.2
|
|
4
4
|
|
|
5
5
|
### Patch Changes
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
- def82d4: Fixed the built-in rate limiter throwing a validation error and refusing to start when `backend.rateLimit` is enabled. Requests are now keyed using the address normalization helper from `express-rate-limit`, which is required by newer versions of that library and ensures IPv6 clients are grouped by their address block rather than by individual address.
|
|
9
|
-
- Updated dependencies
|
|
10
|
-
- @backstage/integration@2.0.3-next.0
|
|
11
|
-
- @backstage/plugin-auth-node@0.7.2-next.0
|
|
12
|
-
- @backstage/backend-app-api@1.7.1-next.0
|
|
13
|
-
- @backstage/plugin-permission-node@0.11.1-next.0
|
|
14
|
-
- @backstage/backend-plugin-api@1.9.2-next.0
|
|
15
|
-
- @backstage/plugin-events-node@0.4.23-next.0
|
|
7
|
+
- eb80f4f: Fix gitlabUrlReader issue with retrieving the repository archive tree
|
|
16
8
|
|
|
17
9
|
## 0.17.1
|
|
18
10
|
|
|
@@ -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 { toError, ForwardedError, NotModifiedError } from '@backstage/errors';\nimport { Readable } from 'node:stream';\nimport { relative } from 'node:path/posix';\nimport { ReadUrlResponseFactory } from './ReadUrlResponseFactory';\nimport {\n AzureBlobStorageIntegration,\n AzureCredentialsManager,\n DefaultAzureCredentialsManager,\n ScmIntegrations,\n} from '@backstage/integration';\nimport {\n UrlReaderService,\n UrlReaderServiceReadTreeOptions,\n UrlReaderServiceReadTreeResponse,\n UrlReaderServiceReadUrlOptions,\n UrlReaderServiceReadUrlResponse,\n UrlReaderServiceSearchOptions,\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 private readonly credsManager: AzureCredentialsManager;\n private readonly integration: AzureBlobStorageIntegration;\n private readonly deps: {\n treeResponseFactory: ReadTreeResponseFactory;\n };\n\n constructor(\n credsManager: AzureCredentialsManager,\n integration: AzureBlobStorageIntegration,\n deps: {\n treeResponseFactory: ReadTreeResponseFactory;\n },\n ) {\n this.credsManager = credsManager;\n this.integration = integration;\n this.deps = deps;\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 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\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(\n url: string,\n options?: UrlReaderServiceSearchOptions,\n ): Promise<UrlReaderServiceSearchResponse> {\n const { path } = parseUrl(url);\n\n if (path.match(/[*?]/)) {\n throw new Error(\n 'Glob search pattern not implemented for AzureBlobStorageUrlReader',\n );\n }\n\n try {\n const data = await this.readUrl(url, options);\n\n return {\n files: [\n {\n url: url,\n content: data.buffer,\n lastModifiedAt: data.lastModifiedAt,\n },\n ],\n etag: data.etag ?? '',\n };\n } catch (e) {\n throw toError(e);\n }\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","toError"],"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 { toError, ForwardedError, NotModifiedError } from '@backstage/errors';\nimport { Readable } from 'node:stream';\nimport { relative } from 'node: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 UrlReaderServiceSearchOptions,\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 private readonly credsManager: AzureCredentialsManager;\n private readonly integration: AzureBlobStorageIntergation;\n private readonly deps: {\n treeResponseFactory: ReadTreeResponseFactory;\n };\n\n constructor(\n credsManager: AzureCredentialsManager,\n integration: AzureBlobStorageIntergation,\n deps: {\n treeResponseFactory: ReadTreeResponseFactory;\n },\n ) {\n this.credsManager = credsManager;\n this.integration = integration;\n this.deps = deps;\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 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\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(\n url: string,\n options?: UrlReaderServiceSearchOptions,\n ): Promise<UrlReaderServiceSearchResponse> {\n const { path } = parseUrl(url);\n\n if (path.match(/[*?]/)) {\n throw new Error(\n 'Glob search pattern not implemented for AzureBlobStorageUrlReader',\n );\n }\n\n try {\n const data = await this.readUrl(url, options);\n\n return {\n files: [\n {\n url: url,\n content: data.buffer,\n lastModifiedAt: data.lastModifiedAt,\n },\n ],\n etag: data.etag ?? '',\n };\n } catch (e) {\n throw toError(e);\n }\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","toError"],"mappings":"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"}
|
|
@@ -140,6 +140,10 @@ class GitlabUrlReader {
|
|
|
140
140
|
)}/repository/archive?${archiveReqParams.toString()}`;
|
|
141
141
|
const archiveGitLabResponse = await this.integration.fetch(reqUrl, {
|
|
142
142
|
...integration.getGitLabRequestOptions(this.integration.config, token),
|
|
143
|
+
// The mode is set to 'same-origin' to overwrite the default 'cors' value.
|
|
144
|
+
// The repository/archive endpoint marks mode='cors' as a "hotlink" which will return 406 - Not Acceptable as a response
|
|
145
|
+
// More info on this issue can be found @ https://github.com/backstage/backstage/issues/34395
|
|
146
|
+
mode: "same-origin",
|
|
143
147
|
// TODO(freben): The signal cast is there because pre-3.x versions of
|
|
144
148
|
// node-fetch have a very slightly deviating AbortSignal type signature.
|
|
145
149
|
// The difference does not affect us in practice however. The cast can
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GitlabUrlReader.cjs.js","sources":["../../../../src/entrypoints/urlReader/lib/GitlabUrlReader.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 {\n UrlReaderService,\n UrlReaderServiceReadTreeOptions,\n UrlReaderServiceReadTreeResponse,\n UrlReaderServiceReadUrlOptions,\n UrlReaderServiceReadUrlResponse,\n UrlReaderServiceSearchOptions,\n UrlReaderServiceSearchResponse,\n} from '@backstage/backend-plugin-api';\nimport { toError, NotFoundError, NotModifiedError } from '@backstage/errors';\nimport {\n getGitLabFileFetchUrl,\n getGitLabIntegrationRelativePath,\n getGitLabRequestOptions,\n GitLabIntegration,\n ScmIntegrations,\n} from '@backstage/integration';\nimport parseGitUrl from 'git-url-parse';\nimport { trimEnd, trimStart } from 'lodash';\nimport { Minimatch } from 'minimatch';\nimport { ReadUrlResponseFactory } from './ReadUrlResponseFactory';\nimport { ReaderFactory, ReadTreeResponseFactory } from './types';\n\n/**\n * Implements a {@link @backstage/backend-plugin-api#UrlReaderService} for files on GitLab.\n *\n * @public\n */\nexport class GitlabUrlReader implements UrlReaderService {\n static factory: ReaderFactory = ({ config, treeResponseFactory }) => {\n const integrations = ScmIntegrations.fromConfig(config);\n return integrations.gitlab.list().map(integration => {\n const reader = new GitlabUrlReader(integration, {\n treeResponseFactory,\n });\n const predicate = (url: URL) => url.host === integration.config.host;\n return { reader, predicate };\n });\n };\n\n private readonly integration: GitLabIntegration;\n private readonly deps: { treeResponseFactory: ReadTreeResponseFactory };\n\n constructor(\n integration: GitLabIntegration,\n deps: { treeResponseFactory: ReadTreeResponseFactory },\n ) {\n this.integration = integration;\n this.deps = deps;\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, signal, token } = options ?? {};\n const isArtifact = url.includes('/-/jobs/artifacts/');\n const builtUrl = await this.getGitlabFetchUrl(url, token);\n\n let response: Response;\n try {\n response = await this.integration.fetch(builtUrl, {\n headers: {\n ...getGitLabRequestOptions(this.integration.config, token).headers,\n ...(etag && !isArtifact && { 'If-None-Match': etag }),\n ...(lastModifiedAfter &&\n !isArtifact && {\n 'If-Modified-Since': lastModifiedAfter.toUTCString(),\n }),\n },\n // TODO(freben): The signal cast is there because pre-3.x versions of\n // node-fetch have a very slightly deviating AbortSignal type signature.\n // The difference does not affect us in practice however. The cast can be\n // removed after we support ESM for CLI dependencies and migrate to\n // version 3 of node-fetch.\n // https://github.com/backstage/backstage/issues/8242\n ...(signal && { signal: signal as any }),\n });\n } catch (e) {\n throw new Error(`Unable to read ${url}, ${e}`);\n }\n\n if (response.status === 304) {\n throw new NotModifiedError();\n }\n\n if (response.ok) {\n return ReadUrlResponseFactory.fromResponse(response);\n }\n\n const message = `${url} could not be read as ${builtUrl}, ${response.status} ${response.statusText}`;\n if (response.status === 404) {\n throw new NotFoundError(message);\n }\n throw new Error(message);\n }\n\n async readTree(\n url: string,\n options?: UrlReaderServiceReadTreeOptions,\n ): Promise<UrlReaderServiceReadTreeResponse> {\n const { etag, signal, token } = options ?? {};\n const { ref, full_name, filepath } = parseGitUrl(url);\n\n let repoFullName = full_name;\n\n const relativePath = getGitLabIntegrationRelativePath(\n this.integration.config,\n );\n\n // Considering self hosted gitlab with relative\n // assuming '/gitlab' is the relative path\n // from: /gitlab/repo/project\n // to: repo/project\n if (relativePath) {\n const rectifiedRelativePath = `${trimStart(relativePath, '/')}/`;\n repoFullName = full_name.replace(rectifiedRelativePath, '');\n }\n\n // Use GitLab API to get the default branch\n // encodeURIComponent is required for GitLab API\n // https://docs.gitlab.com/ee/api/README.html#namespaced-path-encoding\n const projectGitlabResponse = await this.integration.fetch(\n new URL(\n `${this.integration.config.apiBaseUrl}/projects/${encodeURIComponent(\n repoFullName,\n )}`,\n ).toString(),\n getGitLabRequestOptions(this.integration.config, token),\n );\n if (!projectGitlabResponse.ok) {\n const msg = `Failed to read tree from ${url}, ${projectGitlabResponse.status} ${projectGitlabResponse.statusText}`;\n if (projectGitlabResponse.status === 404) {\n throw new NotFoundError(msg);\n }\n throw new Error(msg);\n }\n const projectGitlabResponseJson = await projectGitlabResponse.json();\n\n // ref is an empty string if no branch is set in provided url to readTree.\n const branch = ref || projectGitlabResponseJson.default_branch;\n\n // Fetch the latest commit that modifies the filepath in the provided or default branch\n // to compare against the provided sha.\n const commitsReqParams = new URLSearchParams();\n commitsReqParams.set('ref_name', branch);\n if (!!filepath) {\n commitsReqParams.set('path', filepath);\n }\n const commitsGitlabResponse = await this.integration.fetch(\n new URL(\n `${this.integration.config.apiBaseUrl}/projects/${encodeURIComponent(\n repoFullName,\n )}/repository/commits?${commitsReqParams.toString()}`,\n ).toString(),\n {\n ...getGitLabRequestOptions(this.integration.config, token),\n // TODO(freben): The signal cast is there because pre-3.x versions of\n // node-fetch have a very slightly deviating AbortSignal type signature.\n // The difference does not affect us in practice however. The cast can\n // be removed after we support ESM for CLI dependencies and migrate to\n // version 3 of node-fetch.\n // https://github.com/backstage/backstage/issues/8242\n ...(signal && { signal: signal as any }),\n },\n );\n if (!commitsGitlabResponse.ok) {\n const message = `Failed to read tree (branch) from ${url}, ${commitsGitlabResponse.status} ${commitsGitlabResponse.statusText}`;\n if (commitsGitlabResponse.status === 404) {\n throw new NotFoundError(message);\n }\n throw new Error(message);\n }\n\n const commitSha = (await commitsGitlabResponse.json())[0]?.id ?? '';\n if (etag && etag === commitSha) {\n throw new NotModifiedError();\n }\n\n const archiveReqParams = new URLSearchParams();\n archiveReqParams.set('sha', branch);\n if (!!filepath) {\n archiveReqParams.set('path', filepath);\n }\n // https://docs.gitlab.com/ee/api/repositories.html#get-file-archive\n const reqUrl = `${\n this.integration.config.apiBaseUrl\n }/projects/${encodeURIComponent(\n repoFullName,\n )}/repository/archive?${archiveReqParams.toString()}`;\n const archiveGitLabResponse = await this.integration.fetch(reqUrl, {\n ...getGitLabRequestOptions(this.integration.config, token),\n // TODO(freben): The signal cast is there because pre-3.x versions of\n // node-fetch have a very slightly deviating AbortSignal type signature.\n // The difference does not affect us in practice however. The cast can\n // be removed after we support ESM for CLI dependencies and migrate to\n // version 3 of node-fetch.\n // https://github.com/backstage/backstage/issues/8242\n ...(signal && { signal: signal as any }),\n });\n if (!archiveGitLabResponse.ok) {\n const message = `Failed to read tree (archive) from ${url}, ${reqUrl}, ${archiveGitLabResponse.status} ${archiveGitLabResponse.statusText}`;\n if (archiveGitLabResponse.status === 404) {\n throw new NotFoundError(message);\n }\n throw new Error(message);\n }\n\n return await this.deps.treeResponseFactory.fromTarArchive({\n response: archiveGitLabResponse,\n subpath: filepath,\n etag: commitSha,\n filter: options?.filter,\n });\n }\n\n async search(\n url: string,\n options?: UrlReaderServiceSearchOptions,\n ): Promise<UrlReaderServiceSearchResponse> {\n const { filepath } = parseGitUrl(url);\n\n // If it's a direct URL we use readUrl instead\n if (!filepath?.match(/[*?]/)) {\n try {\n const data = await this.readUrl(url, options);\n\n return {\n files: [\n {\n url: url,\n content: data.buffer,\n lastModifiedAt: data.lastModifiedAt,\n },\n ],\n etag: data.etag ?? '',\n };\n } catch (e) {\n const error = toError(e);\n if (error.name === 'NotFoundError') {\n return {\n files: [],\n etag: '',\n };\n }\n throw error;\n }\n }\n\n const staticPart = this.getStaticPart(filepath);\n const matcher = new Minimatch(filepath);\n const treeUrl = trimEnd(url.replace(filepath, staticPart), `/`);\n const pathPrefix = staticPart ? `${staticPart}/` : '';\n const tree = await this.readTree(treeUrl, {\n etag: options?.etag,\n signal: options?.signal,\n filter: path => matcher.match(`${pathPrefix}${path}`),\n });\n\n const files = await tree.files();\n return {\n etag: tree.etag,\n files: files.map(file => ({\n url: this.integration.resolveUrl({\n url: `/${pathPrefix}${file.path}`,\n base: url,\n }),\n content: file.content,\n lastModifiedAt: file.lastModifiedAt,\n })),\n };\n }\n\n /**\n * This function splits the input globPattern string into segments using the path separator /. It then iterates over\n * the segments from the end of the array towards the beginning, checking if the concatenated string up to that\n * segment matches the original globPattern using the minimatch function. If a match is found, it continues iterating.\n * If no match is found, it returns the concatenated string up to the current segment, which is the static part of the\n * glob pattern.\n *\n * E.g. `catalog/foo/*.yaml` will return `catalog/foo`.\n *\n * @param globPattern - the glob pattern\n */\n private getStaticPart(globPattern: string) {\n const segments = globPattern.split('/');\n const globIndex = segments.findIndex(segment => segment.match(/[*?]/));\n return globIndex === -1\n ? globPattern\n : segments.slice(0, globIndex).join('/');\n }\n\n toString() {\n const { host, token } = this.integration.config;\n return `gitlab{host=${host},authed=${Boolean(token)}}`;\n }\n\n private async getGitlabFetchUrl(\n target: string,\n token?: string,\n ): Promise<string> {\n // If the target is for a job artifact then go down that path\n const targetUrl = new URL(target);\n if (targetUrl.pathname.includes('/-/jobs/artifacts/')) {\n return this.getGitlabArtifactFetchUrl(targetUrl).then(value =>\n value.toString(),\n );\n }\n // Default to the optimized behavior - no API call needed for file URLs\n return getGitLabFileFetchUrl(target, this.integration.config, token);\n }\n\n // convert urls of the form:\n // https://example.com/<namespace>/<project>/-/jobs/artifacts/<ref>/raw/<path_to_file>?job=<job_name>\n // to urls of the form:\n // https://example.com/api/v4/projects/namespace%2Fproject/jobs/artifacts/:ref_name/raw/*artifact_path?job=<job_name>\n private getGitlabArtifactFetchUrl(target: URL): Promise<URL> {\n if (!target.pathname.includes('/-/jobs/artifacts/')) {\n throw new Error('Unable to process url as an GitLab artifact');\n }\n try {\n const [namespaceAndProject, ref] =\n target.pathname.split('/-/jobs/artifacts/');\n\n // Extract project path directly instead of making API call\n const relativePath = getGitLabIntegrationRelativePath(\n this.integration.config,\n );\n\n let projectPath = namespaceAndProject;\n // Check relative path exist and remove it\n if (relativePath) {\n projectPath = trimStart(projectPath, relativePath);\n }\n // Trim an initial / if it exists\n projectPath = projectPath.replace(/^\\//, '');\n\n const newUrl = new URL(target);\n newUrl.pathname = `${relativePath}/api/v4/projects/${encodeURIComponent(\n projectPath,\n )}/jobs/artifacts/${ref}`;\n return Promise.resolve(newUrl);\n } catch (e) {\n throw new Error(\n `Unable to translate GitLab artifact URL: ${target}, ${e}`,\n );\n }\n }\n}\n"],"names":["ScmIntegrations","getGitLabRequestOptions","NotModifiedError","ReadUrlResponseFactory","NotFoundError","parseGitUrl","getGitLabIntegrationRelativePath","trimStart","toError","Minimatch","trimEnd","getGitLabFileFetchUrl"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"GitlabUrlReader.cjs.js","sources":["../../../../src/entrypoints/urlReader/lib/GitlabUrlReader.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 {\n UrlReaderService,\n UrlReaderServiceReadTreeOptions,\n UrlReaderServiceReadTreeResponse,\n UrlReaderServiceReadUrlOptions,\n UrlReaderServiceReadUrlResponse,\n UrlReaderServiceSearchOptions,\n UrlReaderServiceSearchResponse,\n} from '@backstage/backend-plugin-api';\nimport { toError, NotFoundError, NotModifiedError } from '@backstage/errors';\nimport {\n getGitLabFileFetchUrl,\n getGitLabIntegrationRelativePath,\n getGitLabRequestOptions,\n GitLabIntegration,\n ScmIntegrations,\n} from '@backstage/integration';\nimport parseGitUrl from 'git-url-parse';\nimport { trimEnd, trimStart } from 'lodash';\nimport { Minimatch } from 'minimatch';\nimport { ReadUrlResponseFactory } from './ReadUrlResponseFactory';\nimport { ReaderFactory, ReadTreeResponseFactory } from './types';\n\n/**\n * Implements a {@link @backstage/backend-plugin-api#UrlReaderService} for files on GitLab.\n *\n * @public\n */\nexport class GitlabUrlReader implements UrlReaderService {\n static factory: ReaderFactory = ({ config, treeResponseFactory }) => {\n const integrations = ScmIntegrations.fromConfig(config);\n return integrations.gitlab.list().map(integration => {\n const reader = new GitlabUrlReader(integration, {\n treeResponseFactory,\n });\n const predicate = (url: URL) => url.host === integration.config.host;\n return { reader, predicate };\n });\n };\n\n private readonly integration: GitLabIntegration;\n private readonly deps: { treeResponseFactory: ReadTreeResponseFactory };\n\n constructor(\n integration: GitLabIntegration,\n deps: { treeResponseFactory: ReadTreeResponseFactory },\n ) {\n this.integration = integration;\n this.deps = deps;\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, signal, token } = options ?? {};\n const isArtifact = url.includes('/-/jobs/artifacts/');\n const builtUrl = await this.getGitlabFetchUrl(url, token);\n\n let response: Response;\n try {\n response = await this.integration.fetch(builtUrl, {\n headers: {\n ...getGitLabRequestOptions(this.integration.config, token).headers,\n ...(etag && !isArtifact && { 'If-None-Match': etag }),\n ...(lastModifiedAfter &&\n !isArtifact && {\n 'If-Modified-Since': lastModifiedAfter.toUTCString(),\n }),\n },\n // TODO(freben): The signal cast is there because pre-3.x versions of\n // node-fetch have a very slightly deviating AbortSignal type signature.\n // The difference does not affect us in practice however. The cast can be\n // removed after we support ESM for CLI dependencies and migrate to\n // version 3 of node-fetch.\n // https://github.com/backstage/backstage/issues/8242\n ...(signal && { signal: signal as any }),\n });\n } catch (e) {\n throw new Error(`Unable to read ${url}, ${e}`);\n }\n\n if (response.status === 304) {\n throw new NotModifiedError();\n }\n\n if (response.ok) {\n return ReadUrlResponseFactory.fromResponse(response);\n }\n\n const message = `${url} could not be read as ${builtUrl}, ${response.status} ${response.statusText}`;\n if (response.status === 404) {\n throw new NotFoundError(message);\n }\n throw new Error(message);\n }\n\n async readTree(\n url: string,\n options?: UrlReaderServiceReadTreeOptions,\n ): Promise<UrlReaderServiceReadTreeResponse> {\n const { etag, signal, token } = options ?? {};\n const { ref, full_name, filepath } = parseGitUrl(url);\n\n let repoFullName = full_name;\n\n const relativePath = getGitLabIntegrationRelativePath(\n this.integration.config,\n );\n\n // Considering self hosted gitlab with relative\n // assuming '/gitlab' is the relative path\n // from: /gitlab/repo/project\n // to: repo/project\n if (relativePath) {\n const rectifiedRelativePath = `${trimStart(relativePath, '/')}/`;\n repoFullName = full_name.replace(rectifiedRelativePath, '');\n }\n\n // Use GitLab API to get the default branch\n // encodeURIComponent is required for GitLab API\n // https://docs.gitlab.com/ee/api/README.html#namespaced-path-encoding\n const projectGitlabResponse = await this.integration.fetch(\n new URL(\n `${this.integration.config.apiBaseUrl}/projects/${encodeURIComponent(\n repoFullName,\n )}`,\n ).toString(),\n getGitLabRequestOptions(this.integration.config, token),\n );\n if (!projectGitlabResponse.ok) {\n const msg = `Failed to read tree from ${url}, ${projectGitlabResponse.status} ${projectGitlabResponse.statusText}`;\n if (projectGitlabResponse.status === 404) {\n throw new NotFoundError(msg);\n }\n throw new Error(msg);\n }\n const projectGitlabResponseJson = await projectGitlabResponse.json();\n\n // ref is an empty string if no branch is set in provided url to readTree.\n const branch = ref || projectGitlabResponseJson.default_branch;\n\n // Fetch the latest commit that modifies the filepath in the provided or default branch\n // to compare against the provided sha.\n const commitsReqParams = new URLSearchParams();\n commitsReqParams.set('ref_name', branch);\n if (!!filepath) {\n commitsReqParams.set('path', filepath);\n }\n const commitsGitlabResponse = await this.integration.fetch(\n new URL(\n `${this.integration.config.apiBaseUrl}/projects/${encodeURIComponent(\n repoFullName,\n )}/repository/commits?${commitsReqParams.toString()}`,\n ).toString(),\n {\n ...getGitLabRequestOptions(this.integration.config, token),\n // TODO(freben): The signal cast is there because pre-3.x versions of\n // node-fetch have a very slightly deviating AbortSignal type signature.\n // The difference does not affect us in practice however. The cast can\n // be removed after we support ESM for CLI dependencies and migrate to\n // version 3 of node-fetch.\n // https://github.com/backstage/backstage/issues/8242\n ...(signal && { signal: signal as any }),\n },\n );\n if (!commitsGitlabResponse.ok) {\n const message = `Failed to read tree (branch) from ${url}, ${commitsGitlabResponse.status} ${commitsGitlabResponse.statusText}`;\n if (commitsGitlabResponse.status === 404) {\n throw new NotFoundError(message);\n }\n throw new Error(message);\n }\n\n const commitSha = (await commitsGitlabResponse.json())[0]?.id ?? '';\n if (etag && etag === commitSha) {\n throw new NotModifiedError();\n }\n\n const archiveReqParams = new URLSearchParams();\n archiveReqParams.set('sha', branch);\n if (!!filepath) {\n archiveReqParams.set('path', filepath);\n }\n // https://docs.gitlab.com/ee/api/repositories.html#get-file-archive\n const reqUrl = `${\n this.integration.config.apiBaseUrl\n }/projects/${encodeURIComponent(\n repoFullName,\n )}/repository/archive?${archiveReqParams.toString()}`;\n const archiveGitLabResponse = await this.integration.fetch(reqUrl, {\n ...getGitLabRequestOptions(this.integration.config, token),\n // The mode is set to 'same-origin' to overwrite the default 'cors' value.\n // The repository/archive endpoint marks mode='cors' as a \"hotlink\" which will return 406 - Not Acceptable as a response\n // More info on this issue can be found @ https://github.com/backstage/backstage/issues/34395\n mode: 'same-origin',\n // TODO(freben): The signal cast is there because pre-3.x versions of\n // node-fetch have a very slightly deviating AbortSignal type signature.\n // The difference does not affect us in practice however. The cast can\n // be removed after we support ESM for CLI dependencies and migrate to\n // version 3 of node-fetch.\n // https://github.com/backstage/backstage/issues/8242\n ...(signal && { signal: signal as any }),\n });\n if (!archiveGitLabResponse.ok) {\n const message = `Failed to read tree (archive) from ${url}, ${reqUrl}, ${archiveGitLabResponse.status} ${archiveGitLabResponse.statusText}`;\n if (archiveGitLabResponse.status === 404) {\n throw new NotFoundError(message);\n }\n throw new Error(message);\n }\n\n return await this.deps.treeResponseFactory.fromTarArchive({\n response: archiveGitLabResponse,\n subpath: filepath,\n etag: commitSha,\n filter: options?.filter,\n });\n }\n\n async search(\n url: string,\n options?: UrlReaderServiceSearchOptions,\n ): Promise<UrlReaderServiceSearchResponse> {\n const { filepath } = parseGitUrl(url);\n\n // If it's a direct URL we use readUrl instead\n if (!filepath?.match(/[*?]/)) {\n try {\n const data = await this.readUrl(url, options);\n\n return {\n files: [\n {\n url: url,\n content: data.buffer,\n lastModifiedAt: data.lastModifiedAt,\n },\n ],\n etag: data.etag ?? '',\n };\n } catch (e) {\n const error = toError(e);\n if (error.name === 'NotFoundError') {\n return {\n files: [],\n etag: '',\n };\n }\n throw error;\n }\n }\n\n const staticPart = this.getStaticPart(filepath);\n const matcher = new Minimatch(filepath);\n const treeUrl = trimEnd(url.replace(filepath, staticPart), `/`);\n const pathPrefix = staticPart ? `${staticPart}/` : '';\n const tree = await this.readTree(treeUrl, {\n etag: options?.etag,\n signal: options?.signal,\n filter: path => matcher.match(`${pathPrefix}${path}`),\n });\n\n const files = await tree.files();\n return {\n etag: tree.etag,\n files: files.map(file => ({\n url: this.integration.resolveUrl({\n url: `/${pathPrefix}${file.path}`,\n base: url,\n }),\n content: file.content,\n lastModifiedAt: file.lastModifiedAt,\n })),\n };\n }\n\n /**\n * This function splits the input globPattern string into segments using the path separator /. It then iterates over\n * the segments from the end of the array towards the beginning, checking if the concatenated string up to that\n * segment matches the original globPattern using the minimatch function. If a match is found, it continues iterating.\n * If no match is found, it returns the concatenated string up to the current segment, which is the static part of the\n * glob pattern.\n *\n * E.g. `catalog/foo/*.yaml` will return `catalog/foo`.\n *\n * @param globPattern - the glob pattern\n */\n private getStaticPart(globPattern: string) {\n const segments = globPattern.split('/');\n const globIndex = segments.findIndex(segment => segment.match(/[*?]/));\n return globIndex === -1\n ? globPattern\n : segments.slice(0, globIndex).join('/');\n }\n\n toString() {\n const { host, token } = this.integration.config;\n return `gitlab{host=${host},authed=${Boolean(token)}}`;\n }\n\n private async getGitlabFetchUrl(\n target: string,\n token?: string,\n ): Promise<string> {\n // If the target is for a job artifact then go down that path\n const targetUrl = new URL(target);\n if (targetUrl.pathname.includes('/-/jobs/artifacts/')) {\n return this.getGitlabArtifactFetchUrl(targetUrl).then(value =>\n value.toString(),\n );\n }\n // Default to the optimized behavior - no API call needed for file URLs\n return getGitLabFileFetchUrl(target, this.integration.config, token);\n }\n\n // convert urls of the form:\n // https://example.com/<namespace>/<project>/-/jobs/artifacts/<ref>/raw/<path_to_file>?job=<job_name>\n // to urls of the form:\n // https://example.com/api/v4/projects/namespace%2Fproject/jobs/artifacts/:ref_name/raw/*artifact_path?job=<job_name>\n private getGitlabArtifactFetchUrl(target: URL): Promise<URL> {\n if (!target.pathname.includes('/-/jobs/artifacts/')) {\n throw new Error('Unable to process url as an GitLab artifact');\n }\n try {\n const [namespaceAndProject, ref] =\n target.pathname.split('/-/jobs/artifacts/');\n\n // Extract project path directly instead of making API call\n const relativePath = getGitLabIntegrationRelativePath(\n this.integration.config,\n );\n\n let projectPath = namespaceAndProject;\n // Check relative path exist and remove it\n if (relativePath) {\n projectPath = trimStart(projectPath, relativePath);\n }\n // Trim an initial / if it exists\n projectPath = projectPath.replace(/^\\//, '');\n\n const newUrl = new URL(target);\n newUrl.pathname = `${relativePath}/api/v4/projects/${encodeURIComponent(\n projectPath,\n )}/jobs/artifacts/${ref}`;\n return Promise.resolve(newUrl);\n } catch (e) {\n throw new Error(\n `Unable to translate GitLab artifact URL: ${target}, ${e}`,\n );\n }\n }\n}\n"],"names":["ScmIntegrations","getGitLabRequestOptions","NotModifiedError","ReadUrlResponseFactory","NotFoundError","parseGitUrl","getGitLabIntegrationRelativePath","trimStart","toError","Minimatch","trimEnd","getGitLabFileFetchUrl"],"mappings":";;;;;;;;;;;;;AA4CO,MAAM,eAAA,CAA4C;AAAA,EACvD,OAAO,OAAA,GAAyB,CAAC,EAAE,MAAA,EAAQ,qBAAoB,KAAM;AACnE,IAAA,MAAM,YAAA,GAAeA,2BAAA,CAAgB,UAAA,CAAW,MAAM,CAAA;AACtD,IAAA,OAAO,YAAA,CAAa,MAAA,CAAO,IAAA,EAAK,CAAE,IAAI,CAAA,WAAA,KAAe;AACnD,MAAA,MAAM,MAAA,GAAS,IAAI,eAAA,CAAgB,WAAA,EAAa;AAAA,QAC9C;AAAA,OACD,CAAA;AACD,MAAA,MAAM,YAAY,CAAC,GAAA,KAAa,GAAA,CAAI,IAAA,KAAS,YAAY,MAAA,CAAO,IAAA;AAChE,MAAA,OAAO,EAAE,QAAQ,SAAA,EAAU;AAAA,IAC7B,CAAC,CAAA;AAAA,EACH,CAAA;AAAA,EAEiB,WAAA;AAAA,EACA,IAAA;AAAA,EAEjB,WAAA,CACE,aACA,IAAA,EACA;AACA,IAAA,IAAA,CAAK,WAAA,GAAc,WAAA;AACnB,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AAAA,EACd;AAAA,EAEA,MAAM,KAAK,GAAA,EAA8B;AACvC,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,OAAA,CAAQ,GAAG,CAAA;AACvC,IAAA,OAAO,SAAS,MAAA,EAAO;AAAA,EACzB;AAAA,EAEA,MAAM,OAAA,CACJ,GAAA,EACA,OAAA,EAC0C;AAC1C,IAAA,MAAM,EAAE,IAAA,EAAM,iBAAA,EAAmB,QAAQ,KAAA,EAAM,GAAI,WAAW,EAAC;AAC/D,IAAA,MAAM,UAAA,GAAa,GAAA,CAAI,QAAA,CAAS,oBAAoB,CAAA;AACpD,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,iBAAA,CAAkB,KAAK,KAAK,CAAA;AAExD,IAAA,IAAI,QAAA;AACJ,IAAA,IAAI;AACF,MAAA,QAAA,GAAW,MAAM,IAAA,CAAK,WAAA,CAAY,KAAA,CAAM,QAAA,EAAU;AAAA,QAChD,OAAA,EAAS;AAAA,UACP,GAAGC,mCAAA,CAAwB,IAAA,CAAK,WAAA,CAAY,MAAA,EAAQ,KAAK,CAAA,CAAE,OAAA;AAAA,UAC3D,GAAI,IAAA,IAAQ,CAAC,UAAA,IAAc,EAAE,iBAAiB,IAAA,EAAK;AAAA,UACnD,GAAI,iBAAA,IACF,CAAC,UAAA,IAAc;AAAA,YACb,mBAAA,EAAqB,kBAAkB,WAAA;AAAY;AACrD,SACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAOA,GAAI,MAAA,IAAU,EAAE,MAAA;AAAsB,OACvC,CAAA;AAAA,IACH,SAAS,CAAA,EAAG;AACV,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,eAAA,EAAkB,GAAG,CAAA,EAAA,EAAK,CAAC,CAAA,CAAE,CAAA;AAAA,IAC/C;AAEA,IAAA,IAAI,QAAA,CAAS,WAAW,GAAA,EAAK;AAC3B,MAAA,MAAM,IAAIC,uBAAA,EAAiB;AAAA,IAC7B;AAEA,IAAA,IAAI,SAAS,EAAA,EAAI;AACf,MAAA,OAAOC,6CAAA,CAAuB,aAAa,QAAQ,CAAA;AAAA,IACrD;AAEA,IAAA,MAAM,OAAA,GAAU,CAAA,EAAG,GAAG,CAAA,sBAAA,EAAyB,QAAQ,KAAK,QAAA,CAAS,MAAM,CAAA,CAAA,EAAI,QAAA,CAAS,UAAU,CAAA,CAAA;AAClG,IAAA,IAAI,QAAA,CAAS,WAAW,GAAA,EAAK;AAC3B,MAAA,MAAM,IAAIC,qBAAc,OAAO,CAAA;AAAA,IACjC;AACA,IAAA,MAAM,IAAI,MAAM,OAAO,CAAA;AAAA,EACzB;AAAA,EAEA,MAAM,QAAA,CACJ,GAAA,EACA,OAAA,EAC2C;AAC3C,IAAA,MAAM,EAAE,IAAA,EAAM,MAAA,EAAQ,KAAA,EAAM,GAAI,WAAW,EAAC;AAC5C,IAAA,MAAM,EAAE,GAAA,EAAK,SAAA,EAAW,QAAA,EAAS,GAAIC,6BAAY,GAAG,CAAA;AAEpD,IAAA,IAAI,YAAA,GAAe,SAAA;AAEnB,IAAA,MAAM,YAAA,GAAeC,4CAAA;AAAA,MACnB,KAAK,WAAA,CAAY;AAAA,KACnB;AAMA,IAAA,IAAI,YAAA,EAAc;AAChB,MAAA,MAAM,qBAAA,GAAwB,CAAA,EAAGC,gBAAA,CAAU,YAAA,EAAc,GAAG,CAAC,CAAA,CAAA,CAAA;AAC7D,MAAA,YAAA,GAAe,SAAA,CAAU,OAAA,CAAQ,qBAAA,EAAuB,EAAE,CAAA;AAAA,IAC5D;AAKA,IAAA,MAAM,qBAAA,GAAwB,MAAM,IAAA,CAAK,WAAA,CAAY,KAAA;AAAA,MACnD,IAAI,GAAA;AAAA,QACF,CAAA,EAAG,IAAA,CAAK,WAAA,CAAY,MAAA,CAAO,UAAU,CAAA,UAAA,EAAa,kBAAA;AAAA,UAChD;AAAA,SACD,CAAA;AAAA,QACD,QAAA,EAAS;AAAA,MACXN,mCAAA,CAAwB,IAAA,CAAK,WAAA,CAAY,MAAA,EAAQ,KAAK;AAAA,KACxD;AACA,IAAA,IAAI,CAAC,sBAAsB,EAAA,EAAI;AAC7B,MAAA,MAAM,GAAA,GAAM,4BAA4B,GAAG,CAAA,EAAA,EAAK,sBAAsB,MAAM,CAAA,CAAA,EAAI,sBAAsB,UAAU,CAAA,CAAA;AAChH,MAAA,IAAI,qBAAA,CAAsB,WAAW,GAAA,EAAK;AACxC,QAAA,MAAM,IAAIG,qBAAc,GAAG,CAAA;AAAA,MAC7B;AACA,MAAA,MAAM,IAAI,MAAM,GAAG,CAAA;AAAA,IACrB;AACA,IAAA,MAAM,yBAAA,GAA4B,MAAM,qBAAA,CAAsB,IAAA,EAAK;AAGnE,IAAA,MAAM,MAAA,GAAS,OAAO,yBAAA,CAA0B,cAAA;AAIhD,IAAA,MAAM,gBAAA,GAAmB,IAAI,eAAA,EAAgB;AAC7C,IAAA,gBAAA,CAAiB,GAAA,CAAI,YAAY,MAAM,CAAA;AACvC,IAAA,IAAI,CAAC,CAAC,QAAA,EAAU;AACd,MAAA,gBAAA,CAAiB,GAAA,CAAI,QAAQ,QAAQ,CAAA;AAAA,IACvC;AACA,IAAA,MAAM,qBAAA,GAAwB,MAAM,IAAA,CAAK,WAAA,CAAY,KAAA;AAAA,MACnD,IAAI,GAAA;AAAA,QACF,CAAA,EAAG,IAAA,CAAK,WAAA,CAAY,MAAA,CAAO,UAAU,CAAA,UAAA,EAAa,kBAAA;AAAA,UAChD;AAAA,SACD,CAAA,oBAAA,EAAuB,gBAAA,CAAiB,QAAA,EAAU,CAAA;AAAA,QACnD,QAAA,EAAS;AAAA,MACX;AAAA,QACE,GAAGH,mCAAA,CAAwB,IAAA,CAAK,WAAA,CAAY,QAAQ,KAAK,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAOzD,GAAI,MAAA,IAAU,EAAE,MAAA;AAAsB;AACxC,KACF;AACA,IAAA,IAAI,CAAC,sBAAsB,EAAA,EAAI;AAC7B,MAAA,MAAM,OAAA,GAAU,qCAAqC,GAAG,CAAA,EAAA,EAAK,sBAAsB,MAAM,CAAA,CAAA,EAAI,sBAAsB,UAAU,CAAA,CAAA;AAC7H,MAAA,IAAI,qBAAA,CAAsB,WAAW,GAAA,EAAK;AACxC,QAAA,MAAM,IAAIG,qBAAc,OAAO,CAAA;AAAA,MACjC;AACA,MAAA,MAAM,IAAI,MAAM,OAAO,CAAA;AAAA,IACzB;AAEA,IAAA,MAAM,aAAa,MAAM,qBAAA,CAAsB,MAAK,EAAG,CAAC,GAAG,EAAA,IAAM,EAAA;AACjE,IAAA,IAAI,IAAA,IAAQ,SAAS,SAAA,EAAW;AAC9B,MAAA,MAAM,IAAIF,uBAAA,EAAiB;AAAA,IAC7B;AAEA,IAAA,MAAM,gBAAA,GAAmB,IAAI,eAAA,EAAgB;AAC7C,IAAA,gBAAA,CAAiB,GAAA,CAAI,OAAO,MAAM,CAAA;AAClC,IAAA,IAAI,CAAC,CAAC,QAAA,EAAU;AACd,MAAA,gBAAA,CAAiB,GAAA,CAAI,QAAQ,QAAQ,CAAA;AAAA,IACvC;AAEA,IAAA,MAAM,SAAS,CAAA,EACb,IAAA,CAAK,WAAA,CAAY,MAAA,CAAO,UAC1B,CAAA,UAAA,EAAa,kBAAA;AAAA,MACX;AAAA,KACD,CAAA,oBAAA,EAAuB,gBAAA,CAAiB,QAAA,EAAU,CAAA,CAAA;AACnD,IAAA,MAAM,qBAAA,GAAwB,MAAM,IAAA,CAAK,WAAA,CAAY,MAAM,MAAA,EAAQ;AAAA,MACjE,GAAGD,mCAAA,CAAwB,IAAA,CAAK,WAAA,CAAY,QAAQ,KAAK,CAAA;AAAA;AAAA;AAAA;AAAA,MAIzD,IAAA,EAAM,aAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAON,GAAI,MAAA,IAAU,EAAE,MAAA;AAAsB,KACvC,CAAA;AACD,IAAA,IAAI,CAAC,sBAAsB,EAAA,EAAI;AAC7B,MAAA,MAAM,OAAA,GAAU,CAAA,mCAAA,EAAsC,GAAG,CAAA,EAAA,EAAK,MAAM,KAAK,qBAAA,CAAsB,MAAM,CAAA,CAAA,EAAI,qBAAA,CAAsB,UAAU,CAAA,CAAA;AACzI,MAAA,IAAI,qBAAA,CAAsB,WAAW,GAAA,EAAK;AACxC,QAAA,MAAM,IAAIG,qBAAc,OAAO,CAAA;AAAA,MACjC;AACA,MAAA,MAAM,IAAI,MAAM,OAAO,CAAA;AAAA,IACzB;AAEA,IAAA,OAAO,MAAM,IAAA,CAAK,IAAA,CAAK,mBAAA,CAAoB,cAAA,CAAe;AAAA,MACxD,QAAA,EAAU,qBAAA;AAAA,MACV,OAAA,EAAS,QAAA;AAAA,MACT,IAAA,EAAM,SAAA;AAAA,MACN,QAAQ,OAAA,EAAS;AAAA,KAClB,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,MAAA,CACJ,GAAA,EACA,OAAA,EACyC;AACzC,IAAA,MAAM,EAAE,QAAA,EAAS,GAAIC,4BAAA,CAAY,GAAG,CAAA;AAGpC,IAAA,IAAI,CAAC,QAAA,EAAU,KAAA,CAAM,MAAM,CAAA,EAAG;AAC5B,MAAA,IAAI;AACF,QAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,OAAA,CAAQ,KAAK,OAAO,CAAA;AAE5C,QAAA,OAAO;AAAA,UACL,KAAA,EAAO;AAAA,YACL;AAAA,cACE,GAAA;AAAA,cACA,SAAS,IAAA,CAAK,MAAA;AAAA,cACd,gBAAgB,IAAA,CAAK;AAAA;AACvB,WACF;AAAA,UACA,IAAA,EAAM,KAAK,IAAA,IAAQ;AAAA,SACrB;AAAA,MACF,SAAS,CAAA,EAAG;AACV,QAAA,MAAM,KAAA,GAAQG,eAAQ,CAAC,CAAA;AACvB,QAAA,IAAI,KAAA,CAAM,SAAS,eAAA,EAAiB;AAClC,UAAA,OAAO;AAAA,YACL,OAAO,EAAC;AAAA,YACR,IAAA,EAAM;AAAA,WACR;AAAA,QACF;AACA,QAAA,MAAM,KAAA;AAAA,MACR;AAAA,IACF;AAEA,IAAA,MAAM,UAAA,GAAa,IAAA,CAAK,aAAA,CAAc,QAAQ,CAAA;AAC9C,IAAA,MAAM,OAAA,GAAU,IAAIC,mBAAA,CAAU,QAAQ,CAAA;AACtC,IAAA,MAAM,UAAUC,cAAA,CAAQ,GAAA,CAAI,QAAQ,QAAA,EAAU,UAAU,GAAG,CAAA,CAAA,CAAG,CAAA;AAC9D,IAAA,MAAM,UAAA,GAAa,UAAA,GAAa,CAAA,EAAG,UAAU,CAAA,CAAA,CAAA,GAAM,EAAA;AACnD,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,QAAA,CAAS,OAAA,EAAS;AAAA,MACxC,MAAM,OAAA,EAAS,IAAA;AAAA,MACf,QAAQ,OAAA,EAAS,MAAA;AAAA,MACjB,MAAA,EAAQ,UAAQ,OAAA,CAAQ,KAAA,CAAM,GAAG,UAAU,CAAA,EAAG,IAAI,CAAA,CAAE;AAAA,KACrD,CAAA;AAED,IAAA,MAAM,KAAA,GAAQ,MAAM,IAAA,CAAK,KAAA,EAAM;AAC/B,IAAA,OAAO;AAAA,MACL,MAAM,IAAA,CAAK,IAAA;AAAA,MACX,KAAA,EAAO,KAAA,CAAM,GAAA,CAAI,CAAA,IAAA,MAAS;AAAA,QACxB,GAAA,EAAK,IAAA,CAAK,WAAA,CAAY,UAAA,CAAW;AAAA,UAC/B,GAAA,EAAK,CAAA,CAAA,EAAI,UAAU,CAAA,EAAG,KAAK,IAAI,CAAA,CAAA;AAAA,UAC/B,IAAA,EAAM;AAAA,SACP,CAAA;AAAA,QACD,SAAS,IAAA,CAAK,OAAA;AAAA,QACd,gBAAgB,IAAA,CAAK;AAAA,OACvB,CAAE;AAAA,KACJ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaQ,cAAc,WAAA,EAAqB;AACzC,IAAA,MAAM,QAAA,GAAW,WAAA,CAAY,KAAA,CAAM,GAAG,CAAA;AACtC,IAAA,MAAM,YAAY,QAAA,CAAS,SAAA,CAAU,aAAW,OAAA,CAAQ,KAAA,CAAM,MAAM,CAAC,CAAA;AACrE,IAAA,OAAO,SAAA,KAAc,KACjB,WAAA,GACA,QAAA,CAAS,MAAM,CAAA,EAAG,SAAS,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA;AAAA,EAC3C;AAAA,EAEA,QAAA,GAAW;AACT,IAAA,MAAM,EAAE,IAAA,EAAM,KAAA,EAAM,GAAI,KAAK,WAAA,CAAY,MAAA;AACzC,IAAA,OAAO,CAAA,YAAA,EAAe,IAAI,CAAA,QAAA,EAAW,OAAA,CAAQ,KAAK,CAAC,CAAA,CAAA,CAAA;AAAA,EACrD;AAAA,EAEA,MAAc,iBAAA,CACZ,MAAA,EACA,KAAA,EACiB;AAEjB,IAAA,MAAM,SAAA,GAAY,IAAI,GAAA,CAAI,MAAM,CAAA;AAChC,IAAA,IAAI,SAAA,CAAU,QAAA,CAAS,QAAA,CAAS,oBAAoB,CAAA,EAAG;AACrD,MAAA,OAAO,IAAA,CAAK,yBAAA,CAA0B,SAAS,CAAA,CAAE,IAAA;AAAA,QAAK,CAAA,KAAA,KACpD,MAAM,QAAA;AAAS,OACjB;AAAA,IACF;AAEA,IAAA,OAAOC,iCAAA,CAAsB,MAAA,EAAQ,IAAA,CAAK,WAAA,CAAY,QAAQ,KAAK,CAAA;AAAA,EACrE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,0BAA0B,MAAA,EAA2B;AAC3D,IAAA,IAAI,CAAC,MAAA,CAAO,QAAA,CAAS,QAAA,CAAS,oBAAoB,CAAA,EAAG;AACnD,MAAA,MAAM,IAAI,MAAM,6CAA6C,CAAA;AAAA,IAC/D;AACA,IAAA,IAAI;AACF,MAAA,MAAM,CAAC,mBAAA,EAAqB,GAAG,IAC7B,MAAA,CAAO,QAAA,CAAS,MAAM,oBAAoB,CAAA;AAG5C,MAAA,MAAM,YAAA,GAAeL,4CAAA;AAAA,QACnB,KAAK,WAAA,CAAY;AAAA,OACnB;AAEA,MAAA,IAAI,WAAA,GAAc,mBAAA;AAElB,MAAA,IAAI,YAAA,EAAc;AAChB,QAAA,WAAA,GAAcC,gBAAA,CAAU,aAAa,YAAY,CAAA;AAAA,MACnD;AAEA,MAAA,WAAA,GAAc,WAAA,CAAY,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA;AAE3C,MAAA,MAAM,MAAA,GAAS,IAAI,GAAA,CAAI,MAAM,CAAA;AAC7B,MAAA,MAAA,CAAO,QAAA,GAAW,CAAA,EAAG,YAAY,CAAA,iBAAA,EAAoB,kBAAA;AAAA,QACnD;AAAA,OACD,mBAAmB,GAAG,CAAA,CAAA;AACvB,MAAA,OAAO,OAAA,CAAQ,QAAQ,MAAM,CAAA;AAAA,IAC/B,SAAS,CAAA,EAAG;AACV,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,yCAAA,EAA4C,MAAM,CAAA,EAAA,EAAK,CAAC,CAAA;AAAA,OAC1D;AAAA,IACF;AAAA,EACF;AACF;;;;"}
|
|
@@ -41,8 +41,10 @@ const rateLimitMiddleware = (options) => {
|
|
|
41
41
|
skipFailedRequests,
|
|
42
42
|
passOnStoreError,
|
|
43
43
|
keyGenerator(req, _res) {
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
if (!req.ip) {
|
|
45
|
+
return req.socket.remoteAddress;
|
|
46
|
+
}
|
|
47
|
+
return req.ip;
|
|
46
48
|
},
|
|
47
49
|
skip: (req, _res) => {
|
|
48
50
|
return Boolean(req.ip && ipAllowList.includes(req.ip)) || Boolean(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rateLimitMiddleware.cjs.js","sources":["../../src/lib/rateLimitMiddleware.ts"],"sourcesContent":["/*\n * Copyright 2025 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 */\nimport { RequestHandler } from 'express';\nimport {
|
|
1
|
+
{"version":3,"file":"rateLimitMiddleware.cjs.js","sources":["../../src/lib/rateLimitMiddleware.ts"],"sourcesContent":["/*\n * Copyright 2025 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 */\nimport { RequestHandler } from 'express';\nimport { rateLimit, Store } from 'express-rate-limit';\nimport { Config, readDurationFromConfig } from '@backstage/config';\nimport { durationToMilliseconds } from '@backstage/types';\n\nexport const rateLimitMiddleware = (options: {\n store?: Store;\n config?: Config;\n}): RequestHandler => {\n const { store, config } = options;\n let windowMs: number = 60000;\n if (config && config.has('window')) {\n const windowDuration = readDurationFromConfig(config, {\n key: 'window',\n });\n windowMs = durationToMilliseconds(windowDuration);\n }\n const limit = config?.getOptionalNumber('incomingRequestLimit');\n const ipAllowList = config?.getOptionalStringArray('ipAllowList') ?? [\n '127.0.0.1',\n '0:0:0:0:0:0:0:1',\n '::1',\n ];\n const skipSuccessfulRequests = config?.getOptionalBoolean(\n 'skipSuccessfulRequests',\n );\n const skipFailedRequests = config?.getOptionalBoolean('skipFailedRequests');\n const passOnStoreError = config?.getOptionalBoolean('passOnStoreError');\n\n return rateLimit({\n windowMs,\n limit,\n skipSuccessfulRequests,\n message: {\n error: {\n name: 'Error',\n message: `Too many requests, please try again later`,\n },\n response: {\n statusCode: 429,\n },\n },\n statusCode: 429,\n skipFailedRequests,\n passOnStoreError: passOnStoreError,\n keyGenerator(req, _res): string {\n if (!req.ip) {\n return req.socket.remoteAddress!;\n }\n return req.ip;\n },\n skip: (req, _res) => {\n return (\n Boolean(req.ip && ipAllowList.includes(req.ip)) ||\n Boolean(\n req.socket.remoteAddress &&\n ipAllowList.includes(req.socket.remoteAddress),\n )\n );\n },\n validate: {\n trustProxy: false,\n },\n store,\n });\n};\n"],"names":["config","readDurationFromConfig","durationToMilliseconds","rateLimit"],"mappings":";;;;;;AAoBO,MAAM,mBAAA,GAAsB,CAAC,OAAA,KAGd;AACpB,EAAA,MAAM,EAAE,KAAA,UAAOA,QAAA,EAAO,GAAI,OAAA;AAC1B,EAAA,IAAI,QAAA,GAAmB,GAAA;AACvB,EAAA,IAAIA,QAAA,IAAUA,QAAA,CAAO,GAAA,CAAI,QAAQ,CAAA,EAAG;AAClC,IAAA,MAAM,cAAA,GAAiBC,8BAAuBD,QAAA,EAAQ;AAAA,MACpD,GAAA,EAAK;AAAA,KACN,CAAA;AACD,IAAA,QAAA,GAAWE,6BAAuB,cAAc,CAAA;AAAA,EAClD;AACA,EAAA,MAAM,KAAA,GAAQF,QAAA,EAAQ,iBAAA,CAAkB,sBAAsB,CAAA;AAC9D,EAAA,MAAM,WAAA,GAAcA,QAAA,EAAQ,sBAAA,CAAuB,aAAa,CAAA,IAAK;AAAA,IACnE,WAAA;AAAA,IACA,iBAAA;AAAA,IACA;AAAA,GACF;AACA,EAAA,MAAM,yBAAyBA,QAAA,EAAQ,kBAAA;AAAA,IACrC;AAAA,GACF;AACA,EAAA,MAAM,kBAAA,GAAqBA,QAAA,EAAQ,kBAAA,CAAmB,oBAAoB,CAAA;AAC1E,EAAA,MAAM,gBAAA,GAAmBA,QAAA,EAAQ,kBAAA,CAAmB,kBAAkB,CAAA;AAEtE,EAAA,OAAOG,0BAAA,CAAU;AAAA,IACf,QAAA;AAAA,IACA,KAAA;AAAA,IACA,sBAAA;AAAA,IACA,OAAA,EAAS;AAAA,MACP,KAAA,EAAO;AAAA,QACL,IAAA,EAAM,OAAA;AAAA,QACN,OAAA,EAAS,CAAA,yCAAA;AAAA,OACX;AAAA,MACA,QAAA,EAAU;AAAA,QACR,UAAA,EAAY;AAAA;AACd,KACF;AAAA,IACA,UAAA,EAAY,GAAA;AAAA,IACZ,kBAAA;AAAA,IACA,gBAAA;AAAA,IACA,YAAA,CAAa,KAAK,IAAA,EAAc;AAC9B,MAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,QAAA,OAAO,IAAI,MAAA,CAAO,aAAA;AAAA,MACpB;AACA,MAAA,OAAO,GAAA,CAAI,EAAA;AAAA,IACb,CAAA;AAAA,IACA,IAAA,EAAM,CAAC,GAAA,EAAK,IAAA,KAAS;AACnB,MAAA,OACE,OAAA,CAAQ,IAAI,EAAA,IAAM,WAAA,CAAY,SAAS,GAAA,CAAI,EAAE,CAAC,CAAA,IAC9C,OAAA;AAAA,QACE,IAAI,MAAA,CAAO,aAAA,IACT,YAAY,QAAA,CAAS,GAAA,CAAI,OAAO,aAAa;AAAA,OACjD;AAAA,IAEJ,CAAA;AAAA,IACA,QAAA,EAAU;AAAA,MACR,UAAA,EAAY;AAAA,KACd;AAAA,IACA;AAAA,GACD,CAAA;AACH;;;;"}
|
package/dist/package.json.cjs.js
CHANGED
package/dist/urlReader.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
|
|
2
2
|
import { RootConfigService, LoggerService, UrlReaderServiceReadTreeResponse, UrlReaderService, UrlReaderServiceReadUrlOptions, UrlReaderServiceReadUrlResponse, UrlReaderServiceReadTreeOptions, UrlReaderServiceSearchOptions, UrlReaderServiceSearchResponse } from '@backstage/backend-plugin-api';
|
|
3
|
-
import { AzureIntegration, AzureDevOpsCredentialsProvider, BitbucketCloudIntegration, BitbucketServerIntegration, GerritIntegration, GithubIntegration, GithubCredentialsProvider, GitLabIntegration, GiteaIntegration, HarnessIntegration, AwsS3Integration, AzureCredentialsManager,
|
|
3
|
+
import { AzureIntegration, AzureDevOpsCredentialsProvider, BitbucketCloudIntegration, BitbucketServerIntegration, GerritIntegration, GithubIntegration, GithubCredentialsProvider, GitLabIntegration, GiteaIntegration, HarnessIntegration, AwsS3Integration, AzureCredentialsManager, AzureBlobStorageIntergation } from '@backstage/integration';
|
|
4
4
|
import { Readable } from 'node:stream';
|
|
5
5
|
import { AwsCredentialsManager } from '@backstage/integration-aws-node';
|
|
6
6
|
import { Config } from '@backstage/config';
|
|
@@ -334,7 +334,7 @@ declare class AzureBlobStorageUrlReader implements UrlReaderService {
|
|
|
334
334
|
private readonly credsManager;
|
|
335
335
|
private readonly integration;
|
|
336
336
|
private readonly deps;
|
|
337
|
-
constructor(credsManager: AzureCredentialsManager, integration:
|
|
337
|
+
constructor(credsManager: AzureCredentialsManager, integration: AzureBlobStorageIntergation, deps: {
|
|
338
338
|
treeResponseFactory: ReadTreeResponseFactory;
|
|
339
339
|
});
|
|
340
340
|
private createContainerClient;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/backend-defaults",
|
|
3
|
-
"version": "0.17.2
|
|
3
|
+
"version": "0.17.2",
|
|
4
4
|
"description": "Backend defaults used by Backstage backend apps",
|
|
5
5
|
"backstage": {
|
|
6
6
|
"role": "node-library"
|
|
@@ -218,20 +218,20 @@
|
|
|
218
218
|
"@aws-sdk/types": "^3.347.0",
|
|
219
219
|
"@azure/identity": "^4.0.0",
|
|
220
220
|
"@azure/storage-blob": "^12.5.0",
|
|
221
|
-
"@backstage/backend-app-api": "1.7.
|
|
222
|
-
"@backstage/backend-dev-utils": "0.1.7",
|
|
223
|
-
"@backstage/backend-plugin-api": "1.9.
|
|
224
|
-
"@backstage/cli-node": "0.3.2",
|
|
225
|
-
"@backstage/config": "1.3.8",
|
|
226
|
-
"@backstage/config-loader": "1.10.11",
|
|
227
|
-
"@backstage/errors": "1.3.1",
|
|
228
|
-
"@backstage/integration": "2.0.
|
|
229
|
-
"@backstage/integration-aws-node": "0.2.0",
|
|
230
|
-
"@backstage/plugin-auth-node": "0.7.
|
|
231
|
-
"@backstage/plugin-events-node": "0.4.
|
|
232
|
-
"@backstage/plugin-permission-common": "0.9.9",
|
|
233
|
-
"@backstage/plugin-permission-node": "0.11.
|
|
234
|
-
"@backstage/types": "1.2.2",
|
|
221
|
+
"@backstage/backend-app-api": "^1.7.0",
|
|
222
|
+
"@backstage/backend-dev-utils": "^0.1.7",
|
|
223
|
+
"@backstage/backend-plugin-api": "^1.9.1",
|
|
224
|
+
"@backstage/cli-node": "^0.3.2",
|
|
225
|
+
"@backstage/config": "^1.3.8",
|
|
226
|
+
"@backstage/config-loader": "^1.10.11",
|
|
227
|
+
"@backstage/errors": "^1.3.1",
|
|
228
|
+
"@backstage/integration": "^2.0.2",
|
|
229
|
+
"@backstage/integration-aws-node": "^0.2.0",
|
|
230
|
+
"@backstage/plugin-auth-node": "^0.7.1",
|
|
231
|
+
"@backstage/plugin-events-node": "^0.4.22",
|
|
232
|
+
"@backstage/plugin-permission-common": "^0.9.9",
|
|
233
|
+
"@backstage/plugin-permission-node": "^0.11.0",
|
|
234
|
+
"@backstage/types": "^1.2.2",
|
|
235
235
|
"@google-cloud/storage": "^7.0.0",
|
|
236
236
|
"@keyv/memcache": "^2.0.1",
|
|
237
237
|
"@keyv/redis": "^4.0.1",
|
|
@@ -286,8 +286,8 @@
|
|
|
286
286
|
},
|
|
287
287
|
"devDependencies": {
|
|
288
288
|
"@aws-sdk/util-stream-node": "^3.350.0",
|
|
289
|
-
"@backstage/backend-test-utils": "1.11.
|
|
290
|
-
"@backstage/cli": "0.36.
|
|
289
|
+
"@backstage/backend-test-utils": "^1.11.3",
|
|
290
|
+
"@backstage/cli": "^0.36.2",
|
|
291
291
|
"@google-cloud/cloud-sql-connector": "^1.4.0",
|
|
292
292
|
"@types/archiver": "^7.0.0",
|
|
293
293
|
"@types/base64-stream": "^1.0.2",
|