@backstage/backend-defaults 0.10.1-next.1 → 0.11.0

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.
Files changed (52) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/config.d.ts +86 -0
  3. package/dist/CreateBackend.cjs.js +5 -5
  4. package/dist/CreateBackend.cjs.js.map +1 -1
  5. package/dist/alpha/entrypoints/actions/DefaultActionsService.cjs.js.map +1 -0
  6. package/dist/{entrypoints → alpha/entrypoints}/actions/actionsServiceFactory.cjs.js +2 -1
  7. package/dist/alpha/entrypoints/actions/actionsServiceFactory.cjs.js.map +1 -0
  8. package/dist/{entrypoints → alpha/entrypoints}/actionsRegistry/DefaultActionsRegistryService.cjs.js +9 -2
  9. package/dist/alpha/entrypoints/actionsRegistry/DefaultActionsRegistryService.cjs.js.map +1 -0
  10. package/dist/{entrypoints → alpha/entrypoints}/actionsRegistry/actionsRegistryServiceFactory.cjs.js +2 -1
  11. package/dist/alpha/entrypoints/actionsRegistry/actionsRegistryServiceFactory.cjs.js.map +1 -0
  12. package/dist/alpha.cjs.js +10 -0
  13. package/dist/alpha.cjs.js.map +1 -0
  14. package/dist/alpha.d.ts +14 -0
  15. package/dist/auditor.d.ts +3 -1
  16. package/dist/entrypoints/auditor/DefaultAuditorService.cjs.js +1 -1
  17. package/dist/entrypoints/auditor/DefaultAuditorService.cjs.js.map +1 -1
  18. package/dist/entrypoints/auditor/WinstonRootAuditorService.cjs.js +17 -4
  19. package/dist/entrypoints/auditor/WinstonRootAuditorService.cjs.js.map +1 -1
  20. package/dist/entrypoints/auditor/auditorServiceFactory.cjs.js +15 -31
  21. package/dist/entrypoints/auditor/auditorServiceFactory.cjs.js.map +1 -1
  22. package/dist/entrypoints/auditor/types.cjs.js +13 -0
  23. package/dist/entrypoints/auditor/types.cjs.js.map +1 -0
  24. package/dist/entrypoints/auditor/utils.cjs.js +31 -0
  25. package/dist/entrypoints/auditor/utils.cjs.js.map +1 -0
  26. package/dist/entrypoints/httpRouter/http/createRateLimitMiddleware.cjs.js +23 -0
  27. package/dist/entrypoints/httpRouter/http/createRateLimitMiddleware.cjs.js.map +1 -0
  28. package/dist/entrypoints/httpRouter/httpRouterServiceFactory.cjs.js +2 -0
  29. package/dist/entrypoints/httpRouter/httpRouterServiceFactory.cjs.js.map +1 -1
  30. package/dist/entrypoints/rootHttpRouter/http/MiddlewareFactory.cjs.js +31 -0
  31. package/dist/entrypoints/rootHttpRouter/http/MiddlewareFactory.cjs.js.map +1 -1
  32. package/dist/entrypoints/rootHttpRouter/rootHttpRouterServiceFactory.cjs.js +1 -0
  33. package/dist/entrypoints/rootHttpRouter/rootHttpRouterServiceFactory.cjs.js.map +1 -1
  34. package/dist/lib/RateLimitStoreFactory.cjs.js +37 -0
  35. package/dist/lib/RateLimitStoreFactory.cjs.js.map +1 -0
  36. package/dist/lib/rateLimitMiddleware.cjs.js +62 -0
  37. package/dist/lib/rateLimitMiddleware.cjs.js.map +1 -0
  38. package/dist/package.json.cjs.js +7 -9
  39. package/dist/package.json.cjs.js.map +1 -1
  40. package/dist/rootHttpRouter.d.ts +11 -0
  41. package/package.json +27 -33
  42. package/dist/actions.cjs.js +0 -8
  43. package/dist/actions.cjs.js.map +0 -1
  44. package/dist/actions.d.ts +0 -8
  45. package/dist/actionsRegistry.cjs.js +0 -8
  46. package/dist/actionsRegistry.cjs.js.map +0 -1
  47. package/dist/actionsRegistry.d.ts +0 -8
  48. package/dist/entrypoints/actions/DefaultActionsService.cjs.js.map +0 -1
  49. package/dist/entrypoints/actions/actionsServiceFactory.cjs.js.map +0 -1
  50. package/dist/entrypoints/actionsRegistry/DefaultActionsRegistryService.cjs.js.map +0 -1
  51. package/dist/entrypoints/actionsRegistry/actionsRegistryServiceFactory.cjs.js.map +0 -1
  52. /package/dist/{entrypoints → alpha/entrypoints}/actions/DefaultActionsService.cjs.js +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,76 @@
1
1
  # @backstage/backend-defaults
2
2
 
