@backstage/plugin-notifications-backend-module-email 0.0.0-nightly-20240426021211
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 +17 -0
- package/README.md +72 -0
- package/config.d.ts +123 -0
- package/dist/index.cjs.js +309 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/package.json +56 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# @backstage/plugin-notifications-backend-module-email
|
|
2
|
+
|
|
3
|
+
## 0.0.0-nightly-20240426021211
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- dbf2696: Allow sending notifications by email with the new notifications module
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @backstage/catalog-model@0.0.0-nightly-20240426021211
|
|
10
|
+
- @backstage/plugin-notifications-node@0.0.0-nightly-20240426021211
|
|
11
|
+
- @backstage/backend-common@0.0.0-nightly-20240426021211
|
|
12
|
+
- @backstage/backend-plugin-api@0.0.0-nightly-20240426021211
|
|
13
|
+
- @backstage/catalog-client@0.0.0-nightly-20240426021211
|
|
14
|
+
- @backstage/config@1.2.0
|
|
15
|
+
- @backstage/integration-aws-node@0.1.12
|
|
16
|
+
- @backstage/types@1.1.1
|
|
17
|
+
- @backstage/plugin-notifications-common@0.0.3
|
package/README.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# @backstage/plugin-notifications-backend-module-email
|
|
2
|
+
|
|
3
|
+
Adds support for sending Backstage notifications as emails to users.
|
|
4
|
+
|
|
5
|
+
Supports sending emails using `SMTP`, `SES`, or `sendmail`.
|
|
6
|
+
|
|
7
|
+
## Customizing email content
|
|
8
|
+
|
|
9
|
+
The email content can be customized with the `notificationsEmailTemplateExtensionPoint`. When you create
|
|
10
|
+
this extension, you can set the custom `NotificationTemplateRenderer` to the module. To modify the contents,
|
|
11
|
+
override the `getSubject`, `getHtml` and `getText` methods.
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { notificationsEmailTemplateExtensionPoint } from '@backstage/plugin-notifications-backend-module-email';
|
|
15
|
+
import { Notification } from '@backstage/plugin-notifications-common';
|
|
16
|
+
|
|
17
|
+
export const notificationsModuleEmailDecorator = createBackendModule({
|
|
18
|
+
pluginId: 'notifications',
|
|
19
|
+
moduleId: 'email.templates',
|
|
20
|
+
register(reg) {
|
|
21
|
+
reg.registerInit({
|
|
22
|
+
deps: {
|
|
23
|
+
emailTemplates: notificationsEmailTemplateExtensionPoint,
|
|
24
|
+
},
|
|
25
|
+
async init({ emailTemplates }) {
|
|
26
|
+
emailTemplates.setTemplateRenderer({
|
|
27
|
+
getSubject(notification) {
|
|
28
|
+
return `New notification from ${notification.source}`;
|
|
29
|
+
},
|
|
30
|
+
getText(notification) {
|
|
31
|
+
return notification.content;
|
|
32
|
+
},
|
|
33
|
+
getHtml(notification) {
|
|
34
|
+
return `<p>${notification.content}</p>`;
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Example configuration:
|
|
44
|
+
|
|
45
|
+
```yaml
|
|
46
|
+
notifications:
|
|
47
|
+
processors:
|
|
48
|
+
email:
|
|
49
|
+
# Transport config, see options at `config.d.ts`
|
|
50
|
+
transportConfig:
|
|
51
|
+
transport: 'smtp'
|
|
52
|
+
hostname: 'my-smtp-server'
|
|
53
|
+
port: 587
|
|
54
|
+
secure: false
|
|
55
|
+
username: 'my-username'
|
|
56
|
+
password: 'my-password'
|
|
57
|
+
# The email sender address
|
|
58
|
+
sender: 'sender@mycompany.com'
|
|
59
|
+
replyTo: 'no-reply@mycompany.com'
|
|
60
|
+
# Who to get email for broadcast notifications
|
|
61
|
+
broadcastConfig:
|
|
62
|
+
receiver: 'users'
|
|
63
|
+
# How many emails to send concurrently, defaults to 2
|
|
64
|
+
concurrencyLimit: 10
|
|
65
|
+
# Cache configuration for email addresses
|
|
66
|
+
# This is to prevent unnecessary calls to the catalog
|
|
67
|
+
cache:
|
|
68
|
+
ttl:
|
|
69
|
+
days: 1
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
See `config.d.ts` for more options for configuration.
|
package/config.d.ts
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 The Backstage Authors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { HumanDuration } from '@backstage/types';
|
|
18
|
+
|
|
19
|
+
export interface Config {
|
|
20
|
+
/**
|
|
21
|
+
* Configuration options for notifications-backend-module-email */
|
|
22
|
+
notifications: {
|
|
23
|
+
processors: {
|
|
24
|
+
email: {
|
|
25
|
+
/**
|
|
26
|
+
* Transport to use for sending emails */
|
|
27
|
+
transportConfig:
|
|
28
|
+
| {
|
|
29
|
+
transport: 'smtp';
|
|
30
|
+
/**
|
|
31
|
+
* SMTP server hostname
|
|
32
|
+
*/
|
|
33
|
+
hostname: string;
|
|
34
|
+
/**
|
|
35
|
+
* SMTP server port
|
|
36
|
+
*/
|
|
37
|
+
port: number;
|
|
38
|
+
/**
|
|
39
|
+
* Use secure connection for SMTP, defaults to false
|
|
40
|
+
*/
|
|
41
|
+
secure?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Require TLS for SMTP connection, defaults to false
|
|
44
|
+
*/
|
|
45
|
+
requireTls?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* SMTP username
|
|
48
|
+
*/
|
|
49
|
+
username?: string;
|
|
50
|
+
/**
|
|
51
|
+
* SMTP password
|
|
52
|
+
* @visibility secret
|
|
53
|
+
*/
|
|
54
|
+
password?: string;
|
|
55
|
+
}
|
|
56
|
+
| {
|
|
57
|
+
transport: 'ses';
|
|
58
|
+
/**
|
|
59
|
+
* SES ApiVersion to use, defaults to 2010-12-01
|
|
60
|
+
*/
|
|
61
|
+
apiVersion?: string;
|
|
62
|
+
/**
|
|
63
|
+
* AWS account ID to use
|
|
64
|
+
*/
|
|
65
|
+
accountId?: string;
|
|
66
|
+
/**
|
|
67
|
+
* AWS region to use
|
|
68
|
+
*/
|
|
69
|
+
region?: string;
|
|
70
|
+
}
|
|
71
|
+
| {
|
|
72
|
+
transport: 'sendmail';
|
|
73
|
+
/**
|
|
74
|
+
* Sendmail binary path, defaults to /usr/sbin/sendmail
|
|
75
|
+
*/
|
|
76
|
+
path?: string;
|
|
77
|
+
/**
|
|
78
|
+
* Newline style, defaults to 'unix'
|
|
79
|
+
*/
|
|
80
|
+
newline?: 'unix' | 'windows';
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Sender email address
|
|
84
|
+
*/
|
|
85
|
+
sender: string;
|
|
86
|
+
/**
|
|
87
|
+
* Optional reply-to address
|
|
88
|
+
*/
|
|
89
|
+
replyTo?: string;
|
|
90
|
+
/**
|
|
91
|
+
* Concurrency limit for email sending, defaults to 2
|
|
92
|
+
*/
|
|
93
|
+
concurrencyLimit?: number;
|
|
94
|
+
/**
|
|
95
|
+
* Throttle duration between email sending, defaults to 100ms
|
|
96
|
+
*/
|
|
97
|
+
throttleInterval?: HumanDuration;
|
|
98
|
+
/**
|
|
99
|
+
* Configuration for broadcast notifications
|
|
100
|
+
*/
|
|
101
|
+
broadcastConfig?: {
|
|
102
|
+
/**
|
|
103
|
+
* Receiver of the broadcast notifications:
|
|
104
|
+
* none - skips sending
|
|
105
|
+
* users - sends to all users in backstage, might have performance impact
|
|
106
|
+
* config - sends to the emails specified in the config
|
|
107
|
+
*/
|
|
108
|
+
receiver: 'none' | 'users' | 'config';
|
|
109
|
+
/**
|
|
110
|
+
* Broadcast notification receivers when receiver is set to config
|
|
111
|
+
*/
|
|
112
|
+
receiverEmails?: string[];
|
|
113
|
+
};
|
|
114
|
+
cache?: {
|
|
115
|
+
/**
|
|
116
|
+
* Email cache TTL, defaults to 1 hour
|
|
117
|
+
*/
|
|
118
|
+
ttl?: HumanDuration;
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
}
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var backendPluginApi = require('@backstage/backend-plugin-api');
|
|
6
|
+
var catalogClient = require('@backstage/catalog-client');
|
|
7
|
+
var pluginNotificationsNode = require('@backstage/plugin-notifications-node');
|
|
8
|
+
var config = require('@backstage/config');
|
|
9
|
+
var types = require('@backstage/types');
|
|
10
|
+
var nodemailer = require('nodemailer');
|
|
11
|
+
var clientSes = require('@aws-sdk/client-ses');
|
|
12
|
+
var lodash = require('lodash');
|
|
13
|
+
var integrationAwsNode = require('@backstage/integration-aws-node');
|
|
14
|
+
var pThrottle = require('p-throttle');
|
|
15
|
+
|
|
16
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
17
|
+
|
|
18
|
+
var pThrottle__default = /*#__PURE__*/_interopDefaultCompat(pThrottle);
|
|
19
|
+
|
|
20
|
+
const createSmtpTransport = (config) => {
|
|
21
|
+
var _a, _b;
|
|
22
|
+
const username = config.getOptionalString("username");
|
|
23
|
+
const password = config.getOptionalString("password");
|
|
24
|
+
return nodemailer.createTransport({
|
|
25
|
+
host: config.getString("hostname"),
|
|
26
|
+
port: config.getNumber("port"),
|
|
27
|
+
secure: (_a = config.getOptionalBoolean("secure")) != null ? _a : false,
|
|
28
|
+
requireTLS: (_b = config.getOptionalBoolean("requireTls")) != null ? _b : false,
|
|
29
|
+
auth: username && password ? { user: username, pass: password } : void 0
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const createSesTransport = async (config, credentialsManager) => {
|
|
34
|
+
var _a;
|
|
35
|
+
const credentials = await credentialsManager.getCredentialProvider({
|
|
36
|
+
accountId: config.getOptionalString("accountId")
|
|
37
|
+
});
|
|
38
|
+
const ses = new clientSes.SES([
|
|
39
|
+
{
|
|
40
|
+
apiVersion: (_a = config.getOptionalString("apiVersion")) != null ? _a : "2010-12-01",
|
|
41
|
+
credentials: credentials.sdkCredentialProvider,
|
|
42
|
+
region: config.getOptionalString("region")
|
|
43
|
+
}
|
|
44
|
+
]);
|
|
45
|
+
return nodemailer.createTransport({
|
|
46
|
+
SES: { ses, aws: { SendRawEmailCommand: clientSes.SendRawEmailCommand } }
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const createSendmailTransport = (config) => {
|
|
51
|
+
var _a, _b;
|
|
52
|
+
return nodemailer.createTransport({
|
|
53
|
+
sendmail: true,
|
|
54
|
+
newline: (_a = config.getOptionalString("newline")) != null ? _a : "unix",
|
|
55
|
+
path: (_b = config.getOptionalString("path")) != null ? _b : "/usr/sbin/sendmail"
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
var __defProp = Object.defineProperty;
|
|
60
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
61
|
+
var __publicField = (obj, key, value) => {
|
|
62
|
+
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
63
|
+
return value;
|
|
64
|
+
};
|
|
65
|
+
class NotificationsEmailProcessor {
|
|
66
|
+
constructor(logger, config$1, catalog, auth, cache, templateRenderer) {
|
|
67
|
+
this.logger = logger;
|
|
68
|
+
this.config = config$1;
|
|
69
|
+
this.catalog = catalog;
|
|
70
|
+
this.auth = auth;
|
|
71
|
+
this.cache = cache;
|
|
72
|
+
this.templateRenderer = templateRenderer;
|
|
73
|
+
__publicField(this, "transporter");
|
|
74
|
+
__publicField(this, "broadcastConfig");
|
|
75
|
+
__publicField(this, "transportConfig");
|
|
76
|
+
__publicField(this, "sender");
|
|
77
|
+
__publicField(this, "replyTo");
|
|
78
|
+
__publicField(this, "cacheTtl");
|
|
79
|
+
__publicField(this, "concurrencyLimit");
|
|
80
|
+
__publicField(this, "throttleInterval");
|
|
81
|
+
var _a;
|
|
82
|
+
const emailProcessorConfig = config$1.getConfig(
|
|
83
|
+
"notifications.processors.email"
|
|
84
|
+
);
|
|
85
|
+
this.transportConfig = emailProcessorConfig.getConfig("transport");
|
|
86
|
+
this.broadcastConfig = emailProcessorConfig.getOptionalConfig("broadcastConfig");
|
|
87
|
+
this.sender = emailProcessorConfig.getString("sender");
|
|
88
|
+
this.replyTo = emailProcessorConfig.getOptionalString("replyTo");
|
|
89
|
+
this.concurrencyLimit = (_a = emailProcessorConfig.getOptionalNumber("concurrencyLimit")) != null ? _a : 2;
|
|
90
|
+
const throttleConfig = emailProcessorConfig.getOptionalConfig("throttleInterval");
|
|
91
|
+
this.throttleInterval = throttleConfig ? types.durationToMilliseconds(config.readDurationFromConfig(throttleConfig)) : 100;
|
|
92
|
+
const cacheConfig = emailProcessorConfig.getOptionalConfig("cache.ttl");
|
|
93
|
+
this.cacheTtl = cacheConfig ? types.durationToMilliseconds(config.readDurationFromConfig(cacheConfig)) : 36e5;
|
|
94
|
+
}
|
|
95
|
+
async getTransporter() {
|
|
96
|
+
if (this.transporter) {
|
|
97
|
+
return this.transporter;
|
|
98
|
+
}
|
|
99
|
+
const transport = this.transportConfig.getString("transport");
|
|
100
|
+
if (transport === "smtp") {
|
|
101
|
+
this.transporter = createSmtpTransport(this.transportConfig);
|
|
102
|
+
} else if (transport === "ses") {
|
|
103
|
+
const awsCredentialsManager = integrationAwsNode.DefaultAwsCredentialsManager.fromConfig(
|
|
104
|
+
this.config
|
|
105
|
+
);
|
|
106
|
+
this.transporter = await createSesTransport(
|
|
107
|
+
this.transportConfig,
|
|
108
|
+
awsCredentialsManager
|
|
109
|
+
);
|
|
110
|
+
} else if (transport === "sendmail") {
|
|
111
|
+
this.transporter = createSendmailTransport(this.transportConfig);
|
|
112
|
+
} else {
|
|
113
|
+
throw new Error(`Unsupported transport: ${transport}`);
|
|
114
|
+
}
|
|
115
|
+
return this.transporter;
|
|
116
|
+
}
|
|
117
|
+
getName() {
|
|
118
|
+
return "Email";
|
|
119
|
+
}
|
|
120
|
+
async getBroadcastEmails() {
|
|
121
|
+
var _a, _b, _c;
|
|
122
|
+
if (!this.broadcastConfig) {
|
|
123
|
+
return [];
|
|
124
|
+
}
|
|
125
|
+
const receiver = this.broadcastConfig.getString("receiver");
|
|
126
|
+
if (receiver === "none") {
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
if (receiver === "config") {
|
|
130
|
+
return (_a = this.broadcastConfig.getOptionalStringArray("receiverEmails")) != null ? _a : [];
|
|
131
|
+
}
|
|
132
|
+
if (receiver === "users") {
|
|
133
|
+
const cached = await ((_b = this.cache) == null ? void 0 : _b.get("user-emails:all"));
|
|
134
|
+
if (cached) {
|
|
135
|
+
return cached;
|
|
136
|
+
}
|
|
137
|
+
const { token } = await this.auth.getPluginRequestToken({
|
|
138
|
+
onBehalfOf: await this.auth.getOwnServiceCredentials(),
|
|
139
|
+
targetPluginId: "catalog"
|
|
140
|
+
});
|
|
141
|
+
const entities = await this.catalog.getEntities(
|
|
142
|
+
{
|
|
143
|
+
filter: [
|
|
144
|
+
{ kind: "user", "spec.profile.email": catalogClient.CATALOG_FILTER_EXISTS }
|
|
145
|
+
],
|
|
146
|
+
fields: ["spec.profile.email"]
|
|
147
|
+
},
|
|
148
|
+
{ token }
|
|
149
|
+
);
|
|
150
|
+
const ret = lodash.compact([
|
|
151
|
+
...new Set(
|
|
152
|
+
entities.items.map((entity) => {
|
|
153
|
+
var _a2;
|
|
154
|
+
return (_a2 = entity == null ? void 0 : entity.spec.profile) == null ? void 0 : _a2.email;
|
|
155
|
+
})
|
|
156
|
+
)
|
|
157
|
+
]);
|
|
158
|
+
await ((_c = this.cache) == null ? void 0 : _c.set("user-emails:all", ret, {
|
|
159
|
+
ttl: this.cacheTtl
|
|
160
|
+
}));
|
|
161
|
+
return ret;
|
|
162
|
+
}
|
|
163
|
+
throw new Error(`Unsupported broadcast receiver: ${receiver}`);
|
|
164
|
+
}
|
|
165
|
+
async getUserEmail(entityRef) {
|
|
166
|
+
var _a, _b, _c;
|
|
167
|
+
const cached = await ((_a = this.cache) == null ? void 0 : _a.get(`user-emails:${entityRef}`));
|
|
168
|
+
if (cached) {
|
|
169
|
+
return cached;
|
|
170
|
+
}
|
|
171
|
+
const { token } = await this.auth.getPluginRequestToken({
|
|
172
|
+
onBehalfOf: await this.auth.getOwnServiceCredentials(),
|
|
173
|
+
targetPluginId: "catalog"
|
|
174
|
+
});
|
|
175
|
+
const entity = await this.catalog.getEntityByRef(entityRef, { token });
|
|
176
|
+
const ret = [];
|
|
177
|
+
if (entity) {
|
|
178
|
+
const userEntity = entity;
|
|
179
|
+
if ((_b = userEntity.spec.profile) == null ? void 0 : _b.email) {
|
|
180
|
+
ret.push(userEntity.spec.profile.email);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
await ((_c = this.cache) == null ? void 0 : _c.set(`user-emails:${entityRef}`, ret, {
|
|
184
|
+
ttl: this.cacheTtl
|
|
185
|
+
}));
|
|
186
|
+
return ret;
|
|
187
|
+
}
|
|
188
|
+
async getRecipientEmails(notification, options) {
|
|
189
|
+
if (options.recipients.type === "broadcast" || notification.user === null) {
|
|
190
|
+
return await this.getBroadcastEmails();
|
|
191
|
+
}
|
|
192
|
+
return await this.getUserEmail(notification.user);
|
|
193
|
+
}
|
|
194
|
+
async sendMail(options) {
|
|
195
|
+
try {
|
|
196
|
+
await this.transporter.sendMail(options);
|
|
197
|
+
} catch (e) {
|
|
198
|
+
this.logger.error(`Failed to send email to ${options.to}: ${e}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
async sendMails(options, emails) {
|
|
202
|
+
const throttle = pThrottle__default.default({
|
|
203
|
+
limit: this.concurrencyLimit,
|
|
204
|
+
interval: this.throttleInterval
|
|
205
|
+
});
|
|
206
|
+
const throttled = throttle((opts) => this.sendMail(opts));
|
|
207
|
+
await Promise.all(
|
|
208
|
+
emails.map((email) => throttled({ ...options, to: email }))
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
async sendPlainEmail(notification, emails) {
|
|
212
|
+
const contentParts = [];
|
|
213
|
+
if (notification.payload.description) {
|
|
214
|
+
contentParts.push(`${notification.payload.description}`);
|
|
215
|
+
}
|
|
216
|
+
if (notification.payload.link) {
|
|
217
|
+
contentParts.push(`${notification.payload.link}`);
|
|
218
|
+
}
|
|
219
|
+
const mailOptions = {
|
|
220
|
+
from: this.sender,
|
|
221
|
+
subject: notification.payload.title,
|
|
222
|
+
html: `<p>${contentParts.join("<br/>")}</p>`,
|
|
223
|
+
text: contentParts.join("\n\n"),
|
|
224
|
+
replyTo: this.replyTo
|
|
225
|
+
};
|
|
226
|
+
await this.sendMails(mailOptions, emails);
|
|
227
|
+
}
|
|
228
|
+
async sendTemplateEmail(notification, emails) {
|
|
229
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
230
|
+
const mailOptions = {
|
|
231
|
+
from: this.sender,
|
|
232
|
+
subject: (_c = (_b = (_a = this.templateRenderer) == null ? void 0 : _a.getSubject) == null ? void 0 : _b.call(_a, notification)) != null ? _c : notification.payload.title,
|
|
233
|
+
html: (_e = (_d = this.templateRenderer) == null ? void 0 : _d.getHtml) == null ? void 0 : _e.call(_d, notification),
|
|
234
|
+
text: (_g = (_f = this.templateRenderer) == null ? void 0 : _f.getText) == null ? void 0 : _g.call(_f, notification),
|
|
235
|
+
replyTo: this.replyTo
|
|
236
|
+
};
|
|
237
|
+
await this.sendMails(mailOptions, emails);
|
|
238
|
+
}
|
|
239
|
+
async postProcess(notification, options) {
|
|
240
|
+
this.transporter = await this.getTransporter();
|
|
241
|
+
let emails = [];
|
|
242
|
+
try {
|
|
243
|
+
emails = await this.getRecipientEmails(notification, options);
|
|
244
|
+
} catch (e) {
|
|
245
|
+
this.logger.error(`Failed to resolve recipient emails: ${e}`);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
if (emails.length === 0) {
|
|
249
|
+
this.logger.info(
|
|
250
|
+
`No email recipients found for notification: ${notification.id}, skipping`
|
|
251
|
+
);
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
if (!this.templateRenderer) {
|
|
255
|
+
await this.sendPlainEmail(notification, emails);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
await this.sendTemplateEmail(notification, emails);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const notificationsEmailTemplateExtensionPoint = backendPluginApi.createExtensionPoint({
|
|
263
|
+
id: "notifications.email.templates"
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
const notificationsModuleEmail = backendPluginApi.createBackendModule({
|
|
267
|
+
pluginId: "notifications",
|
|
268
|
+
moduleId: "email",
|
|
269
|
+
register(reg) {
|
|
270
|
+
let templateRenderer;
|
|
271
|
+
reg.registerExtensionPoint(notificationsEmailTemplateExtensionPoint, {
|
|
272
|
+
setTemplateRenderer(renderer) {
|
|
273
|
+
if (templateRenderer) {
|
|
274
|
+
throw new Error(`Email template renderer was already registered`);
|
|
275
|
+
}
|
|
276
|
+
templateRenderer = renderer;
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
reg.registerInit({
|
|
280
|
+
deps: {
|
|
281
|
+
config: backendPluginApi.coreServices.rootConfig,
|
|
282
|
+
notifications: pluginNotificationsNode.notificationsProcessingExtensionPoint,
|
|
283
|
+
discovery: backendPluginApi.coreServices.discovery,
|
|
284
|
+
logger: backendPluginApi.coreServices.logger,
|
|
285
|
+
auth: backendPluginApi.coreServices.auth,
|
|
286
|
+
cache: backendPluginApi.coreServices.cache
|
|
287
|
+
},
|
|
288
|
+
async init({ config, notifications, discovery, logger, auth, cache }) {
|
|
289
|
+
const catalogClient$1 = new catalogClient.CatalogClient({
|
|
290
|
+
discoveryApi: discovery
|
|
291
|
+
});
|
|
292
|
+
notifications.addProcessor(
|
|
293
|
+
new NotificationsEmailProcessor(
|
|
294
|
+
logger,
|
|
295
|
+
config,
|
|
296
|
+
catalogClient$1,
|
|
297
|
+
auth,
|
|
298
|
+
cache,
|
|
299
|
+
templateRenderer
|
|
300
|
+
)
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
exports.default = notificationsModuleEmail;
|
|
308
|
+
exports.notificationsEmailTemplateExtensionPoint = notificationsEmailTemplateExtensionPoint;
|
|
309
|
+
//# sourceMappingURL=index.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../src/processor/transports/smtp.ts","../src/processor/transports/ses.ts","../src/processor/transports/sendmail.ts","../src/processor/NotificationsEmailProcessor.ts","../src/extensions.ts","../src/module.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 */\nimport { createTransport } from 'nodemailer';\nimport { Config } from '@backstage/config';\n\nexport const createSmtpTransport = (config: Config) => {\n const username = config.getOptionalString('username');\n const password = config.getOptionalString('password');\n\n return createTransport({\n host: config.getString('hostname'),\n port: config.getNumber('port'),\n secure: config.getOptionalBoolean('secure') ?? false,\n requireTLS: config.getOptionalBoolean('requireTls') ?? false,\n auth: username && password ? { user: username, pass: password } : undefined,\n });\n};\n","/*\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 */\nimport { createTransport } from 'nodemailer';\nimport { SendRawEmailCommand, SES } from '@aws-sdk/client-ses';\nimport { Config } from '@backstage/config';\nimport { AwsCredentialsManager } from '@backstage/integration-aws-node';\n\nexport const createSesTransport = async (\n config: Config,\n credentialsManager: AwsCredentialsManager,\n) => {\n const credentials = await credentialsManager.getCredentialProvider({\n accountId: config.getOptionalString('accountId'),\n });\n const ses = new SES([\n {\n apiVersion: config.getOptionalString('apiVersion') ?? '2010-12-01',\n credentials: credentials.sdkCredentialProvider,\n region: config.getOptionalString('region'),\n },\n ]);\n return createTransport({\n SES: { ses, aws: { SendRawEmailCommand } },\n });\n};\n","/*\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 */\nimport { createTransport } from 'nodemailer';\nimport { Config } from '@backstage/config';\n\nexport const createSendmailTransport = (config: Config) => {\n return createTransport({\n sendmail: true,\n newline: config.getOptionalString('newline') ?? 'unix',\n path: config.getOptionalString('path') ?? '/usr/sbin/sendmail',\n });\n};\n","/*\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 */\nimport {\n NotificationProcessor,\n NotificationSendOptions,\n} from '@backstage/plugin-notifications-node';\nimport {\n AuthService,\n CacheService,\n LoggerService,\n} from '@backstage/backend-plugin-api';\nimport { Config, readDurationFromConfig } from '@backstage/config';\nimport { durationToMilliseconds } from '@backstage/types';\nimport {\n CATALOG_FILTER_EXISTS,\n CatalogClient,\n} from '@backstage/catalog-client';\nimport { Notification } from '@backstage/plugin-notifications-common';\nimport {\n createSendmailTransport,\n createSesTransport,\n createSmtpTransport,\n} from './transports';\nimport { UserEntity } from '@backstage/catalog-model';\nimport { compact } from 'lodash';\nimport { DefaultAwsCredentialsManager } from '@backstage/integration-aws-node';\nimport { NotificationTemplateRenderer } from '../extensions';\nimport Mail from 'nodemailer/lib/mailer';\nimport pThrottle from 'p-throttle';\n\nexport class NotificationsEmailProcessor implements NotificationProcessor {\n private transporter: any;\n private readonly broadcastConfig?: Config;\n private readonly transportConfig: Config;\n private readonly sender: string;\n private readonly replyTo?: string;\n private readonly cacheTtl: number;\n private readonly concurrencyLimit: number;\n private readonly throttleInterval: number;\n\n constructor(\n private readonly logger: LoggerService,\n private readonly config: Config,\n private readonly catalog: CatalogClient,\n private readonly auth: AuthService,\n private readonly cache?: CacheService,\n private readonly templateRenderer?: NotificationTemplateRenderer,\n ) {\n const emailProcessorConfig = config.getConfig(\n 'notifications.processors.email',\n );\n this.transportConfig = emailProcessorConfig.getConfig('transport');\n this.broadcastConfig =\n emailProcessorConfig.getOptionalConfig('broadcastConfig');\n this.sender = emailProcessorConfig.getString('sender');\n this.replyTo = emailProcessorConfig.getOptionalString('replyTo');\n this.concurrencyLimit =\n emailProcessorConfig.getOptionalNumber('concurrencyLimit') ?? 2;\n const throttleConfig =\n emailProcessorConfig.getOptionalConfig('throttleInterval');\n this.throttleInterval = throttleConfig\n ? durationToMilliseconds(readDurationFromConfig(throttleConfig))\n : 100;\n const cacheConfig = emailProcessorConfig.getOptionalConfig('cache.ttl');\n this.cacheTtl = cacheConfig\n ? durationToMilliseconds(readDurationFromConfig(cacheConfig))\n : 3_600_000;\n }\n\n private async getTransporter() {\n if (this.transporter) {\n return this.transporter;\n }\n const transport = this.transportConfig.getString('transport');\n if (transport === 'smtp') {\n this.transporter = createSmtpTransport(this.transportConfig);\n } else if (transport === 'ses') {\n const awsCredentialsManager = DefaultAwsCredentialsManager.fromConfig(\n this.config,\n );\n this.transporter = await createSesTransport(\n this.transportConfig,\n awsCredentialsManager,\n );\n } else if (transport === 'sendmail') {\n this.transporter = createSendmailTransport(this.transportConfig);\n } else {\n throw new Error(`Unsupported transport: ${transport}`);\n }\n return this.transporter;\n }\n\n getName(): string {\n return 'Email';\n }\n\n private async getBroadcastEmails(): Promise<string[]> {\n if (!this.broadcastConfig) {\n return [];\n }\n\n const receiver = this.broadcastConfig.getString('receiver');\n if (receiver === 'none') {\n return [];\n }\n\n if (receiver === 'config') {\n return (\n this.broadcastConfig.getOptionalStringArray('receiverEmails') ?? []\n );\n }\n\n if (receiver === 'users') {\n const cached = await this.cache?.get<string[]>('user-emails:all');\n if (cached) {\n return cached;\n }\n\n const { token } = await this.auth.getPluginRequestToken({\n onBehalfOf: await this.auth.getOwnServiceCredentials(),\n targetPluginId: 'catalog',\n });\n const entities = await this.catalog.getEntities(\n {\n filter: [\n { kind: 'user', 'spec.profile.email': CATALOG_FILTER_EXISTS },\n ],\n fields: ['spec.profile.email'],\n },\n { token },\n );\n const ret = compact([\n ...new Set(\n entities.items.map(entity => {\n return (entity as UserEntity)?.spec.profile?.email;\n }),\n ),\n ]);\n\n await this.cache?.set('user-emails:all', ret, {\n ttl: this.cacheTtl,\n });\n return ret;\n }\n\n throw new Error(`Unsupported broadcast receiver: ${receiver}`);\n }\n\n private async getUserEmail(entityRef: string): Promise<string[]> {\n const cached = await this.cache?.get<string[]>(`user-emails:${entityRef}`);\n if (cached) {\n return cached;\n }\n\n const { token } = await this.auth.getPluginRequestToken({\n onBehalfOf: await this.auth.getOwnServiceCredentials(),\n targetPluginId: 'catalog',\n });\n const entity = await this.catalog.getEntityByRef(entityRef, { token });\n const ret: string[] = [];\n if (entity) {\n const userEntity = entity as UserEntity;\n if (userEntity.spec.profile?.email) {\n ret.push(userEntity.spec.profile.email);\n }\n }\n\n await this.cache?.set(`user-emails:${entityRef}`, ret, {\n ttl: this.cacheTtl,\n });\n\n return ret;\n }\n\n private async getRecipientEmails(\n notification: Notification,\n options: NotificationSendOptions,\n ) {\n if (options.recipients.type === 'broadcast' || notification.user === null) {\n return await this.getBroadcastEmails();\n }\n return await this.getUserEmail(notification.user);\n }\n\n private async sendMail(options: Mail.Options) {\n try {\n await this.transporter.sendMail(options);\n } catch (e) {\n this.logger.error(`Failed to send email to ${options.to}: ${e}`);\n }\n }\n\n private async sendMails(options: Mail.Options, emails: string[]) {\n const throttle = pThrottle({\n limit: this.concurrencyLimit,\n interval: this.throttleInterval,\n });\n\n const throttled = throttle((opts: Mail.Options) => this.sendMail(opts));\n await Promise.all(\n emails.map(email => throttled({ ...options, to: email })),\n );\n }\n\n private async sendPlainEmail(notification: Notification, emails: string[]) {\n const contentParts: string[] = [];\n if (notification.payload.description) {\n contentParts.push(`${notification.payload.description}`);\n }\n if (notification.payload.link) {\n contentParts.push(`${notification.payload.link}`);\n }\n\n const mailOptions = {\n from: this.sender,\n subject: notification.payload.title,\n html: `<p>${contentParts.join('<br/>')}</p>`,\n text: contentParts.join('\\n\\n'),\n replyTo: this.replyTo,\n };\n\n await this.sendMails(mailOptions, emails);\n }\n\n private async sendTemplateEmail(\n notification: Notification,\n emails: string[],\n ) {\n const mailOptions = {\n from: this.sender,\n subject:\n this.templateRenderer?.getSubject?.(notification) ??\n notification.payload.title,\n html: this.templateRenderer?.getHtml?.(notification),\n text: this.templateRenderer?.getText?.(notification),\n replyTo: this.replyTo,\n };\n\n await this.sendMails(mailOptions, emails);\n }\n\n async postProcess(\n notification: Notification,\n options: NotificationSendOptions,\n ): Promise<void> {\n this.transporter = await this.getTransporter();\n\n let emails: string[] = [];\n try {\n emails = await this.getRecipientEmails(notification, options);\n } catch (e) {\n this.logger.error(`Failed to resolve recipient emails: ${e}`);\n return;\n }\n\n if (emails.length === 0) {\n this.logger.info(\n `No email recipients found for notification: ${notification.id}, skipping`,\n );\n return;\n }\n\n if (!this.templateRenderer) {\n await this.sendPlainEmail(notification, emails);\n return;\n }\n\n await this.sendTemplateEmail(notification, emails);\n }\n}\n","/*\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 */\nimport { createExtensionPoint } from '@backstage/backend-plugin-api';\nimport { Notification } from '@backstage/plugin-notifications-common';\n\n/**\n * @public\n */\nexport interface NotificationTemplateRenderer {\n getSubject?(notification: Notification): string;\n getText?(notification: Notification): string;\n getHtml?(notification: Notification): string;\n}\n\n/**\n * @public\n */\nexport interface NotificationsEmailTemplateExtensionPoint {\n setTemplateRenderer(renderer: NotificationTemplateRenderer): void;\n}\n\n/**\n * @public\n */\nexport const notificationsEmailTemplateExtensionPoint =\n createExtensionPoint<NotificationsEmailTemplateExtensionPoint>({\n id: 'notifications.email.templates',\n });\n","/*\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 */\nimport {\n coreServices,\n createBackendModule,\n} from '@backstage/backend-plugin-api';\nimport { CatalogClient } from '@backstage/catalog-client';\nimport { notificationsProcessingExtensionPoint } from '@backstage/plugin-notifications-node';\nimport { NotificationsEmailProcessor } from './processor';\nimport {\n notificationsEmailTemplateExtensionPoint,\n NotificationTemplateRenderer,\n} from './extensions';\n\n/**\n * @public\n */\nexport const notificationsModuleEmail = createBackendModule({\n pluginId: 'notifications',\n moduleId: 'email',\n register(reg) {\n let templateRenderer: NotificationTemplateRenderer | undefined;\n reg.registerExtensionPoint(notificationsEmailTemplateExtensionPoint, {\n setTemplateRenderer(renderer) {\n if (templateRenderer) {\n throw new Error(`Email template renderer was already registered`);\n }\n templateRenderer = renderer;\n },\n });\n\n reg.registerInit({\n deps: {\n config: coreServices.rootConfig,\n notifications: notificationsProcessingExtensionPoint,\n discovery: coreServices.discovery,\n logger: coreServices.logger,\n auth: coreServices.auth,\n cache: coreServices.cache,\n },\n async init({ config, notifications, discovery, logger, auth, cache }) {\n const catalogClient = new CatalogClient({\n discoveryApi: discovery,\n });\n\n notifications.addProcessor(\n new NotificationsEmailProcessor(\n logger,\n config,\n catalogClient,\n auth,\n cache,\n templateRenderer,\n ),\n );\n },\n });\n },\n});\n"],"names":["createTransport","SES","SendRawEmailCommand","config","durationToMilliseconds","readDurationFromConfig","DefaultAwsCredentialsManager","CATALOG_FILTER_EXISTS","compact","_a","pThrottle","createExtensionPoint","createBackendModule","coreServices","notificationsProcessingExtensionPoint","catalogClient","CatalogClient"],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
|
|
2
|
+
import { Notification } from '@backstage/plugin-notifications-common';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @public
|
|
6
|
+
*/
|
|
7
|
+
declare const notificationsModuleEmail: () => _backstage_backend_plugin_api.BackendFeature;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @public
|
|
11
|
+
*/
|
|
12
|
+
interface NotificationTemplateRenderer {
|
|
13
|
+
getSubject?(notification: Notification): string;
|
|
14
|
+
getText?(notification: Notification): string;
|
|
15
|
+
getHtml?(notification: Notification): string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
20
|
+
interface NotificationsEmailTemplateExtensionPoint {
|
|
21
|
+
setTemplateRenderer(renderer: NotificationTemplateRenderer): void;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* @public
|
|
25
|
+
*/
|
|
26
|
+
declare const notificationsEmailTemplateExtensionPoint: _backstage_backend_plugin_api.ExtensionPoint<NotificationsEmailTemplateExtensionPoint>;
|
|
27
|
+
|
|
28
|
+
export { type NotificationTemplateRenderer, type NotificationsEmailTemplateExtensionPoint, notificationsModuleEmail as default, notificationsEmailTemplateExtensionPoint };
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@backstage/plugin-notifications-backend-module-email",
|
|
3
|
+
"version": "0.0.0-nightly-20240426021211",
|
|
4
|
+
"description": "The email backend module for the notifications plugin.",
|
|
5
|
+
"backstage": {
|
|
6
|
+
"role": "backend-plugin-module"
|
|
7
|
+
},
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public",
|
|
10
|
+
"main": "dist/index.cjs.js",
|
|
11
|
+
"types": "dist/index.d.ts"
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/backstage/backstage",
|
|
16
|
+
"directory": "plugins/notifications-backend-module-email"
|
|
17
|
+
},
|
|
18
|
+
"license": "Apache-2.0",
|
|
19
|
+
"main": "dist/index.cjs.js",
|
|
20
|
+
"types": "dist/index.d.ts",
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"config.d.ts"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "backstage-cli package build",
|
|
27
|
+
"clean": "backstage-cli package clean",
|
|
28
|
+
"lint": "backstage-cli package lint",
|
|
29
|
+
"prepack": "backstage-cli package prepack",
|
|
30
|
+
"postpack": "backstage-cli package postpack",
|
|
31
|
+
"start": "backstage-cli package start",
|
|
32
|
+
"test": "backstage-cli package test"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@aws-sdk/client-ses": "^3.550.0",
|
|
36
|
+
"@aws-sdk/types": "^3.347.0",
|
|
37
|
+
"@backstage/backend-common": "^0.0.0-nightly-20240426021211",
|
|
38
|
+
"@backstage/backend-plugin-api": "^0.0.0-nightly-20240426021211",
|
|
39
|
+
"@backstage/catalog-client": "^0.0.0-nightly-20240426021211",
|
|
40
|
+
"@backstage/catalog-model": "^0.0.0-nightly-20240426021211",
|
|
41
|
+
"@backstage/config": "^1.2.0",
|
|
42
|
+
"@backstage/integration-aws-node": "^0.1.12",
|
|
43
|
+
"@backstage/plugin-notifications-common": "^0.0.3",
|
|
44
|
+
"@backstage/plugin-notifications-node": "^0.0.0-nightly-20240426021211",
|
|
45
|
+
"@backstage/types": "^1.1.1",
|
|
46
|
+
"lodash": "^4.17.21",
|
|
47
|
+
"nodemailer": "^6.9.13",
|
|
48
|
+
"p-throttle": "^6.1.0"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@backstage/backend-test-utils": "^0.0.0-nightly-20240426021211",
|
|
52
|
+
"@backstage/cli": "^0.0.0-nightly-20240426021211",
|
|
53
|
+
"@types/nodemailer": "^6.4.14"
|
|
54
|
+
},
|
|
55
|
+
"configSchema": "config.d.ts"
|
|
56
|
+
}
|