3
+ ## 0.11.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 3ccb7fc: Enhanced error handling in the auditor service factory to pass errors as objects. Aligned WinstonRootAuditorService with the default service factory's error handling.
8
+
9
+ ### Patch Changes
10
+
11
+ - 1220cf8: Added new rate limit middleware to allow rate limiting requests to the backend
12
+
13
+ If you are using the `configure` callback of the root HTTP router service and do NOT call `applyDefaults()` inside it, please see [the relevant changes](https://github.com/backstage/backstage/pull/28708/files#diff-86ad1b6a694dd250823aee39d410428dd837c9d9a04ca8c33bd1081fbe3f22af) that were made, to see if you want to apply them as well to your custom configuration.
14
+ Rate limiting can be turned on by adding the following configuration to `app-config.yaml`:
15
+
16
+ ```yaml
17
+ backend:
18
+ rateLimit:
19
+ window: 6s
20
+ incomingRequestLimit: 100
21
+ ```
22
+
23
+ Plugin specific rate limiting can be configured by adding the following configuration to `app-config.yaml`:
24
+
25
+ ```yaml
26
+ backend:
27
+ rateLimit:
28
+ global: false # This will disable the global rate limiting
29
+ plugin:
30
+ catalog:
31
+ window: 6s
32
+ incomingRequestLimit: 100
33
+ ```
34
+
35
+ - c999c25: Added some default implementations for the experimental `ActionsService` and `ActionsRegistryService` under `/alpha` that allow registration of actions for a particular plugin.
36
+ - Updated dependencies
37
+ - @backstage/plugin-auth-node@0.6.4
38
+ - @backstage/backend-app-api@1.2.4
39
+ - @backstage/backend-plugin-api@1.4.0
40
+ - @backstage/backend-dev-utils@0.1.5
41
+ - @backstage/cli-node@0.2.13
42
+ - @backstage/config@1.3.2
43
+ - @backstage/config-loader@1.10.1
44
+ - @backstage/errors@1.2.7
45
+ - @backstage/integration@1.17.0
46
+ - @backstage/integration-aws-node@0.1.16
47
+ - @backstage/types@1.2.1
48
+ - @backstage/plugin-events-node@0.4.12
49
+ - @backstage/plugin-permission-node@0.10.1
50
+
51
+ ## 0.11.0-next.2
52
+
53
+ ### Minor Changes
54
+
55
+ - 3ccb7fc: Enhanced error handling in the auditor service factory to pass errors as objects. Aligned WinstonRootAuditorService with the default service factory's error handling.
56
+
57
+ ### Patch Changes
58
+
59
+ - Updated dependencies
60
+ - @backstage/backend-app-api@1.2.4-next.2
61
+ - @backstage/backend-dev-utils@0.1.5
62
+ - @backstage/backend-plugin-api@1.4.0-next.1
63
+ - @backstage/cli-node@0.2.13
64
+ - @backstage/config@1.3.2
65
+ - @backstage/config-loader@1.10.1
66
+ - @backstage/errors@1.2.7
67
+ - @backstage/integration@1.17.0
68
+ - @backstage/integration-aws-node@0.1.16
69
+ - @backstage/types@1.2.1
70
+ - @backstage/plugin-auth-node@0.6.4-next.1
71
+ - @backstage/plugin-events-node@0.4.12-next.1
72
+ - @backstage/plugin-permission-node@0.10.1-next.1
73
+
3
74
  ## 0.10.1-next.1
4
75
 
5
76
  ### Patch Changes
package/config.d.ts CHANGED
@@ -790,6 +790,92 @@ export interface Config {
790
790
  headers?: { [name: string]: string };
791
791
  };
792
792
 
793
+ /**
794
+ * Rate limiting options. Defining this as `true` will enable rate limiting with default values.
795
+ */
796
+ rateLimit?:
797
+ | true
798
+ | {
799
+ store?:
800
+ | {
801
+ type: 'redis';
802
+ connection: string;
803
+ }
804
+ | {
805
+ type: 'memory';
806
+ };
807
+ /**
808
+ * Enable/disable global rate limiting. If this is disabled, plugin specific rate limiting must be
809
+ * used.
810
+ */
811
+ global?: boolean;
812
+ /**
813
+ * Time frame in milliseconds or as human duration for which requests are checked/remembered.
814
+ * Defaults to one minute.
815
+ */
816
+ window?: string | HumanDuration;
817
+ /**
818
+ * The maximum number of connections to allow during the `window` before rate limiting the client.
819
+ * Defaults to 5.
820
+ */
821
+ incomingRequestLimit?: number;
822
+ /**
823
+ * Whether to pass requests in case of store failure.
824
+ * Defaults to false.
825
+ */
826
+ passOnStoreError?: boolean;
827
+ /**
828
+ * List of allowed IP addresses that are not rate limited.
829
+ * Defaults to [127.0.0.1, 0:0:0:0:0:0:0:1, ::1].
830
+ */
831
+ ipAllowList?: string[];
832
+ /**
833
+ * Skip rate limiting for requests that have been successful.
834
+ * Defaults to false.
835
+ */
836
+ skipSuccessfulRequests?: boolean;
837
+ /**
838
+ * Skip rate limiting for requests that have failed.
839
+ * Defaults to false.
840
+ */
841
+ skipFailedRequests?: boolean;
842
+ /** Plugin specific rate limiting configuration */
843
+ plugin?: {
844
+ [pluginId: string]: {
845
+ /**
846
+ * Time frame in milliseconds or as human duration for which requests are checked/remembered.
847
+ * Defaults to one minute.
848
+ */
849
+ window?: string | HumanDuration;
850
+ /**
851
+ * The maximum number of connections to allow during the `window` before rate limiting the client.
852
+ * Defaults to 5.
853
+ */
854
+ incomingRequestLimit?: number;
855
+ /**
856
+ * Whether to pass requests in case of store failure.
857
+ * Defaults to false.
858
+ */
859
+ passOnStoreError?: boolean;
860
+ /**
861
+ * List of allowed IP addresses that are not rate limited.
862
+ * Defaults to [127.0.0.1, 0:0:0:0:0:0:0:1, ::1].
863
+ */
864
+ ipAllowList?: string[];
865
+ /**
866
+ * Skip rate limiting for requests that have been successful.
867
+ * Defaults to false.
868
+ */
869
+ skipSuccessfulRequests?: boolean;
870
+ /**
871
+ * Skip rate limiting for requests that have failed.
872
+ * Defaults to false.
873
+ */
874
+ skipFailedRequests?: boolean;
875
+ };
876
+ };
877
+ };
878
+
793
879
  /**
794
880
  * Configuration related to URL reading, used for example for reading catalog info
795
881
  * files, scaffolder templates, and techdocs content.
@@ -21,12 +21,9 @@ var scheduler = require('@backstage/backend-defaults/scheduler');
21
21
  var urlReader = require('@backstage/backend-defaults/urlReader');
22
22
  var userInfo = require('@backstage/backend-defaults/userInfo');
23
23
  var pluginEventsNode = require('@backstage/plugin-events-node');
24
- var actionsRegistryServiceFactory = require('./entrypoints/actionsRegistry/actionsRegistryServiceFactory.cjs.js');
25
- var actionsServiceFactory = require('./entrypoints/actions/actionsServiceFactory.cjs.js');
24
+ var alpha = require('@backstage/backend-defaults/alpha');
26
25
 
27
26
  const defaultServiceFactories = [
28
- actionsRegistryServiceFactory.actionsRegistryServiceFactory,
29
- actionsServiceFactory.actionsServiceFactory,
30
27
  auditor.auditorServiceFactory,
31
28
  auth.authServiceFactory,
32
29
  cache.cacheServiceFactory,
@@ -46,7 +43,10 @@ const defaultServiceFactories = [
46
43
  scheduler.schedulerServiceFactory,
47
44
  userInfo.userInfoServiceFactory,
48
45
  urlReader.urlReaderServiceFactory,
49
- pluginEventsNode.eventsServiceFactory
46
+ pluginEventsNode.eventsServiceFactory,
47
+ // alpha services
48
+ alpha.actionsRegistryServiceFactory,
49
+ alpha.actionsServiceFactory
50
50
  ];
51
51
  function createBackend() {
52
52
  return backendAppApi.createSpecializedBackend({ defaultServiceFactories });
@@ -1 +1 @@
1
- {"version":3,"file":"CreateBackend.cjs.js","sources":["../src/CreateBackend.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 { Backend, createSpecializedBackend } from '@backstage/backend-app-api';\nimport { auditorServiceFactory } from '@backstage/backend-defaults/auditor';\nimport { authServiceFactory } from '@backstage/backend-defaults/auth';\nimport { cacheServiceFactory } from '@backstage/backend-defaults/cache';\nimport { databaseServiceFactory } from '@backstage/backend-defaults/database';\nimport { discoveryServiceFactory } from '@backstage/backend-defaults/discovery';\nimport { httpAuthServiceFactory } from '@backstage/backend-defaults/httpAuth';\nimport { httpRouterServiceFactory } from '@backstage/backend-defaults/httpRouter';\nimport { lifecycleServiceFactory } from '@backstage/backend-defaults/lifecycle';\nimport { loggerServiceFactory } from '@backstage/backend-defaults/logger';\nimport { permissionsServiceFactory } from '@backstage/backend-defaults/permissions';\nimport { permissionsRegistryServiceFactory } from '@backstage/backend-defaults/permissionsRegistry';\nimport { rootConfigServiceFactory } from '@backstage/backend-defaults/rootConfig';\nimport { rootHealthServiceFactory } from '@backstage/backend-defaults/rootHealth';\nimport { rootHttpRouterServiceFactory } from '@backstage/backend-defaults/rootHttpRouter';\nimport { rootLifecycleServiceFactory } from '@backstage/backend-defaults/rootLifecycle';\nimport { rootLoggerServiceFactory } from '@backstage/backend-defaults/rootLogger';\nimport { schedulerServiceFactory } from '@backstage/backend-defaults/scheduler';\nimport { urlReaderServiceFactory } from '@backstage/backend-defaults/urlReader';\nimport { userInfoServiceFactory } from '@backstage/backend-defaults/userInfo';\nimport { eventsServiceFactory } from '@backstage/plugin-events-node';\nimport { actionsRegistryServiceFactory } from './entrypoints/actionsRegistry';\nimport { actionsServiceFactory } from './entrypoints/actions';\n\nexport const defaultServiceFactories = [\n actionsRegistryServiceFactory,\n actionsServiceFactory,\n auditorServiceFactory,\n authServiceFactory,\n cacheServiceFactory,\n rootConfigServiceFactory,\n databaseServiceFactory,\n discoveryServiceFactory,\n httpAuthServiceFactory,\n httpRouterServiceFactory,\n lifecycleServiceFactory,\n loggerServiceFactory,\n permissionsServiceFactory,\n permissionsRegistryServiceFactory,\n rootHealthServiceFactory,\n rootHttpRouterServiceFactory,\n rootLifecycleServiceFactory,\n rootLoggerServiceFactory,\n schedulerServiceFactory,\n userInfoServiceFactory,\n urlReaderServiceFactory,\n eventsServiceFactory,\n];\n\n/**\n * @public\n */\nexport function createBackend(): Backend {\n return createSpecializedBackend({ defaultServiceFactories });\n}\n"],"names":["actionsRegistryServiceFactory","actionsServiceFactory","auditorServiceFactory","authServiceFactory","cacheServiceFactory","rootConfigServiceFactory","databaseServiceFactory","discoveryServiceFactory","httpAuthServiceFactory","httpRouterServiceFactory","lifecycleServiceFactory","loggerServiceFactory","permissionsServiceFactory","permissionsRegistryServiceFactory","rootHealthServiceFactory","rootHttpRouterServiceFactory","rootLifecycleServiceFactory","rootLoggerServiceFactory","schedulerServiceFactory","userInfoServiceFactory","urlReaderServiceFactory","eventsServiceFactory","createSpecializedBackend"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAwCO,MAAM,uBAA0B,GAAA;AAAA,EACrCA,2DAAA;AAAA,EACAC,2CAAA;AAAA,EACAC,6BAAA;AAAA,EACAC,uBAAA;AAAA,EACAC,yBAAA;AAAA,EACAC,mCAAA;AAAA,EACAC,+BAAA;AAAA,EACAC,iCAAA;AAAA,EACAC,+BAAA;AAAA,EACAC,mCAAA;AAAA,EACAC,iCAAA;AAAA,EACAC,2BAAA;AAAA,EACAC,qCAAA;AAAA,EACAC,qDAAA;AAAA,EACAC,mCAAA;AAAA,EACAC,2CAAA;AAAA,EACAC,yCAAA;AAAA,EACAC,mCAAA;AAAA,EACAC,iCAAA;AAAA,EACAC,+BAAA;AAAA,EACAC,iCAAA;AAAA,EACAC;AACF;AAKO,SAAS,aAAyB,GAAA;AACvC,EAAO,OAAAC,sCAAA,CAAyB,EAAE,uBAAA,EAAyB,CAAA;AAC7D;;;;;"}
1
+ {"version":3,"file":"CreateBackend.cjs.js","sources":["../src/CreateBackend.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 { Backend, createSpecializedBackend } from '@backstage/backend-app-api';\nimport { auditorServiceFactory } from '@backstage/backend-defaults/auditor';\nimport { authServiceFactory } from '@backstage/backend-defaults/auth';\nimport { cacheServiceFactory } from '@backstage/backend-defaults/cache';\nimport { databaseServiceFactory } from '@backstage/backend-defaults/database';\nimport { discoveryServiceFactory } from '@backstage/backend-defaults/discovery';\nimport { httpAuthServiceFactory } from '@backstage/backend-defaults/httpAuth';\nimport { httpRouterServiceFactory } from '@backstage/backend-defaults/httpRouter';\nimport { lifecycleServiceFactory } from '@backstage/backend-defaults/lifecycle';\nimport { loggerServiceFactory } from '@backstage/backend-defaults/logger';\nimport { permissionsServiceFactory } from '@backstage/backend-defaults/permissions';\nimport { permissionsRegistryServiceFactory } from '@backstage/backend-defaults/permissionsRegistry';\nimport { rootConfigServiceFactory } from '@backstage/backend-defaults/rootConfig';\nimport { rootHealthServiceFactory } from '@backstage/backend-defaults/rootHealth';\nimport { rootHttpRouterServiceFactory } from '@backstage/backend-defaults/rootHttpRouter';\nimport { rootLifecycleServiceFactory } from '@backstage/backend-defaults/rootLifecycle';\nimport { rootLoggerServiceFactory } from '@backstage/backend-defaults/rootLogger';\nimport { schedulerServiceFactory } from '@backstage/backend-defaults/scheduler';\nimport { urlReaderServiceFactory } from '@backstage/backend-defaults/urlReader';\nimport { userInfoServiceFactory } from '@backstage/backend-defaults/userInfo';\nimport { eventsServiceFactory } from '@backstage/plugin-events-node';\nimport {\n actionsRegistryServiceFactory,\n actionsServiceFactory,\n} from '@backstage/backend-defaults/alpha';\n\nexport const defaultServiceFactories = [\n auditorServiceFactory,\n authServiceFactory,\n cacheServiceFactory,\n rootConfigServiceFactory,\n databaseServiceFactory,\n discoveryServiceFactory,\n httpAuthServiceFactory,\n httpRouterServiceFactory,\n lifecycleServiceFactory,\n loggerServiceFactory,\n permissionsServiceFactory,\n permissionsRegistryServiceFactory,\n rootHealthServiceFactory,\n rootHttpRouterServiceFactory,\n rootLifecycleServiceFactory,\n rootLoggerServiceFactory,\n schedulerServiceFactory,\n userInfoServiceFactory,\n urlReaderServiceFactory,\n eventsServiceFactory,\n\n // alpha services\n actionsRegistryServiceFactory,\n actionsServiceFactory,\n];\n\n/**\n * @public\n */\nexport function createBackend(): Backend {\n return createSpecializedBackend({ defaultServiceFactories });\n}\n"],"names":["auditorServiceFactory","authServiceFactory","cacheServiceFactory","rootConfigServiceFactory","databaseServiceFactory","discoveryServiceFactory","httpAuthServiceFactory","httpRouterServiceFactory","lifecycleServiceFactory","loggerServiceFactory","permissionsServiceFactory","permissionsRegistryServiceFactory","rootHealthServiceFactory","rootHttpRouterServiceFactory","rootLifecycleServiceFactory","rootLoggerServiceFactory","schedulerServiceFactory","userInfoServiceFactory","urlReaderServiceFactory","eventsServiceFactory","actionsRegistryServiceFactory","actionsServiceFactory","createSpecializedBackend"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA0CO,MAAM,uBAA0B,GAAA;AAAA,EACrCA,6BAAA;AAAA,EACAC,uBAAA;AAAA,EACAC,yBAAA;AAAA,EACAC,mCAAA;AAAA,EACAC,+BAAA;AAAA,EACAC,iCAAA;AAAA,EACAC,+BAAA;AAAA,EACAC,mCAAA;AAAA,EACAC,iCAAA;AAAA,EACAC,2BAAA;AAAA,EACAC,qCAAA;AAAA,EACAC,qDAAA;AAAA,EACAC,mCAAA;AAAA,EACAC,2CAAA;AAAA,EACAC,yCAAA;AAAA,EACAC,mCAAA;AAAA,EACAC,iCAAA;AAAA,EACAC,+BAAA;AAAA,EACAC,iCAAA;AAAA,EACAC,qCAAA;AAAA;AAAA,EAGAC,mCAAA;AAAA,EACAC;AACF;AAKO,SAAS,aAAyB,GAAA;AACvC,EAAO,OAAAC,sCAAA,CAAyB,EAAE,uBAAA,EAAyB,CAAA;AAC7D;;;;;"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DefaultActionsService.cjs.js","sources":["../../../../src/alpha/entrypoints/actions/DefaultActionsService.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 {\n AuthService,\n BackstageCredentials,\n DiscoveryService,\n LoggerService,\n RootConfigService,\n} from '@backstage/backend-plugin-api';\nimport { ResponseError } from '@backstage/errors';\nimport { JsonObject } from '@backstage/types';\nimport {\n ActionsService,\n ActionsServiceAction,\n} from '@backstage/backend-plugin-api/alpha';\n\nexport class DefaultActionsService implements ActionsService {\n private constructor(\n private readonly discovery: DiscoveryService,\n private readonly config: RootConfigService,\n private readonly logger: LoggerService,\n private readonly auth: AuthService,\n ) {}\n\n static create({\n discovery,\n config,\n logger,\n auth,\n }: {\n discovery: DiscoveryService;\n config: RootConfigService;\n logger: LoggerService;\n auth: AuthService;\n }) {\n return new DefaultActionsService(discovery, config, logger, auth);\n }\n\n async list({ credentials }: { credentials: BackstageCredentials }) {\n const pluginSources =\n this.config.getOptionalStringArray('backend.actions.pluginSources') ?? [];\n\n const remoteActionsList = await Promise.all(\n pluginSources.map(async source => {\n try {\n const response = await this.makeRequest({\n path: `/.backstage/actions/v1/actions`,\n pluginId: source,\n credentials,\n });\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n const { actions } = (await response.json()) as {\n actions: ActionsServiceAction;\n };\n\n return actions;\n } catch (error) {\n this.logger.warn(`Failed to fetch actions from ${source}`, error);\n return [];\n }\n }),\n );\n\n return { actions: remoteActionsList.flat() };\n }\n\n async invoke(opts: {\n id: string;\n input?: JsonObject;\n credentials: BackstageCredentials;\n }) {\n const pluginId = this.pluginIdFromActionId(opts.id);\n const response = await this.makeRequest({\n path: `/.backstage/actions/v1/actions/${encodeURIComponent(\n opts.id,\n )}/invoke`,\n pluginId,\n credentials: opts.credentials,\n options: {\n method: 'POST',\n body: JSON.stringify(opts.input),\n headers: {\n 'Content-Type': 'application/json',\n },\n },\n });\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n const { output } = await response.json();\n return { output };\n }\n\n private async makeRequest(opts: {\n path: string;\n pluginId: string;\n options?: RequestInit;\n credentials: BackstageCredentials;\n }) {\n const { path, pluginId, credentials, options } = opts;\n const baseUrl = await this.discovery.getBaseUrl(pluginId);\n\n const { token } = await this.auth.getPluginRequestToken({\n onBehalfOf: credentials,\n targetPluginId: opts.pluginId,\n });\n\n return fetch(`${baseUrl}${path}`, {\n ...options,\n headers: {\n ...options?.headers,\n Authorization: `Bearer ${token}`,\n },\n });\n }\n\n private pluginIdFromActionId(id: string): string {\n const colonIndex = id.indexOf(':');\n if (colonIndex === -1) {\n throw new Error(`Invalid action id: ${id}`);\n }\n return id.substring(0, colonIndex);\n }\n}\n"],"names":["ResponseError"],"mappings":"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"}
@@ -2,9 +2,10 @@
2
2
 
3
3
  var backendPluginApi = require('@backstage/backend-plugin-api');
4
4
  var DefaultActionsService = require('./DefaultActionsService.cjs.js');
5
+ var alpha = require('@backstage/backend-plugin-api/alpha');
5
6
 
6
7
  const actionsServiceFactory = backendPluginApi.createServiceFactory({
7
- service: backendPluginApi.coreServices.actions,
8
+ service: alpha.actionsServiceRef,
8
9
  deps: {
9
10
  discovery: backendPluginApi.coreServices.discovery,
10
11
  config: backendPluginApi.coreServices.rootConfig,
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actionsServiceFactory.cjs.js","sources":["../../../../src/alpha/entrypoints/actions/actionsServiceFactory.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 { createServiceFactory } from '@backstage/backend-plugin-api';\nimport { coreServices } from '@backstage/backend-plugin-api';\nimport { DefaultActionsService } from './DefaultActionsService';\nimport { actionsServiceRef } from '@backstage/backend-plugin-api/alpha';\n\n/**\n * @public\n */\nexport const actionsServiceFactory = createServiceFactory({\n service: actionsServiceRef,\n deps: {\n discovery: coreServices.discovery,\n config: coreServices.rootConfig,\n logger: coreServices.logger,\n auth: coreServices.auth,\n },\n factory: ({ discovery, config, logger, auth }) =>\n DefaultActionsService.create({\n discovery,\n config,\n logger,\n auth,\n }),\n});\n"],"names":["createServiceFactory","actionsServiceRef","coreServices","DefaultActionsService"],"mappings":";;;;;;AAuBO,MAAM,wBAAwBA,qCAAqB,CAAA;AAAA,EACxD,OAAS,EAAAC,uBAAA;AAAA,EACT,IAAM,EAAA;AAAA,IACJ,WAAWC,6BAAa,CAAA,SAAA;AAAA,IACxB,QAAQA,6BAAa,CAAA,UAAA;AAAA,IACrB,QAAQA,6BAAa,CAAA,MAAA;AAAA,IACrB,MAAMA,6BAAa,CAAA;AAAA,GACrB;AAAA,EACA,OAAA,EAAS,CAAC,EAAE,SAAA,EAAW,QAAQ,MAAQ,EAAA,IAAA,EACrC,KAAAC,2CAAA,CAAsB,MAAO,CAAA;AAAA,IAC3B,SAAA;AAAA,IACA,MAAA;AAAA,IACA,MAAA;AAAA,IACA;AAAA,GACD;AACL,CAAC;;;;"}
@@ -35,9 +35,16 @@ class DefaultActionsRegistryService {
35
35
  actions: Array.from(this.actions.entries()).map(([id, action]) => ({
36
36
  id,
37
37
  ...action,
38
+ attributes: {
39
+ // Inspired by the @modelcontextprotocol/sdk defaults for the hints.
40
+ // https://github.com/modelcontextprotocol/typescript-sdk/blob/dd69efa1de8646bb6b195ff8d5f52e13739f4550/src/types.ts#L777-L812
41
+ destructive: action.attributes?.destructive ?? true,
42
+ idempotent: action.attributes?.idempotent ?? false,
43
+ readOnly: action.attributes?.readOnly ?? false
44
+ },
38
45
  schema: {
39
- input: action.schema?.input ? zodToJsonSchema__default.default(action.schema.input(zod.z)) : zodToJsonSchema__default.default(zod.z.any()),
40
- output: action.schema?.output ? zodToJsonSchema__default.default(action.schema.output(zod.z)) : zodToJsonSchema__default.default(zod.z.any())
46
+ input: action.schema?.input ? zodToJsonSchema__default.default(action.schema.input(zod.z)) : zodToJsonSchema__default.default(zod.z.object({})),
47
+ output: action.schema?.output ? zodToJsonSchema__default.default(action.schema.output(zod.z)) : zodToJsonSchema__default.default(zod.z.object({}))
41
48
  }
42
49
  }))
43
50
  });
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DefaultActionsRegistryService.cjs.js","sources":["../../../../src/alpha/entrypoints/actionsRegistry/DefaultActionsRegistryService.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 */\n\nimport {\n AuthService,\n HttpAuthService,\n LoggerService,\n PluginMetadataService,\n} from '@backstage/backend-plugin-api';\nimport PromiseRouter from 'express-promise-router';\nimport { Router, json } from 'express';\nimport { z, AnyZodObject } from 'zod';\nimport zodToJsonSchema from 'zod-to-json-schema';\nimport {\n ActionsRegistryActionOptions,\n ActionsRegistryService,\n} from '@backstage/backend-plugin-api/alpha';\nimport {\n ForwardedError,\n InputError,\n NotAllowedError,\n NotFoundError,\n} from '@backstage/errors';\n\nexport class DefaultActionsRegistryService implements ActionsRegistryService {\n private actions: Map<string, ActionsRegistryActionOptions<any, any>> =\n new Map();\n\n private constructor(\n private readonly logger: LoggerService,\n private readonly httpAuth: HttpAuthService,\n private readonly auth: AuthService,\n private readonly metadata: PluginMetadataService,\n ) {}\n\n static create({\n httpAuth,\n logger,\n auth,\n metadata,\n }: {\n httpAuth: HttpAuthService;\n logger: LoggerService;\n auth: AuthService;\n metadata: PluginMetadataService;\n }): DefaultActionsRegistryService {\n return new DefaultActionsRegistryService(logger, httpAuth, auth, metadata);\n }\n\n createRouter(): Router {\n const router = PromiseRouter();\n router.use(json());\n\n router.get('/.backstage/actions/v1/actions', (_, res) => {\n return res.json({\n actions: Array.from(this.actions.entries()).map(([id, action]) => ({\n id,\n ...action,\n attributes: {\n // Inspired by the @modelcontextprotocol/sdk defaults for the hints.\n // https://github.com/modelcontextprotocol/typescript-sdk/blob/dd69efa1de8646bb6b195ff8d5f52e13739f4550/src/types.ts#L777-L812\n destructive: action.attributes?.destructive ?? true,\n idempotent: action.attributes?.idempotent ?? false,\n readOnly: action.attributes?.readOnly ?? false,\n },\n schema: {\n input: action.schema?.input\n ? zodToJsonSchema(action.schema.input(z))\n : zodToJsonSchema(z.object({})),\n output: action.schema?.output\n ? zodToJsonSchema(action.schema.output(z))\n : zodToJsonSchema(z.object({})),\n },\n })),\n });\n });\n\n router.post(\n '/.backstage/actions/v1/actions/:actionId/invoke',\n async (req, res) => {\n const credentials = await this.httpAuth.credentials(req);\n if (this.auth.isPrincipal(credentials, 'user')) {\n if (!credentials.principal.actor) {\n throw new NotAllowedError(\n `Actions must be invoked by a service, not a user`,\n );\n }\n } else if (this.auth.isPrincipal(credentials, 'none')) {\n throw new NotAllowedError(\n `Actions must be invoked by a service, not an anonymous request`,\n );\n }\n\n const action = this.actions.get(req.params.actionId);\n\n if (!action) {\n throw new NotFoundError(`Action \"${req.params.actionId}\" not found`);\n }\n\n const input = action.schema?.input\n ? action.schema.input(z).safeParse(req.body)\n : ({ success: true, data: undefined } as const);\n\n if (!input.success) {\n throw new InputError(\n `Invalid input to action \"${req.params.actionId}\"`,\n input.error,\n );\n }\n\n try {\n const result = await action.action({\n input: input.data,\n credentials,\n logger: this.logger,\n });\n\n const output = action.schema?.output\n ? action.schema.output(z).safeParse(result?.output)\n : ({ success: true, data: result?.output } as const);\n\n if (!output.success) {\n throw new InputError(\n `Invalid output from action \"${req.params.actionId}\"`,\n output.error,\n );\n }\n\n res.json({ output: output.data });\n } catch (error) {\n throw new ForwardedError(\n `Failed execution of action \"${req.params.actionId}\"`,\n error,\n );\n }\n },\n );\n return router;\n }\n\n register<\n TInputSchema extends AnyZodObject,\n TOutputSchema extends AnyZodObject,\n >(options: ActionsRegistryActionOptions<TInputSchema, TOutputSchema>): void {\n const id = `${this.metadata.getId()}:${options.name}`;\n\n if (this.actions.has(id)) {\n throw new Error(`Action with id \"${id}\" is already registered`);\n }\n\n this.actions.set(id, options);\n }\n}\n"],"names":["PromiseRouter","json","zodToJsonSchema","z","NotAllowedError","NotFoundError","InputError","ForwardedError"],"mappings":"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"}
@@ -2,9 +2,10 @@
2
2
 
3
3
  var backendPluginApi = require('@backstage/backend-plugin-api');
4
4
  var DefaultActionsRegistryService = require('./DefaultActionsRegistryService.cjs.js');
5
+ var alpha = require('@backstage/backend-plugin-api/alpha');
5
6
 
6
7
  const actionsRegistryServiceFactory = backendPluginApi.createServiceFactory({
7
- service: backendPluginApi.coreServices.actionsRegistry,
8
+ service: alpha.actionsRegistryServiceRef,
8
9
  deps: {
9
10
  metadata: backendPluginApi.coreServices.pluginMetadata,
10
11
  httpRouter: backendPluginApi.coreServices.httpRouter,
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actionsRegistryServiceFactory.cjs.js","sources":["../../../../src/alpha/entrypoints/actionsRegistry/actionsRegistryServiceFactory.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 */\n\nimport {\n coreServices,\n createServiceFactory,\n} from '@backstage/backend-plugin-api';\nimport { DefaultActionsRegistryService } from './DefaultActionsRegistryService';\nimport { actionsRegistryServiceRef } from '@backstage/backend-plugin-api/alpha';\n\n/**\n * @public\n */\nexport const actionsRegistryServiceFactory = createServiceFactory({\n service: actionsRegistryServiceRef,\n deps: {\n metadata: coreServices.pluginMetadata,\n httpRouter: coreServices.httpRouter,\n httpAuth: coreServices.httpAuth,\n logger: coreServices.logger,\n auth: coreServices.auth,\n },\n factory: ({ metadata, httpRouter, httpAuth, logger, auth }) => {\n const actionsRegistryService = DefaultActionsRegistryService.create({\n httpAuth,\n logger,\n auth,\n metadata,\n });\n\n httpRouter.use(actionsRegistryService.createRouter());\n\n return actionsRegistryService;\n },\n});\n"],"names":["createServiceFactory","actionsRegistryServiceRef","coreServices","DefaultActionsRegistryService"],"mappings":";;;;;;AA0BO,MAAM,gCAAgCA,qCAAqB,CAAA;AAAA,EAChE,OAAS,EAAAC,+BAAA;AAAA,EACT,IAAM,EAAA;AAAA,IACJ,UAAUC,6BAAa,CAAA,cAAA;AAAA,IACvB,YAAYA,6BAAa,CAAA,UAAA;AAAA,IACzB,UAAUA,6BAAa,CAAA,QAAA;AAAA,IACvB,QAAQA,6BAAa,CAAA,MAAA;AAAA,IACrB,MAAMA,6BAAa,CAAA;AAAA,GACrB;AAAA,EACA,OAAA,EAAS,CAAC,EAAE,QAAA,EAAU,YAAY,QAAU,EAAA,MAAA,EAAQ,MAAW,KAAA;AAC7D,IAAM,MAAA,sBAAA,GAAyBC,4DAA8B,MAAO,CAAA;AAAA,MAClE,QAAA;AAAA,MACA,MAAA;AAAA,MACA,IAAA;AAAA,MACA;AAAA,KACD,CAAA;AAED,IAAW,UAAA,CAAA,GAAA,CAAI,sBAAuB,CAAA,YAAA,EAAc,CAAA;AAEpD,IAAO,OAAA,sBAAA;AAAA;AAEX,CAAC;;;;"}
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+
3
+ var actionsRegistryServiceFactory = require('./alpha/entrypoints/actionsRegistry/actionsRegistryServiceFactory.cjs.js');
4
+ var actionsServiceFactory = require('./alpha/entrypoints/actions/actionsServiceFactory.cjs.js');
5
+
6
+
7
+
8
+ exports.actionsRegistryServiceFactory = actionsRegistryServiceFactory.actionsRegistryServiceFactory;
9
+ exports.actionsServiceFactory = actionsServiceFactory.actionsServiceFactory;
10
+ //# sourceMappingURL=alpha.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alpha.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;"}
@@ -0,0 +1,14 @@
1
+ import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
2
+ import * as _backstage_backend_plugin_api_alpha from '@backstage/backend-plugin-api/alpha';
3
+
4
+ /**
5
+ * @public
6
+ */
7
+ declare const actionsRegistryServiceFactory: _backstage_backend_plugin_api.ServiceFactory<_backstage_backend_plugin_api_alpha.ActionsRegistryService, "plugin", "singleton">;
8
+
9
+ /**
10
+ * @public
11
+ */
12
+ declare const actionsServiceFactory: _backstage_backend_plugin_api.ServiceFactory<_backstage_backend_plugin_api_alpha.ActionsService, "plugin", "singleton">;
13
+
14
+ export { actionsRegistryServiceFactory, actionsServiceFactory };
package/dist/auditor.d.ts CHANGED
@@ -2,6 +2,7 @@ import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
2
2
  import { AuditorServiceEventSeverityLevel, AuditorService, AuthService, HttpAuthService, PluginMetadataService, AuditorServiceCreateEventOptions, AuditorServiceEvent } from '@backstage/backend-plugin-api';
3
3
  import { JsonObject } from '@backstage/types';
4
4
  import { Request } from 'express';
5
+ import { Config } from '@backstage/config';
5
6
  import { Format } from 'logform';
6
7
  import * as winston from 'winston';
7
8
 
@@ -24,7 +25,7 @@ type AuditorEventStatus = {
24
25
  status: 'succeeded';
25
26
  } | {
26
27
  status: 'failed';
27
- error: string;
28
+ error: Error;
28
29
  };
29
30
  /**
30
31
  * Options for creating an auditor event.
@@ -158,6 +159,7 @@ declare class WinstonRootAuditorService {
158
159
  static create(options?: WinstonRootAuditorServiceOptions): WinstonRootAuditorService;
159
160
  forPlugin(deps: {
160
161
  auth: AuthService;
162
+ config: Config;
161
163
  httpAuth: HttpAuthService;
162
164
  plugin: PluginMetadataService;
163
165
  }): AuditorService;
@@ -53,7 +53,7 @@ class DefaultAuditorService {
53
53
  await this.log({
54
54
  ...options,
55
55
  ...params,
56
- error: String(params.error),
56
+ error: params.error,
57
57
  meta: { ...options.meta, ...params?.meta },
58
58
  status: "failed"
59
59
  });
@@ -1 +1 @@
1
- {"version":3,"file":"DefaultAuditorService.cjs.js","sources":["../../../src/entrypoints/auditor/DefaultAuditorService.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 type {\n AuditorService,\n AuditorServiceCreateEventOptions,\n AuditorServiceEvent,\n AuditorServiceEventSeverityLevel,\n AuthService,\n BackstageCredentials,\n HttpAuthService,\n PluginMetadataService,\n} from '@backstage/backend-plugin-api';\nimport { ForwardedError } from '@backstage/errors';\nimport type { JsonObject } from '@backstage/types';\nimport type { Request } from 'express';\n\n/** @public */\nexport type AuditorEventActorDetails = {\n actorId?: string;\n ip?: string;\n hostname?: string;\n userAgent?: string;\n};\n\n/** @public */\nexport type AuditorEventRequest = {\n url: string;\n method: string;\n};\n\n/** @public */\nexport type AuditorEventStatus =\n | { status: 'initiated' }\n | { status: 'succeeded' }\n | {\n status: 'failed';\n error: string;\n };\n\n/**\n * Options for creating an auditor event.\n *\n * @public\n */\nexport type AuditorEventOptions<TMeta extends JsonObject> = {\n /**\n * Use kebab-case to name audit events (e.g., \"user-login\", \"file-download\").\n *\n * The `pluginId` already provides plugin/module context, so avoid redundant prefixes in the `eventId`.\n */\n eventId: string;\n\n severityLevel?: AuditorServiceEventSeverityLevel;\n\n /** (Optional) The associated HTTP request, if applicable. */\n request?: Request<any, any, any, any, any>;\n\n /** (Optional) Additional metadata relevant to the event, structured as a JSON object. */\n meta?: TMeta;\n} & AuditorEventStatus;\n\n/**\n * Common fields of an audit event.\n *\n * @public\n */\nexport type AuditorEvent = {\n plugin: string;\n eventId: string;\n severityLevel: AuditorServiceEventSeverityLevel;\n actor: AuditorEventActorDetails;\n meta?: JsonObject;\n request?: AuditorEventRequest;\n} & AuditorEventStatus;\n\n/**\n * Logging function used by the auditor.\n * @public\n */\nexport type AuditorLogFunction = (event: AuditorEvent) => void | Promise<void>;\n\n/**\n * A {@link @backstage/backend-plugin-api#AuditorService} implementation that logs events using a provided callback.\n *\n * @public\n *\n * @example\n * ```ts\n * export const auditorServiceFactory = createServiceFactory({\n * service: coreServices.auditor,\n * deps: {\n * logger: coreServices.logger,\n * auth: coreServices.auth,\n * httpAuth: coreServices.httpAuth,\n * plugin: coreServices.pluginMetadata,\n * },\n * factory({ logger, plugin, auth, httpAuth }) {\n * const auditLogger = logger.child({ isAuditEvent: true });\n * return DefaultAuditorService.create(\n * event => auditLogger.info(`${event.plugin}.${event.eventId}`, event),\n * { plugin, auth, httpAuth },\n * );\n * },\n * });\n * ```\n */\nexport class DefaultAuditorService implements AuditorService {\n private readonly logFn: AuditorLogFunction;\n private readonly auth: AuthService;\n private readonly httpAuth: HttpAuthService;\n private readonly plugin: PluginMetadataService;\n\n private constructor(\n logFn: AuditorLogFunction,\n deps: {\n auth: AuthService;\n httpAuth: HttpAuthService;\n plugin: PluginMetadataService;\n },\n ) {\n this.logFn = logFn;\n this.auth = deps.auth;\n this.httpAuth = deps.httpAuth;\n this.plugin = deps.plugin;\n }\n\n /**\n * Creates a {@link DefaultAuditorService} instance.\n */\n static create(\n logFn: AuditorLogFunction,\n deps: {\n auth: AuthService;\n httpAuth: HttpAuthService;\n plugin: PluginMetadataService;\n },\n ): DefaultAuditorService {\n return new DefaultAuditorService(logFn, deps);\n }\n\n private async log<TMeta extends JsonObject>(\n options: AuditorEventOptions<TMeta>,\n ): Promise<void> {\n const { eventId, severityLevel = 'low', request, meta, ...rest } = options;\n\n await this.logFn({\n plugin: this.plugin.getId(),\n eventId,\n severityLevel,\n actor: {\n actorId: await this.getActorId(request),\n ip: request?.ip,\n hostname: request?.hostname,\n userAgent: request?.get('user-agent'),\n },\n request: request\n ? {\n url: request?.originalUrl,\n method: request?.method,\n }\n : undefined,\n meta: Object.keys(meta ?? {}).length === 0 ? undefined : meta,\n ...rest,\n });\n }\n\n async createEvent(\n options: AuditorServiceCreateEventOptions,\n ): Promise<AuditorServiceEvent> {\n await this.log({ ...options, status: 'initiated' });\n\n return {\n success: async params => {\n await this.log({\n ...options,\n meta: { ...options.meta, ...params?.meta },\n status: 'succeeded',\n });\n },\n fail: async params => {\n await this.log({\n ...options,\n ...params,\n error: String(params.error),\n meta: { ...options.meta, ...params?.meta },\n status: 'failed',\n });\n },\n };\n }\n\n private async getActorId(\n request?: Request<any, any, any, any, any>,\n ): Promise<string | undefined> {\n let credentials: BackstageCredentials =\n await this.auth.getOwnServiceCredentials();\n\n if (request) {\n try {\n credentials = await this.httpAuth.credentials(request);\n } catch (error) {\n throw new ForwardedError('Could not resolve credentials', error);\n }\n }\n\n if (this.auth.isPrincipal(credentials, 'user')) {\n return credentials.principal.userEntityRef;\n }\n\n if (this.auth.isPrincipal(credentials, 'service')) {\n return credentials.principal.subject;\n }\n\n return undefined;\n }\n}\n"],"names":["ForwardedError"],"mappings":"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"}
1
+ {"version":3,"file":"DefaultAuditorService.cjs.js","sources":["../../../src/entrypoints/auditor/DefaultAuditorService.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 type {\n AuditorService,\n AuditorServiceCreateEventOptions,\n AuditorServiceEvent,\n AuditorServiceEventSeverityLevel,\n AuthService,\n BackstageCredentials,\n HttpAuthService,\n PluginMetadataService,\n} from '@backstage/backend-plugin-api';\nimport { ForwardedError } from '@backstage/errors';\nimport type { JsonObject } from '@backstage/types';\nimport type { Request } from 'express';\n\n/** @public */\nexport type AuditorEventActorDetails = {\n actorId?: string;\n ip?: string;\n hostname?: string;\n userAgent?: string;\n};\n\n/** @public */\nexport type AuditorEventRequest = {\n url: string;\n method: string;\n};\n\n/** @public */\nexport type AuditorEventStatus =\n | { status: 'initiated' }\n | { status: 'succeeded' }\n | {\n status: 'failed';\n error: Error;\n };\n\n/**\n * Options for creating an auditor event.\n *\n * @public\n */\nexport type AuditorEventOptions<TMeta extends JsonObject> = {\n /**\n * Use kebab-case to name audit events (e.g., \"user-login\", \"file-download\").\n *\n * The `pluginId` already provides plugin/module context, so avoid redundant prefixes in the `eventId`.\n */\n eventId: string;\n\n severityLevel?: AuditorServiceEventSeverityLevel;\n\n /** (Optional) The associated HTTP request, if applicable. */\n request?: Request<any, any, any, any, any>;\n\n /** (Optional) Additional metadata relevant to the event, structured as a JSON object. */\n meta?: TMeta;\n} & AuditorEventStatus;\n\n/**\n * Common fields of an audit event.\n *\n * @public\n */\nexport type AuditorEvent = {\n plugin: string;\n eventId: string;\n severityLevel: AuditorServiceEventSeverityLevel;\n actor: AuditorEventActorDetails;\n meta?: JsonObject;\n request?: AuditorEventRequest;\n} & AuditorEventStatus;\n\n/**\n * Logging function used by the auditor.\n * @public\n */\nexport type AuditorLogFunction = (event: AuditorEvent) => void | Promise<void>;\n\n/**\n * A {@link @backstage/backend-plugin-api#AuditorService} implementation that logs events using a provided callback.\n *\n * @public\n *\n * @example\n * ```ts\n * export const auditorServiceFactory = createServiceFactory({\n * service: coreServices.auditor,\n * deps: {\n * logger: coreServices.logger,\n * auth: coreServices.auth,\n * httpAuth: coreServices.httpAuth,\n * plugin: coreServices.pluginMetadata,\n * },\n * factory({ logger, plugin, auth, httpAuth }) {\n * const auditLogger = logger.child({ isAuditEvent: true });\n * return DefaultAuditorService.create(\n * event => auditLogger.info(`${event.plugin}.${event.eventId}`, event),\n * { plugin, auth, httpAuth },\n * );\n * },\n * });\n * ```\n */\nexport class DefaultAuditorService implements AuditorService {\n private readonly logFn: AuditorLogFunction;\n private readonly auth: AuthService;\n private readonly httpAuth: HttpAuthService;\n private readonly plugin: PluginMetadataService;\n\n private constructor(\n logFn: AuditorLogFunction,\n deps: {\n auth: AuthService;\n httpAuth: HttpAuthService;\n plugin: PluginMetadataService;\n },\n ) {\n this.logFn = logFn;\n this.auth = deps.auth;\n this.httpAuth = deps.httpAuth;\n this.plugin = deps.plugin;\n }\n\n /**\n * Creates a {@link DefaultAuditorService} instance.\n */\n static create(\n logFn: AuditorLogFunction,\n deps: {\n auth: AuthService;\n httpAuth: HttpAuthService;\n plugin: PluginMetadataService;\n },\n ): DefaultAuditorService {\n return new DefaultAuditorService(logFn, deps);\n }\n\n private async log<TMeta extends JsonObject>(\n options: AuditorEventOptions<TMeta>,\n ): Promise<void> {\n const { eventId, severityLevel = 'low', request, meta, ...rest } = options;\n\n await this.logFn({\n plugin: this.plugin.getId(),\n eventId,\n severityLevel,\n actor: {\n actorId: await this.getActorId(request),\n ip: request?.ip,\n hostname: request?.hostname,\n userAgent: request?.get('user-agent'),\n },\n request: request\n ? {\n url: request?.originalUrl,\n method: request?.method,\n }\n : undefined,\n meta: Object.keys(meta ?? {}).length === 0 ? undefined : meta,\n ...rest,\n });\n }\n\n async createEvent(\n options: AuditorServiceCreateEventOptions,\n ): Promise<AuditorServiceEvent> {\n await this.log({ ...options, status: 'initiated' });\n\n return {\n success: async params => {\n await this.log({\n ...options,\n meta: { ...options.meta, ...params?.meta },\n status: 'succeeded',\n });\n },\n fail: async params => {\n await this.log({\n ...options,\n ...params,\n error: params.error,\n meta: { ...options.meta, ...params?.meta },\n status: 'failed',\n });\n },\n };\n }\n\n private async getActorId(\n request?: Request<any, any, any, any, any>,\n ): Promise<string | undefined> {\n let credentials: BackstageCredentials =\n await this.auth.getOwnServiceCredentials();\n\n if (request) {\n try {\n credentials = await this.httpAuth.credentials(request);\n } catch (error) {\n throw new ForwardedError('Could not resolve credentials', error);\n }\n }\n\n if (this.auth.isPrincipal(credentials, 'user')) {\n return credentials.principal.userEntityRef;\n }\n\n if (this.auth.isPrincipal(credentials, 'service')) {\n return credentials.principal.subject;\n }\n\n return undefined;\n }\n}\n"],"names":["ForwardedError"],"mappings":"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"}
@@ -4,6 +4,7 @@ var winston = require('winston');
4
4
  require('../rootLogger/rootLoggerServiceFactory.cjs.js');
5
5
  var WinstonLogger = require('../rootLogger/WinstonLogger.cjs.js');
6
6
  var DefaultAuditorService = require('./DefaultAuditorService.cjs.js');
7
+ var utils = require('./utils.cjs.js');
7
8
 
8
9
  function _interopNamespaceCompat(e) {
9
10
  if (e && typeof e === 'object' && 'default' in e) return e;
@@ -61,10 +62,22 @@ class WinstonRootAuditorService {
61
62
  return new WinstonRootAuditorService(winstonLogger);
62
63
  }
63
64
  forPlugin(deps) {
64
- return DefaultAuditorService.DefaultAuditorService.create(
65
- (e) => this.winstonLogger.info(`${e.plugin}.${e.eventId}`, e),
66
- deps
67
- );
65
+ const severityLogLevelMappings = utils.getSeverityLogLevelMappings(deps.config);
66
+ return DefaultAuditorService.DefaultAuditorService.create((event) => {
67
+ if ("error" in event) {
68
+ const { error, ...rest } = event;
69
+ const childAuditLogger = this.winstonLogger.child(rest);
70
+ childAuditLogger[severityLogLevelMappings[event.severityLevel]](
71
+ `${event.plugin}.${event.eventId}`,
72
+ error
73
+ );
74
+ } else {
75
+ this.winstonLogger[severityLogLevelMappings[event.severityLevel]](
76
+ `${event.plugin}.${event.eventId}`,
77
+ event
78
+ );
79
+ }
80
+ }, deps);
68
81
  }
69
82
  }
70
83
 
@@ -1 +1 @@
1
- {"version":3,"file":"WinstonRootAuditorService.cjs.js","sources":["../../../src/entrypoints/auditor/WinstonRootAuditorService.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 */\n\nimport type {\n AuditorService,\n AuthService,\n HttpAuthService,\n PluginMetadataService,\n} from '@backstage/backend-plugin-api';\nimport type { JsonObject } from '@backstage/types';\nimport type { Format } from 'logform';\nimport * as winston from 'winston';\nimport { WinstonLogger } from '../rootLogger';\nimport { DefaultAuditorService } from './DefaultAuditorService';\n\n/** @public */\nexport const defaultFormatter = winston.format.combine(\n winston.format.timestamp({\n format: 'YYYY-MM-DD HH:mm:ss',\n }),\n winston.format.errors({ stack: true }),\n winston.format.splat(),\n winston.format.json(),\n);\n\n/**\n * Adds `isAuditEvent` field\n *\n * @public\n */\nexport const auditorFieldFormat = winston.format(info => {\n return { ...info, isAuditEvent: true };\n})();\n\n/**\n * Options for creating a {@link WinstonRootAuditorService}.\n * @public\n */\nexport type WinstonRootAuditorServiceOptions = {\n meta?: JsonObject;\n format?: Format;\n transports?: winston.transport[];\n};\n\n/**\n * An implementation of the {@link @backstage/backend-plugin-api#AuditorService} that logs events using a separate winston logger.\n *\n * @public\n *\n * @example\n * ```ts\n * export const auditorServiceFactory = createServiceFactory({\n * service: coreServices.auditor,\n * deps: {\n * auth: coreServices.auth,\n * httpAuth: coreServices.httpAuth,\n * plugin: coreServices.pluginMetadata,\n * },\n * createRootContext() {\n * return WinstonRootAuditorService.create();\n * },\n * factory({ plugin, auth, httpAuth }, root) {\n * return root.forPlugin({ plugin, auth, httpAuth });\n * },\n * });\n * ```\n */\nexport class WinstonRootAuditorService {\n private constructor(private readonly winstonLogger: WinstonLogger) {}\n\n /**\n * Creates a {@link WinstonRootAuditorService} instance.\n */\n static create(\n options?: WinstonRootAuditorServiceOptions,\n ): WinstonRootAuditorService {\n let winstonLogger = WinstonLogger.create({\n meta: {\n service: 'backstage',\n },\n level: 'info',\n format: winston.format.combine(\n auditorFieldFormat,\n options?.format ?? defaultFormatter,\n ),\n transports: options?.transports,\n });\n\n if (options?.meta) {\n winstonLogger = winstonLogger.child(options.meta) as WinstonLogger;\n }\n\n return new WinstonRootAuditorService(winstonLogger);\n }\n\n forPlugin(deps: {\n auth: AuthService;\n httpAuth: HttpAuthService;\n plugin: PluginMetadataService;\n }): AuditorService {\n return DefaultAuditorService.create(\n e => this.winstonLogger.info(`${e.plugin}.${e.eventId}`, e),\n deps,\n );\n }\n}\n"],"names":["winston","WinstonLogger","DefaultAuditorService"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AA6Ba,MAAA,gBAAA,GAAmBA,mBAAQ,MAAO,CAAA,OAAA;AAAA,EAC7CA,kBAAA,CAAQ,OAAO,SAAU,CAAA;AAAA,IACvB,MAAQ,EAAA;AAAA,GACT,CAAA;AAAA,EACDA,mBAAQ,MAAO,CAAA,MAAA,CAAO,EAAE,KAAA,EAAO,MAAM,CAAA;AAAA,EACrCA,kBAAA,CAAQ,OAAO,KAAM,EAAA;AAAA,EACrBA,kBAAA,CAAQ,OAAO,IAAK;AACtB;AAOa,MAAA,kBAAA,GAAqBA,kBAAQ,CAAA,MAAA,CAAO,CAAQ,IAAA,KAAA;AACvD,EAAA,OAAO,EAAE,GAAG,IAAM,EAAA,YAAA,EAAc,IAAK,EAAA;AACvC,CAAC,CAAE;AAmCI,MAAM,yBAA0B,CAAA;AAAA,EAC7B,YAA6B,aAA8B,EAAA;AAA9B,IAAA,IAAA,CAAA,aAAA,GAAA,aAAA;AAAA;AAA+B;AAAA;AAAA;AAAA,EAKpE,OAAO,OACL,OAC2B,EAAA;AAC3B,IAAI,IAAA,aAAA,GAAgBC,4BAAc,MAAO,CAAA;AAAA,MACvC,IAAM,EAAA;AAAA,QACJ,OAAS,EAAA;AAAA,OACX;AAAA,MACA,KAAO,EAAA,MAAA;AAAA,MACP,MAAA,EAAQD,mBAAQ,MAAO,CAAA,OAAA;AAAA,QACrB,kBAAA;AAAA,QACA,SAAS,MAAU,IAAA;AAAA,OACrB;AAAA,MACA,YAAY,OAAS,EAAA;AAAA,KACtB,CAAA;AAED,IAAA,IAAI,SAAS,IAAM,EAAA;AACjB,MAAgB,aAAA,GAAA,aAAA,CAAc,KAAM,CAAA,OAAA,CAAQ,IAAI,CAAA;AAAA;AAGlD,IAAO,OAAA,IAAI,0BAA0B,aAAa,CAAA;AAAA;AACpD,EAEA,UAAU,IAIS,EAAA;AACjB,IAAA,OAAOE,2CAAsB,CAAA,MAAA;AAAA,MAC3B,CAAA,CAAA,KAAK,IAAK,CAAA,aAAA,CAAc,IAAK,CAAA,CAAA,EAAG,CAAE,CAAA,MAAM,CAAI,CAAA,EAAA,CAAA,CAAE,OAAO,CAAA,CAAA,EAAI,CAAC,CAAA;AAAA,MAC1D;AAAA,KACF;AAAA;AAEJ;;;;;;"}
1
+ {"version":3,"file":"WinstonRootAuditorService.cjs.js","sources":["../../../src/entrypoints/auditor/WinstonRootAuditorService.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 */\n\nimport type {\n AuditorService,\n AuthService,\n HttpAuthService,\n PluginMetadataService,\n} from '@backstage/backend-plugin-api';\nimport { Config } from '@backstage/config';\nimport type { JsonObject } from '@backstage/types';\nimport type { Format } from 'logform';\nimport * as winston from 'winston';\nimport { WinstonLogger } from '../rootLogger';\nimport { DefaultAuditorService } from './DefaultAuditorService';\nimport { getSeverityLogLevelMappings } from './utils';\n\n/** @public */\nexport const defaultFormatter = winston.format.combine(\n winston.format.timestamp({\n format: 'YYYY-MM-DD HH:mm:ss',\n }),\n winston.format.errors({ stack: true }),\n winston.format.splat(),\n winston.format.json(),\n);\n\n/**\n * Adds `isAuditEvent` field\n *\n * @public\n */\nexport const auditorFieldFormat = winston.format(info => {\n return { ...info, isAuditEvent: true };\n})();\n\n/**\n * Options for creating a {@link WinstonRootAuditorService}.\n * @public\n */\nexport type WinstonRootAuditorServiceOptions = {\n meta?: JsonObject;\n format?: Format;\n transports?: winston.transport[];\n};\n\n/**\n * An implementation of the {@link @backstage/backend-plugin-api#AuditorService} that logs events using a separate winston logger.\n *\n * @public\n *\n * @example\n * ```ts\n * export const auditorServiceFactory = createServiceFactory({\n * service: coreServices.auditor,\n * deps: {\n * auth: coreServices.auth,\n * httpAuth: coreServices.httpAuth,\n * plugin: coreServices.pluginMetadata,\n * },\n * createRootContext() {\n * return WinstonRootAuditorService.create();\n * },\n * factory({ plugin, auth, httpAuth }, root) {\n * return root.forPlugin({ plugin, auth, httpAuth });\n * },\n * });\n * ```\n */\nexport class WinstonRootAuditorService {\n private constructor(private readonly winstonLogger: WinstonLogger) {}\n\n /**\n * Creates a {@link WinstonRootAuditorService} instance.\n */\n static create(\n options?: WinstonRootAuditorServiceOptions,\n ): WinstonRootAuditorService {\n let winstonLogger = WinstonLogger.create({\n meta: {\n service: 'backstage',\n },\n level: 'info',\n format: winston.format.combine(\n auditorFieldFormat,\n options?.format ?? defaultFormatter,\n ),\n transports: options?.transports,\n });\n\n if (options?.meta) {\n winstonLogger = winstonLogger.child(options.meta) as WinstonLogger;\n }\n\n return new WinstonRootAuditorService(winstonLogger);\n }\n\n forPlugin(deps: {\n auth: AuthService;\n config: Config;\n httpAuth: HttpAuthService;\n plugin: PluginMetadataService;\n }): AuditorService {\n const severityLogLevelMappings = getSeverityLogLevelMappings(deps.config);\n\n return DefaultAuditorService.create(event => {\n if ('error' in event) {\n const { error, ...rest } = event;\n const childAuditLogger = this.winstonLogger.child(rest);\n\n childAuditLogger[severityLogLevelMappings[event.severityLevel]](\n `${event.plugin}.${event.eventId}`,\n error,\n );\n } else {\n // the else statement is required for typechecking\n this.winstonLogger[severityLogLevelMappings[event.severityLevel]](\n `${event.plugin}.${event.eventId}`,\n event,\n );\n }\n }, deps);\n }\n}\n"],"names":["winston","WinstonLogger","getSeverityLogLevelMappings","DefaultAuditorService"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+Ba,MAAA,gBAAA,GAAmBA,mBAAQ,MAAO,CAAA,OAAA;AAAA,EAC7CA,kBAAA,CAAQ,OAAO,SAAU,CAAA;AAAA,IACvB,MAAQ,EAAA;AAAA,GACT,CAAA;AAAA,EACDA,mBAAQ,MAAO,CAAA,MAAA,CAAO,EAAE,KAAA,EAAO,MAAM,CAAA;AAAA,EACrCA,kBAAA,CAAQ,OAAO,KAAM,EAAA;AAAA,EACrBA,kBAAA,CAAQ,OAAO,IAAK;AACtB;AAOa,MAAA,kBAAA,GAAqBA,kBAAQ,CAAA,MAAA,CAAO,CAAQ,IAAA,KAAA;AACvD,EAAA,OAAO,EAAE,GAAG,IAAM,EAAA,YAAA,EAAc,IAAK,EAAA;AACvC,CAAC,CAAE;AAmCI,MAAM,yBAA0B,CAAA;AAAA,EAC7B,YAA6B,aAA8B,EAAA;AAA9B,IAAA,IAAA,CAAA,aAAA,GAAA,aAAA;AAAA;AAA+B;AAAA;AAAA;AAAA,EAKpE,OAAO,OACL,OAC2B,EAAA;AAC3B,IAAI,IAAA,aAAA,GAAgBC,4BAAc,MAAO,CAAA;AAAA,MACvC,IAAM,EAAA;AAAA,QACJ,OAAS,EAAA;AAAA,OACX;AAAA,MACA,KAAO,EAAA,MAAA;AAAA,MACP,MAAA,EAAQD,mBAAQ,MAAO,CAAA,OAAA;AAAA,QACrB,kBAAA;AAAA,QACA,SAAS,MAAU,IAAA;AAAA,OACrB;AAAA,MACA,YAAY,OAAS,EAAA;AAAA,KACtB,CAAA;AAED,IAAA,IAAI,SAAS,IAAM,EAAA;AACjB,MAAgB,aAAA,GAAA,aAAA,CAAc,KAAM,CAAA,OAAA,CAAQ,IAAI,CAAA;AAAA;AAGlD,IAAO,OAAA,IAAI,0BAA0B,aAAa,CAAA;AAAA;AACpD,EAEA,UAAU,IAKS,EAAA;AACjB,IAAM,MAAA,wBAAA,GAA2BE,iCAA4B,CAAA,IAAA,CAAK,MAAM,CAAA;AAExE,IAAO,OAAAC,2CAAA,CAAsB,OAAO,CAAS,KAAA,KAAA;AAC3C,MAAA,IAAI,WAAW,KAAO,EAAA;AACpB,QAAA,MAAM,EAAE,KAAA,EAAO,GAAG,IAAA,EAAS,GAAA,KAAA;AAC3B,QAAA,MAAM,gBAAmB,GAAA,IAAA,CAAK,aAAc,CAAA,KAAA,CAAM,IAAI,CAAA;AAEtD,QAAiB,gBAAA,CAAA,wBAAA,CAAyB,KAAM,CAAA,aAAa,CAAC,CAAA;AAAA,UAC5D,CAAG,EAAA,KAAA,CAAM,MAAM,CAAA,CAAA,EAAI,MAAM,OAAO,CAAA,CAAA;AAAA,UAChC;AAAA,SACF;AAAA,OACK,MAAA;AAEL,QAAA,IAAA,CAAK,aAAc,CAAA,wBAAA,CAAyB,KAAM,CAAA,aAAa,CAAC,CAAA;AAAA,UAC9D,CAAG,EAAA,KAAA,CAAM,MAAM,CAAA,CAAA,EAAI,MAAM,OAAO,CAAA,CAAA;AAAA,UAChC;AAAA,SACF;AAAA;AACF,OACC,IAAI,CAAA;AAAA;AAEX;;;;;;"}
@@ -2,14 +2,8 @@
2
2
 
3
3
  var backendPluginApi = require('@backstage/backend-plugin-api');
4
4
  var DefaultAuditorService = require('./DefaultAuditorService.cjs.js');
5
- var zod = require('zod');
6
- var errors = require('@backstage/errors');
5
+ var utils = require('./utils.cjs.js');
7
6
 
8
- const CONFIG_ROOT_KEY = "backend.auditor";
9
- const severityLogLevelMappingsSchema = zod.z.record(
10
- zod.z.enum(["low", "medium", "high", "critical"]),
11
- zod.z.enum(["debug", "info", "warn", "error"])
12
- );
13
7
  const auditorServiceFactory = backendPluginApi.createServiceFactory({
14
8
  service: backendPluginApi.coreServices.auditor,
15
9
  deps: {
@@ -21,32 +15,22 @@ const auditorServiceFactory = backendPluginApi.createServiceFactory({
21
15
  },
22
16
  factory({ config, logger, plugin, auth, httpAuth }) {
23
17
  const auditLogger = logger.child({ isAuditEvent: true });
24
- const auditorConfig = config.getOptionalConfig(CONFIG_ROOT_KEY);
25
- const severityLogLevelMappings = {
26
- low: auditorConfig?.getOptionalString("severityLogLevelMappings.low") ?? "debug",
27
- medium: auditorConfig?.getOptionalString("severityLogLevelMappings.medium") ?? "info",
28
- high: auditorConfig?.getOptionalString("severityLogLevelMappings.high") ?? "info",
29
- critical: auditorConfig?.getOptionalString("severityLogLevelMappings.critical") ?? "info"
30
- };
31
- const res = severityLogLevelMappingsSchema.safeParse(
32
- severityLogLevelMappings
33
- );
34
- if (!res.success) {
35
- const key = res.error.issues.at(0)?.path.at(0);
36
- const value = res.error.issues.at(0).received;
37
- const validKeys = res.error.issues.at(0).options;
38
- throw new errors.InputError(
39
- `The configuration value for 'backend.auditor.severityLogLevelMappings.${key}' was given an invalid value: '${value}'. Expected one of the following valid values: '${validKeys.join(
40
- ", "
41
- )}'.`
42
- );
43
- }
18
+ const severityLogLevelMappings = utils.getSeverityLogLevelMappings(config);
44
19
  return DefaultAuditorService.DefaultAuditorService.create(
45
20
  (event) => {
46
- auditLogger[severityLogLevelMappings[event.severityLevel]](
47
- `${event.plugin}.${event.eventId}`,
48
- event
49
- );
21
+ if ("error" in event) {
22
+ const { error, ...rest } = event;
23
+ const childAuditLogger = auditLogger.child(rest);
24
+ childAuditLogger[severityLogLevelMappings[event.severityLevel]](
25
+ `${event.plugin}.${event.eventId}`,
26
+ error
27
+ );
28
+ } else {
29
+ auditLogger[severityLogLevelMappings[event.severityLevel]](
30
+ `${event.plugin}.${event.eventId}`,
31
+ event
32
+ );
33
+ }
50
34
  },
51
35
  { plugin, auth, httpAuth }
52
36
  );
@@ -1 +1 @@
1
- {"version":3,"file":"auditorServiceFactory.cjs.js","sources":["../../../src/entrypoints/auditor/auditorServiceFactory.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 coreServices,\n createServiceFactory,\n} from '@backstage/backend-plugin-api';\nimport { DefaultAuditorService } from './DefaultAuditorService';\nimport { z } from 'zod';\nimport { InputError } from '@backstage/errors';\n\nconst CONFIG_ROOT_KEY = 'backend.auditor';\n\nconst severityLogLevelMappingsSchema = z.record(\n z.enum(['low', 'medium', 'high', 'critical']),\n z.enum(['debug', 'info', 'warn', 'error']),\n);\n\n/**\n * Plugin-level auditing.\n *\n * See {@link @backstage/code-plugin-api#AuditorService}\n * and {@link https://backstage.io/docs/backend-system/core-services/auditor | the service docs}\n * for more information.\n *\n * @public\n */\nexport const auditorServiceFactory = createServiceFactory({\n service: coreServices.auditor,\n deps: {\n config: coreServices.rootConfig,\n logger: coreServices.logger,\n auth: coreServices.auth,\n httpAuth: coreServices.httpAuth,\n plugin: coreServices.pluginMetadata,\n },\n factory({ config, logger, plugin, auth, httpAuth }) {\n const auditLogger = logger.child({ isAuditEvent: true });\n const auditorConfig = config.getOptionalConfig(CONFIG_ROOT_KEY);\n\n const severityLogLevelMappings = {\n low:\n auditorConfig?.getOptionalString('severityLogLevelMappings.low') ??\n 'debug',\n medium:\n auditorConfig?.getOptionalString('severityLogLevelMappings.medium') ??\n 'info',\n high:\n auditorConfig?.getOptionalString('severityLogLevelMappings.high') ??\n 'info',\n critical:\n auditorConfig?.getOptionalString('severityLogLevelMappings.critical') ??\n 'info',\n } as Required<z.infer<typeof severityLogLevelMappingsSchema>>;\n\n const res = severityLogLevelMappingsSchema.safeParse(\n severityLogLevelMappings,\n );\n if (!res.success) {\n const key = res.error.issues.at(0)?.path.at(0) as string;\n const value = (\n res.error.issues.at(0) as unknown as Record<PropertyKey, unknown>\n ).received as string;\n const validKeys = (\n res.error.issues.at(0) as unknown as Record<PropertyKey, unknown>\n ).options as string[];\n throw new InputError(\n `The configuration value for 'backend.auditor.severityLogLevelMappings.${key}' was given an invalid value: '${value}'. Expected one of the following valid values: '${validKeys.join(\n ', ',\n )}'.`,\n );\n }\n\n return DefaultAuditorService.create(\n event => {\n auditLogger[severityLogLevelMappings[event.severityLevel]](\n `${event.plugin}.${event.eventId}`,\n event,\n );\n },\n { plugin, auth, httpAuth },\n );\n },\n});\n"],"names":["z","createServiceFactory","coreServices","InputError","DefaultAuditorService"],"mappings":";;;;;;;AAwBA,MAAM,eAAkB,GAAA,iBAAA;AAExB,MAAM,iCAAiCA,KAAE,CAAA,MAAA;AAAA,EACvCA,MAAE,IAAK,CAAA,CAAC,OAAO,QAAU,EAAA,MAAA,EAAQ,UAAU,CAAC,CAAA;AAAA,EAC5CA,MAAE,IAAK,CAAA,CAAC,SAAS,MAAQ,EAAA,MAAA,EAAQ,OAAO,CAAC;AAC3C,CAAA;AAWO,MAAM,wBAAwBC,qCAAqB,CAAA;AAAA,EACxD,SAASC,6BAAa,CAAA,OAAA;AAAA,EACtB,IAAM,EAAA;AAAA,IACJ,QAAQA,6BAAa,CAAA,UAAA;AAAA,IACrB,QAAQA,6BAAa,CAAA,MAAA;AAAA,IACrB,MAAMA,6BAAa,CAAA,IAAA;AAAA,IACnB,UAAUA,6BAAa,CAAA,QAAA;AAAA,IACvB,QAAQA,6BAAa,CAAA;AAAA,GACvB;AAAA,EACA,QAAQ,EAAE,MAAA,EAAQ,QAAQ,MAAQ,EAAA,IAAA,EAAM,UAAY,EAAA;AAClD,IAAA,MAAM,cAAc,MAAO,CAAA,KAAA,CAAM,EAAE,YAAA,EAAc,MAAM,CAAA;AACvD,IAAM,MAAA,aAAA,GAAgB,MAAO,CAAA,iBAAA,CAAkB,eAAe,CAAA;AAE9D,IAAA,MAAM,wBAA2B,GAAA;AAAA,MAC/B,GACE,EAAA,aAAA,EAAe,iBAAkB,CAAA,8BAA8B,CAC/D,IAAA,OAAA;AAAA,MACF,MACE,EAAA,aAAA,EAAe,iBAAkB,CAAA,iCAAiC,CAClE,IAAA,MAAA;AAAA,MACF,IACE,EAAA,aAAA,EAAe,iBAAkB,CAAA,+BAA+B,CAChE,IAAA,MAAA;AAAA,MACF,QACE,EAAA,aAAA,EAAe,iBAAkB,CAAA,mCAAmC,CACpE,IAAA;AAAA,KACJ;AAEA,IAAA,MAAM,MAAM,8BAA+B,CAAA,SAAA;AAAA,MACzC;AAAA,KACF;AACA,IAAI,IAAA,CAAC,IAAI,OAAS,EAAA;AAChB,MAAM,MAAA,GAAA,GAAM,IAAI,KAAM,CAAA,MAAA,CAAO,GAAG,CAAC,CAAA,EAAG,IAAK,CAAA,EAAA,CAAG,CAAC,CAAA;AAC7C,MAAA,MAAM,QACJ,GAAI,CAAA,KAAA,CAAM,MAAO,CAAA,EAAA,CAAG,CAAC,CACrB,CAAA,QAAA;AACF,MAAA,MAAM,YACJ,GAAI,CAAA,KAAA,CAAM,MAAO,CAAA,EAAA,CAAG,CAAC,CACrB,CAAA,OAAA;AACF,MAAA,MAAM,IAAIC,iBAAA;AAAA,QACR,CAAyE,sEAAA,EAAA,GAAG,CAAkC,+BAAA,EAAA,KAAK,mDAAmD,SAAU,CAAA,IAAA;AAAA,UAC9K;AAAA,SACD,CAAA,EAAA;AAAA,OACH;AAAA;AAGF,IAAA,OAAOC,2CAAsB,CAAA,MAAA;AAAA,MAC3B,CAAS,KAAA,KAAA;AACP,QAAY,WAAA,CAAA,wBAAA,CAAyB,KAAM,CAAA,aAAa,CAAC,CAAA;AAAA,UACvD,CAAG,EAAA,KAAA,CAAM,MAAM,CAAA,CAAA,EAAI,MAAM,OAAO,CAAA,CAAA;AAAA,UAChC;AAAA,SACF;AAAA,OACF;AAAA,MACA,EAAE,MAAQ,EAAA,IAAA,EAAM,QAAS;AAAA,KAC3B;AAAA;AAEJ,CAAC;;;;"}
1
+ {"version":3,"file":"auditorServiceFactory.cjs.js","sources":["../../../src/entrypoints/auditor/auditorServiceFactory.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 coreServices,\n createServiceFactory,\n} from '@backstage/backend-plugin-api';\nimport { DefaultAuditorService } from './DefaultAuditorService';\nimport { getSeverityLogLevelMappings } from './utils';\n\n/**\n * Plugin-level auditing.\n *\n * See {@link @backstage/code-plugin-api#AuditorService}\n * and {@link https://backstage.io/docs/backend-system/core-services/auditor | the service docs}\n * for more information.\n *\n * @public\n */\nexport const auditorServiceFactory = createServiceFactory({\n service: coreServices.auditor,\n deps: {\n config: coreServices.rootConfig,\n logger: coreServices.logger,\n auth: coreServices.auth,\n httpAuth: coreServices.httpAuth,\n plugin: coreServices.pluginMetadata,\n },\n factory({ config, logger, plugin, auth, httpAuth }) {\n const auditLogger = logger.child({ isAuditEvent: true });\n\n const severityLogLevelMappings = getSeverityLogLevelMappings(config);\n\n return DefaultAuditorService.create(\n event => {\n if ('error' in event) {\n const { error, ...rest } = event;\n const childAuditLogger = auditLogger.child(rest);\n\n childAuditLogger[severityLogLevelMappings[event.severityLevel]](\n `${event.plugin}.${event.eventId}`,\n error,\n );\n } else {\n // the else statement is required for typechecking\n auditLogger[severityLogLevelMappings[event.severityLevel]](\n `${event.plugin}.${event.eventId}`,\n event,\n );\n }\n },\n { plugin, auth, httpAuth },\n );\n },\n});\n"],"names":["createServiceFactory","coreServices","getSeverityLogLevelMappings","DefaultAuditorService"],"mappings":";;;;;;AAgCO,MAAM,wBAAwBA,qCAAqB,CAAA;AAAA,EACxD,SAASC,6BAAa,CAAA,OAAA;AAAA,EACtB,IAAM,EAAA;AAAA,IACJ,QAAQA,6BAAa,CAAA,UAAA;AAAA,IACrB,QAAQA,6BAAa,CAAA,MAAA;AAAA,IACrB,MAAMA,6BAAa,CAAA,IAAA;AAAA,IACnB,UAAUA,6BAAa,CAAA,QAAA;AAAA,IACvB,QAAQA,6BAAa,CAAA;AAAA,GACvB;AAAA,EACA,QAAQ,EAAE,MAAA,EAAQ,QAAQ,MAAQ,EAAA,IAAA,EAAM,UAAY,EAAA;AAClD,IAAA,MAAM,cAAc,MAAO,CAAA,KAAA,CAAM,EAAE,YAAA,EAAc,MAAM,CAAA;AAEvD,IAAM,MAAA,wBAAA,GAA2BC,kCAA4B,MAAM,CAAA;AAEnE,IAAA,OAAOC,2CAAsB,CAAA,MAAA;AAAA,MAC3B,CAAS,KAAA,KAAA;AACP,QAAA,IAAI,WAAW,KAAO,EAAA;AACpB,UAAA,MAAM,EAAE,KAAA,EAAO,GAAG,IAAA,EAAS,GAAA,KAAA;AAC3B,UAAM,MAAA,gBAAA,GAAmB,WAAY,CAAA,KAAA,CAAM,IAAI,CAAA;AAE/C,UAAiB,gBAAA,CAAA,wBAAA,CAAyB,KAAM,CAAA,aAAa,CAAC,CAAA;AAAA,YAC5D,CAAG,EAAA,KAAA,CAAM,MAAM,CAAA,CAAA,EAAI,MAAM,OAAO,CAAA,CAAA;AAAA,YAChC;AAAA,WACF;AAAA,SACK,MAAA;AAEL,UAAY,WAAA,CAAA,wBAAA,CAAyB,KAAM,CAAA,aAAa,CAAC,CAAA;AAAA,YACvD,CAAG,EAAA,KAAA,CAAM,MAAM,CAAA,CAAA,EAAI,MAAM,OAAO,CAAA,CAAA;AAAA,YAChC;AAAA,WACF;AAAA;AACF,OACF;AAAA,MACA,EAAE,MAAQ,EAAA,IAAA,EAAM,QAAS;AAAA,KAC3B;AAAA;AAEJ,CAAC;;;;"}
@@ -0,0 +1,13 @@
1
+ 'use strict';
2
+
3
+ var zod = require('zod');
4
+
5
+ const severityLogLevelMappingsSchema = zod.z.record(
6
+ zod.z.enum(["low", "medium", "high", "critical"]),
7
+ zod.z.enum(["debug", "info", "warn", "error"])
8
+ );
9
+ const CONFIG_ROOT_KEY = "backend.auditor";
10
+
11
+ exports.CONFIG_ROOT_KEY = CONFIG_ROOT_KEY;
12
+ exports.severityLogLevelMappingsSchema = severityLogLevelMappingsSchema;
13
+ //# sourceMappingURL=types.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.cjs.js","sources":["../../../src/entrypoints/auditor/types.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 */\n\nimport { z } from 'zod';\n\n/** @internal */\nexport const severityLogLevelMappingsSchema = z.record(\n z.enum(['low', 'medium', 'high', 'critical']),\n z.enum(['debug', 'info', 'warn', 'error']),\n);\n\n/** @internal */\nexport const CONFIG_ROOT_KEY = 'backend.auditor';\n"],"names":["z"],"mappings":";;;;AAmBO,MAAM,iCAAiCA,KAAE,CAAA,MAAA;AAAA,EAC9CA,MAAE,IAAK,CAAA,CAAC,OAAO,QAAU,EAAA,MAAA,EAAQ,UAAU,CAAC,CAAA;AAAA,EAC5CA,MAAE,IAAK,CAAA,CAAC,SAAS,MAAQ,EAAA,MAAA,EAAQ,OAAO,CAAC;AAC3C;AAGO,MAAM,eAAkB,GAAA;;;;;"}