@stacksjs/ts-cloud 0.7.5 → 0.7.7
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/dist/aws/index.js +259 -0
- package/dist/bin/cli.js +231 -231
- package/dist/chunk-0wxyppza.js +146 -0
- package/dist/chunk-3knnr7wh.js +601 -0
- package/dist/chunk-3rsfns7x.js +10427 -0
- package/dist/chunk-93hjhs78.js +1752 -0
- package/dist/chunk-arsh1g5h.js +1749 -0
- package/dist/chunk-b82pbxyp.js +1572 -0
- package/dist/chunk-c6rgvg1j.js +46124 -0
- package/dist/chunk-d2p5n2aq.js +23 -0
- package/dist/chunk-d7p84vz5.js +1699 -0
- package/dist/chunk-eq08r166.js +8 -0
- package/dist/chunk-hsk6fe6x.js +371 -0
- package/dist/chunk-mj1tmhte.js +13 -0
- package/dist/chunk-pegd7rmf.js +10 -0
- package/dist/chunk-pqfzdg68.js +12 -0
- package/dist/chunk-qpj3edwz.js +601 -0
- package/dist/chunk-tjjgajbh.js +1032 -0
- package/dist/chunk-tnztxpcb.js +630 -0
- package/dist/chunk-tt4kxske.js +1788 -0
- package/dist/chunk-v0bahtg2.js +4 -0
- package/dist/chunk-vd87cpvn.js +1754 -0
- package/dist/chunk-x07dz3sd.js +12828 -0
- package/dist/chunk-xzn0ntr0.js +4616 -0
- package/dist/chunk-zn0nxxa8.js +1779 -0
- package/dist/deploy/index.js +87 -0
- package/dist/dns/index.js +31 -0
- package/dist/drivers/index.d.ts +2 -0
- package/dist/drivers/index.js +108 -0
- package/dist/drivers/shared/box-provision.d.ts +102 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4383 -91358
- package/dist/push/index.js +509 -0
- package/dist/ui/index.html +3 -3
- package/dist/ui/server/actions.html +3 -3
- package/dist/ui/server/backups.html +3 -3
- package/dist/ui/server/database.html +3 -3
- package/dist/ui/server/deployments.html +3 -3
- package/dist/ui/server/firewall.html +3 -3
- package/dist/ui/server/logs.html +3 -3
- package/dist/ui/server/services.html +3 -3
- package/dist/ui/server/sites.html +3 -3
- package/dist/ui/server/ssh-keys.html +3 -3
- package/dist/ui/server/terminal.html +3 -3
- package/dist/ui/server/workers.html +3 -3
- package/dist/ui/serverless/alarms.html +3 -3
- package/dist/ui/serverless/assets.html +3 -3
- package/dist/ui/serverless/data.html +3 -3
- package/dist/ui/serverless/deployments.html +3 -3
- package/dist/ui/serverless/functions.html +3 -3
- package/dist/ui/serverless/logs.html +3 -3
- package/dist/ui/serverless/queues.html +3 -3
- package/dist/ui/serverless/scheduler.html +3 -3
- package/dist/ui/serverless/secrets.html +3 -3
- package/dist/ui/serverless/traces.html +3 -3
- package/dist/ui/serverless.html +3 -3
- package/package.json +3 -3
|
@@ -0,0 +1,1779 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Route53Client
|
|
3
|
+
} from "./chunk-tnztxpcb.js";
|
|
4
|
+
import {
|
|
5
|
+
S3Client
|
|
6
|
+
} from "./chunk-vd87cpvn.js";
|
|
7
|
+
import {
|
|
8
|
+
AWSClient
|
|
9
|
+
} from "./chunk-arsh1g5h.js";
|
|
10
|
+
import {
|
|
11
|
+
__require
|
|
12
|
+
} from "./chunk-v0bahtg2.js";
|
|
13
|
+
|
|
14
|
+
// src/aws/ses.ts
|
|
15
|
+
class SESClient {
|
|
16
|
+
client;
|
|
17
|
+
region;
|
|
18
|
+
constructor(region = "us-east-1", credentials) {
|
|
19
|
+
this.region = region;
|
|
20
|
+
this.client = new AWSClient(credentials);
|
|
21
|
+
}
|
|
22
|
+
async createEmailIdentity(params) {
|
|
23
|
+
const result = await this.client.request({
|
|
24
|
+
service: "email",
|
|
25
|
+
region: this.region,
|
|
26
|
+
method: "POST",
|
|
27
|
+
path: "/v2/email/identities",
|
|
28
|
+
headers: {
|
|
29
|
+
"Content-Type": "application/json"
|
|
30
|
+
},
|
|
31
|
+
body: JSON.stringify(params)
|
|
32
|
+
});
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
async getEmailIdentity(emailIdentity) {
|
|
36
|
+
const result = await this.client.request({
|
|
37
|
+
service: "email",
|
|
38
|
+
region: this.region,
|
|
39
|
+
method: "GET",
|
|
40
|
+
path: `/v2/email/identities/${encodeURIComponent(emailIdentity)}`,
|
|
41
|
+
headers: {
|
|
42
|
+
"Content-Type": "application/json"
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
return {
|
|
46
|
+
IdentityType: result.IdentityType,
|
|
47
|
+
IdentityName: emailIdentity,
|
|
48
|
+
SendingEnabled: result.VerifiedForSendingStatus,
|
|
49
|
+
VerificationStatus: result.VerificationStatus,
|
|
50
|
+
DkimAttributes: result.DkimAttributes,
|
|
51
|
+
MailFromAttributes: result.MailFromAttributes
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
async putEmailIdentityMailFromAttributes(emailIdentity, params) {
|
|
55
|
+
await this.client.request({
|
|
56
|
+
service: "email",
|
|
57
|
+
region: this.region,
|
|
58
|
+
method: "PUT",
|
|
59
|
+
path: `/v2/email/identities/${encodeURIComponent(emailIdentity)}/mail-from`,
|
|
60
|
+
headers: {
|
|
61
|
+
"Content-Type": "application/json"
|
|
62
|
+
},
|
|
63
|
+
body: JSON.stringify(params)
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
async listEmailIdentities(params) {
|
|
67
|
+
let path = "/v2/email/identities";
|
|
68
|
+
const queryParams = [];
|
|
69
|
+
if (params?.PageSize) {
|
|
70
|
+
queryParams.push(`PageSize=${params.PageSize}`);
|
|
71
|
+
}
|
|
72
|
+
if (params?.NextToken) {
|
|
73
|
+
queryParams.push(`NextToken=${encodeURIComponent(params.NextToken)}`);
|
|
74
|
+
}
|
|
75
|
+
if (queryParams.length > 0) {
|
|
76
|
+
path += `?${queryParams.join("&")}`;
|
|
77
|
+
}
|
|
78
|
+
const result = await this.client.request({
|
|
79
|
+
service: "email",
|
|
80
|
+
region: this.region,
|
|
81
|
+
method: "GET",
|
|
82
|
+
path,
|
|
83
|
+
headers: {
|
|
84
|
+
"Content-Type": "application/json"
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
return {
|
|
88
|
+
EmailIdentities: result.EmailIdentities,
|
|
89
|
+
NextToken: result.NextToken
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
async deleteEmailIdentity(emailIdentity) {
|
|
93
|
+
await this.client.request({
|
|
94
|
+
service: "email",
|
|
95
|
+
region: this.region,
|
|
96
|
+
method: "DELETE",
|
|
97
|
+
path: `/v2/email/identities/${encodeURIComponent(emailIdentity)}`,
|
|
98
|
+
headers: {
|
|
99
|
+
"Content-Type": "application/json"
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
async putEmailIdentityDkimAttributes(params) {
|
|
104
|
+
await this.client.request({
|
|
105
|
+
service: "email",
|
|
106
|
+
region: this.region,
|
|
107
|
+
method: "PUT",
|
|
108
|
+
path: `/v2/email/identities/${encodeURIComponent(params.EmailIdentity)}/dkim`,
|
|
109
|
+
headers: {
|
|
110
|
+
"Content-Type": "application/json"
|
|
111
|
+
},
|
|
112
|
+
body: JSON.stringify({ SigningEnabled: params.SigningEnabled })
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
async sendEmail(params) {
|
|
116
|
+
const result = await this.client.request({
|
|
117
|
+
service: "email",
|
|
118
|
+
region: this.region,
|
|
119
|
+
method: "POST",
|
|
120
|
+
path: "/v2/email/outbound-emails",
|
|
121
|
+
headers: {
|
|
122
|
+
"Content-Type": "application/json"
|
|
123
|
+
},
|
|
124
|
+
body: JSON.stringify(params)
|
|
125
|
+
});
|
|
126
|
+
return {
|
|
127
|
+
MessageId: result.MessageId
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
async sendBulkEmail(params) {
|
|
131
|
+
const result = await this.client.request({
|
|
132
|
+
service: "email",
|
|
133
|
+
region: this.region,
|
|
134
|
+
method: "POST",
|
|
135
|
+
path: "/v2/email/outbound-bulk-emails",
|
|
136
|
+
headers: {
|
|
137
|
+
"Content-Type": "application/json"
|
|
138
|
+
},
|
|
139
|
+
body: JSON.stringify(params)
|
|
140
|
+
});
|
|
141
|
+
return {
|
|
142
|
+
BulkEmailEntryResults: result.BulkEmailEntryResults
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
async createEmailTemplate(params) {
|
|
146
|
+
await this.client.request({
|
|
147
|
+
service: "email",
|
|
148
|
+
region: this.region,
|
|
149
|
+
method: "POST",
|
|
150
|
+
path: "/v2/email/templates",
|
|
151
|
+
headers: {
|
|
152
|
+
"Content-Type": "application/json"
|
|
153
|
+
},
|
|
154
|
+
body: JSON.stringify(params)
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
async getEmailTemplate(templateName) {
|
|
158
|
+
const result = await this.client.request({
|
|
159
|
+
service: "email",
|
|
160
|
+
region: this.region,
|
|
161
|
+
method: "GET",
|
|
162
|
+
path: `/v2/email/templates/${encodeURIComponent(templateName)}`,
|
|
163
|
+
headers: {
|
|
164
|
+
"Content-Type": "application/json"
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
return result;
|
|
168
|
+
}
|
|
169
|
+
async deleteEmailTemplate(templateName) {
|
|
170
|
+
await this.client.request({
|
|
171
|
+
service: "email",
|
|
172
|
+
region: this.region,
|
|
173
|
+
method: "DELETE",
|
|
174
|
+
path: `/v2/email/templates/${encodeURIComponent(templateName)}`,
|
|
175
|
+
headers: {
|
|
176
|
+
"Content-Type": "application/json"
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
async listEmailTemplates(params) {
|
|
181
|
+
let path = "/v2/email/templates";
|
|
182
|
+
const queryParams = [];
|
|
183
|
+
if (params?.PageSize) {
|
|
184
|
+
queryParams.push(`PageSize=${params.PageSize}`);
|
|
185
|
+
}
|
|
186
|
+
if (params?.NextToken) {
|
|
187
|
+
queryParams.push(`NextToken=${encodeURIComponent(params.NextToken)}`);
|
|
188
|
+
}
|
|
189
|
+
if (queryParams.length > 0) {
|
|
190
|
+
path += `?${queryParams.join("&")}`;
|
|
191
|
+
}
|
|
192
|
+
const result = await this.client.request({
|
|
193
|
+
service: "email",
|
|
194
|
+
region: this.region,
|
|
195
|
+
method: "GET",
|
|
196
|
+
path,
|
|
197
|
+
headers: {
|
|
198
|
+
"Content-Type": "application/json"
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
return result;
|
|
202
|
+
}
|
|
203
|
+
async getSendStatistics() {
|
|
204
|
+
const result = await this.client.request({
|
|
205
|
+
service: "ses",
|
|
206
|
+
region: this.region,
|
|
207
|
+
method: "POST",
|
|
208
|
+
path: "/",
|
|
209
|
+
headers: {
|
|
210
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
211
|
+
},
|
|
212
|
+
body: "Action=GetSendStatistics&Version=2010-12-01"
|
|
213
|
+
});
|
|
214
|
+
return {
|
|
215
|
+
SendDataPoints: result.GetSendStatisticsResponse?.GetSendStatisticsResult?.SendDataPoints?.member
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
async getSendQuota() {
|
|
219
|
+
const result = await this.client.request({
|
|
220
|
+
service: "ses",
|
|
221
|
+
region: this.region,
|
|
222
|
+
method: "POST",
|
|
223
|
+
path: "/",
|
|
224
|
+
headers: {
|
|
225
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
226
|
+
},
|
|
227
|
+
body: "Action=GetSendQuota&Version=2010-12-01"
|
|
228
|
+
});
|
|
229
|
+
const quota = result.GetSendQuotaResponse?.GetSendQuotaResult;
|
|
230
|
+
return {
|
|
231
|
+
Max24HourSend: quota?.Max24HourSend ? Number(quota.Max24HourSend) : undefined,
|
|
232
|
+
MaxSendRate: quota?.MaxSendRate ? Number(quota.MaxSendRate) : undefined,
|
|
233
|
+
SentLast24Hours: quota?.SentLast24Hours ? Number(quota.SentLast24Hours) : undefined
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
async verifyDomain(domain) {
|
|
237
|
+
const result = await this.createEmailIdentity({
|
|
238
|
+
EmailIdentity: domain
|
|
239
|
+
});
|
|
240
|
+
return {
|
|
241
|
+
dkimTokens: result.DkimAttributes?.Tokens,
|
|
242
|
+
verificationStatus: result.DkimAttributes?.Status
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
async sendSimpleEmail(params) {
|
|
246
|
+
const toAddresses = Array.isArray(params.to) ? params.to : [params.to];
|
|
247
|
+
const replyToAddresses = params.replyTo ? Array.isArray(params.replyTo) ? params.replyTo : [params.replyTo] : undefined;
|
|
248
|
+
const body = {};
|
|
249
|
+
if (params.text) {
|
|
250
|
+
body.Text = { Data: params.text };
|
|
251
|
+
}
|
|
252
|
+
if (params.html) {
|
|
253
|
+
body.Html = { Data: params.html };
|
|
254
|
+
}
|
|
255
|
+
return this.sendEmail({
|
|
256
|
+
FromEmailAddress: params.from,
|
|
257
|
+
Destination: {
|
|
258
|
+
ToAddresses: toAddresses
|
|
259
|
+
},
|
|
260
|
+
Content: {
|
|
261
|
+
Simple: {
|
|
262
|
+
Subject: { Data: params.subject },
|
|
263
|
+
Body: body
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
ReplyToAddresses: replyToAddresses
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
async sendTemplatedEmail(params) {
|
|
270
|
+
const toAddresses = Array.isArray(params.to) ? params.to : [params.to];
|
|
271
|
+
const replyToAddresses = params.replyTo ? Array.isArray(params.replyTo) ? params.replyTo : [params.replyTo] : undefined;
|
|
272
|
+
return this.sendEmail({
|
|
273
|
+
FromEmailAddress: params.from,
|
|
274
|
+
Destination: {
|
|
275
|
+
ToAddresses: toAddresses
|
|
276
|
+
},
|
|
277
|
+
Content: {
|
|
278
|
+
Template: {
|
|
279
|
+
TemplateName: params.templateName,
|
|
280
|
+
TemplateData: JSON.stringify(params.templateData)
|
|
281
|
+
}
|
|
282
|
+
},
|
|
283
|
+
ReplyToAddresses: replyToAddresses
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
async getDkimRecords(domain) {
|
|
287
|
+
const identity = await this.getEmailIdentity(domain);
|
|
288
|
+
if (!identity.DkimAttributes?.Tokens) {
|
|
289
|
+
return [];
|
|
290
|
+
}
|
|
291
|
+
return identity.DkimAttributes.Tokens.map((token) => ({
|
|
292
|
+
name: `${token}._domainkey.${domain}`,
|
|
293
|
+
type: "CNAME",
|
|
294
|
+
value: `${token}.dkim.amazonses.com`
|
|
295
|
+
}));
|
|
296
|
+
}
|
|
297
|
+
async isDomainVerified(domain) {
|
|
298
|
+
try {
|
|
299
|
+
const identity = await this.getEmailIdentity(domain);
|
|
300
|
+
return identity.VerificationStatus === "SUCCESS" && identity.SendingEnabled === true;
|
|
301
|
+
} catch {
|
|
302
|
+
return false;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
async waitForDomainVerification(domain, maxAttempts = 60, delayMs = 30000) {
|
|
306
|
+
for (let i = 0;i < maxAttempts; i++) {
|
|
307
|
+
const isVerified = await this.isDomainVerified(domain);
|
|
308
|
+
if (isVerified) {
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
312
|
+
}
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
buildFormBody(params) {
|
|
316
|
+
const entries = Object.entries(params).filter(([, value]) => value !== undefined).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
|
|
317
|
+
return entries.join("&");
|
|
318
|
+
}
|
|
319
|
+
async createReceiptRuleSet(ruleSetName) {
|
|
320
|
+
await this.client.request({
|
|
321
|
+
service: "ses",
|
|
322
|
+
region: this.region,
|
|
323
|
+
method: "POST",
|
|
324
|
+
path: "/",
|
|
325
|
+
headers: {
|
|
326
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
327
|
+
},
|
|
328
|
+
body: this.buildFormBody({
|
|
329
|
+
Action: "CreateReceiptRuleSet",
|
|
330
|
+
Version: "2010-12-01",
|
|
331
|
+
RuleSetName: ruleSetName
|
|
332
|
+
})
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
async deleteReceiptRuleSet(ruleSetName) {
|
|
336
|
+
await this.client.request({
|
|
337
|
+
service: "ses",
|
|
338
|
+
region: this.region,
|
|
339
|
+
method: "POST",
|
|
340
|
+
path: "/",
|
|
341
|
+
headers: {
|
|
342
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
343
|
+
},
|
|
344
|
+
body: this.buildFormBody({
|
|
345
|
+
Action: "DeleteReceiptRuleSet",
|
|
346
|
+
Version: "2010-12-01",
|
|
347
|
+
RuleSetName: ruleSetName
|
|
348
|
+
})
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
async setActiveReceiptRuleSet(ruleSetName) {
|
|
352
|
+
await this.client.request({
|
|
353
|
+
service: "ses",
|
|
354
|
+
region: this.region,
|
|
355
|
+
method: "POST",
|
|
356
|
+
path: "/",
|
|
357
|
+
headers: {
|
|
358
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
359
|
+
},
|
|
360
|
+
body: this.buildFormBody({
|
|
361
|
+
Action: "SetActiveReceiptRuleSet",
|
|
362
|
+
Version: "2010-12-01",
|
|
363
|
+
RuleSetName: ruleSetName
|
|
364
|
+
})
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
async listReceiptRuleSets(nextToken) {
|
|
368
|
+
const formParams = {
|
|
369
|
+
Action: "ListReceiptRuleSets",
|
|
370
|
+
Version: "2010-12-01"
|
|
371
|
+
};
|
|
372
|
+
if (nextToken) {
|
|
373
|
+
formParams.NextToken = nextToken;
|
|
374
|
+
}
|
|
375
|
+
const result = await this.client.request({
|
|
376
|
+
service: "ses",
|
|
377
|
+
region: this.region,
|
|
378
|
+
method: "POST",
|
|
379
|
+
path: "/",
|
|
380
|
+
headers: {
|
|
381
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
382
|
+
},
|
|
383
|
+
body: this.buildFormBody(formParams)
|
|
384
|
+
});
|
|
385
|
+
const ruleSetsResult = result?.ListReceiptRuleSetsResponse?.ListReceiptRuleSetsResult || result?.ListReceiptRuleSetsResult;
|
|
386
|
+
const ruleSets = ruleSetsResult?.RuleSets?.member;
|
|
387
|
+
return {
|
|
388
|
+
RuleSets: Array.isArray(ruleSets) ? ruleSets : ruleSets ? [ruleSets] : [],
|
|
389
|
+
NextToken: ruleSetsResult?.NextToken
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
async describeReceiptRuleSet(ruleSetName) {
|
|
393
|
+
const result = await this.client.request({
|
|
394
|
+
service: "ses",
|
|
395
|
+
region: this.region,
|
|
396
|
+
method: "POST",
|
|
397
|
+
path: "/",
|
|
398
|
+
headers: {
|
|
399
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
400
|
+
},
|
|
401
|
+
body: this.buildFormBody({
|
|
402
|
+
Action: "DescribeReceiptRuleSet",
|
|
403
|
+
Version: "2010-12-01",
|
|
404
|
+
RuleSetName: ruleSetName
|
|
405
|
+
})
|
|
406
|
+
});
|
|
407
|
+
const response = result?.DescribeReceiptRuleSetResponse?.DescribeReceiptRuleSetResult || result?.DescribeReceiptRuleSetResult;
|
|
408
|
+
const rules = response?.Rules?.member;
|
|
409
|
+
return {
|
|
410
|
+
Metadata: response?.Metadata,
|
|
411
|
+
Rules: Array.isArray(rules) ? rules : rules ? [rules] : []
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
async createReceiptRule(params) {
|
|
415
|
+
const formParams = {
|
|
416
|
+
Action: "CreateReceiptRule",
|
|
417
|
+
Version: "2010-12-01",
|
|
418
|
+
RuleSetName: params.RuleSetName,
|
|
419
|
+
"Rule.Name": params.Rule.Name,
|
|
420
|
+
"Rule.Enabled": params.Rule.Enabled !== false ? "true" : "false"
|
|
421
|
+
};
|
|
422
|
+
if (params.Rule.TlsPolicy) {
|
|
423
|
+
formParams["Rule.TlsPolicy"] = params.Rule.TlsPolicy;
|
|
424
|
+
}
|
|
425
|
+
if (params.Rule.ScanEnabled !== undefined) {
|
|
426
|
+
formParams["Rule.ScanEnabled"] = params.Rule.ScanEnabled ? "true" : "false";
|
|
427
|
+
}
|
|
428
|
+
if (params.After) {
|
|
429
|
+
formParams.After = params.After;
|
|
430
|
+
}
|
|
431
|
+
if (params.Rule.Recipients) {
|
|
432
|
+
params.Rule.Recipients.forEach((recipient, index) => {
|
|
433
|
+
formParams[`Rule.Recipients.member.${index + 1}`] = recipient;
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
params.Rule.Actions.forEach((action, index) => {
|
|
437
|
+
const actionNum = index + 1;
|
|
438
|
+
if (action.S3Action) {
|
|
439
|
+
formParams[`Rule.Actions.member.${actionNum}.S3Action.BucketName`] = action.S3Action.BucketName;
|
|
440
|
+
if (action.S3Action.ObjectKeyPrefix) {
|
|
441
|
+
formParams[`Rule.Actions.member.${actionNum}.S3Action.ObjectKeyPrefix`] = action.S3Action.ObjectKeyPrefix;
|
|
442
|
+
}
|
|
443
|
+
if (action.S3Action.KmsKeyArn) {
|
|
444
|
+
formParams[`Rule.Actions.member.${actionNum}.S3Action.KmsKeyArn`] = action.S3Action.KmsKeyArn;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
if (action.LambdaAction) {
|
|
448
|
+
formParams[`Rule.Actions.member.${actionNum}.LambdaAction.FunctionArn`] = action.LambdaAction.FunctionArn;
|
|
449
|
+
formParams[`Rule.Actions.member.${actionNum}.LambdaAction.InvocationType`] = action.LambdaAction.InvocationType || "Event";
|
|
450
|
+
}
|
|
451
|
+
if (action.SNSAction) {
|
|
452
|
+
formParams[`Rule.Actions.member.${actionNum}.SNSAction.TopicArn`] = action.SNSAction.TopicArn;
|
|
453
|
+
if (action.SNSAction.Encoding) {
|
|
454
|
+
formParams[`Rule.Actions.member.${actionNum}.SNSAction.Encoding`] = action.SNSAction.Encoding;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
if (action.StopAction) {
|
|
458
|
+
formParams[`Rule.Actions.member.${actionNum}.StopAction.Scope`] = action.StopAction.Scope;
|
|
459
|
+
if (action.StopAction.TopicArn) {
|
|
460
|
+
formParams[`Rule.Actions.member.${actionNum}.StopAction.TopicArn`] = action.StopAction.TopicArn;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
await this.client.request({
|
|
465
|
+
service: "ses",
|
|
466
|
+
region: this.region,
|
|
467
|
+
method: "POST",
|
|
468
|
+
path: "/",
|
|
469
|
+
headers: {
|
|
470
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
471
|
+
},
|
|
472
|
+
body: this.buildFormBody(formParams)
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
async deleteReceiptRule(ruleSetName, ruleName) {
|
|
476
|
+
await this.client.request({
|
|
477
|
+
service: "ses",
|
|
478
|
+
region: this.region,
|
|
479
|
+
method: "POST",
|
|
480
|
+
path: "/",
|
|
481
|
+
headers: {
|
|
482
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
483
|
+
},
|
|
484
|
+
body: this.buildFormBody({
|
|
485
|
+
Action: "DeleteReceiptRule",
|
|
486
|
+
Version: "2010-12-01",
|
|
487
|
+
RuleSetName: ruleSetName,
|
|
488
|
+
RuleName: ruleName
|
|
489
|
+
})
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
async receiptRuleSetExists(ruleSetName) {
|
|
493
|
+
try {
|
|
494
|
+
await this.describeReceiptRuleSet(ruleSetName);
|
|
495
|
+
return true;
|
|
496
|
+
} catch (error) {
|
|
497
|
+
if (error.code === "RuleSetDoesNotExist" || error.statusCode === 400) {
|
|
498
|
+
return false;
|
|
499
|
+
}
|
|
500
|
+
throw error;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
async getActiveReceiptRuleSet() {
|
|
504
|
+
const result = await this.client.request({
|
|
505
|
+
service: "ses",
|
|
506
|
+
region: this.region,
|
|
507
|
+
method: "POST",
|
|
508
|
+
path: "/",
|
|
509
|
+
headers: {
|
|
510
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
511
|
+
},
|
|
512
|
+
body: this.buildFormBody({
|
|
513
|
+
Action: "DescribeActiveReceiptRuleSet",
|
|
514
|
+
Version: "2010-12-01"
|
|
515
|
+
})
|
|
516
|
+
});
|
|
517
|
+
const response = result?.DescribeActiveReceiptRuleSetResponse?.DescribeActiveReceiptRuleSetResult || result?.DescribeActiveReceiptRuleSetResult;
|
|
518
|
+
if (!response?.Metadata) {
|
|
519
|
+
return null;
|
|
520
|
+
}
|
|
521
|
+
const rules = response?.Rules?.member;
|
|
522
|
+
return {
|
|
523
|
+
Metadata: response?.Metadata,
|
|
524
|
+
Rules: Array.isArray(rules) ? rules : rules ? [rules] : []
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
async updateReceiptRule(params) {
|
|
528
|
+
const formParams = {
|
|
529
|
+
Action: "UpdateReceiptRule",
|
|
530
|
+
Version: "2010-12-01",
|
|
531
|
+
RuleSetName: params.RuleSetName,
|
|
532
|
+
"Rule.Name": params.Rule.Name
|
|
533
|
+
};
|
|
534
|
+
if (params.Rule.Enabled !== undefined) {
|
|
535
|
+
formParams["Rule.Enabled"] = params.Rule.Enabled ? "true" : "false";
|
|
536
|
+
}
|
|
537
|
+
if (params.Rule.TlsPolicy) {
|
|
538
|
+
formParams["Rule.TlsPolicy"] = params.Rule.TlsPolicy;
|
|
539
|
+
}
|
|
540
|
+
if (params.Rule.ScanEnabled !== undefined) {
|
|
541
|
+
formParams["Rule.ScanEnabled"] = params.Rule.ScanEnabled ? "true" : "false";
|
|
542
|
+
}
|
|
543
|
+
if (params.Rule.Recipients) {
|
|
544
|
+
params.Rule.Recipients.forEach((recipient, index) => {
|
|
545
|
+
formParams[`Rule.Recipients.member.${index + 1}`] = recipient;
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
params.Rule.Actions.forEach((action, index) => {
|
|
549
|
+
const actionNum = index + 1;
|
|
550
|
+
if (action.S3Action) {
|
|
551
|
+
formParams[`Rule.Actions.member.${actionNum}.S3Action.BucketName`] = action.S3Action.BucketName;
|
|
552
|
+
if (action.S3Action.ObjectKeyPrefix) {
|
|
553
|
+
formParams[`Rule.Actions.member.${actionNum}.S3Action.ObjectKeyPrefix`] = action.S3Action.ObjectKeyPrefix;
|
|
554
|
+
}
|
|
555
|
+
if (action.S3Action.KmsKeyArn) {
|
|
556
|
+
formParams[`Rule.Actions.member.${actionNum}.S3Action.KmsKeyArn`] = action.S3Action.KmsKeyArn;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
if (action.LambdaAction) {
|
|
560
|
+
formParams[`Rule.Actions.member.${actionNum}.LambdaAction.FunctionArn`] = action.LambdaAction.FunctionArn;
|
|
561
|
+
formParams[`Rule.Actions.member.${actionNum}.LambdaAction.InvocationType`] = action.LambdaAction.InvocationType || "Event";
|
|
562
|
+
}
|
|
563
|
+
if (action.SNSAction) {
|
|
564
|
+
formParams[`Rule.Actions.member.${actionNum}.SNSAction.TopicArn`] = action.SNSAction.TopicArn;
|
|
565
|
+
if (action.SNSAction.Encoding) {
|
|
566
|
+
formParams[`Rule.Actions.member.${actionNum}.SNSAction.Encoding`] = action.SNSAction.Encoding;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
if (action.StopAction) {
|
|
570
|
+
formParams[`Rule.Actions.member.${actionNum}.StopAction.Scope`] = action.StopAction.Scope;
|
|
571
|
+
if (action.StopAction.TopicArn) {
|
|
572
|
+
formParams[`Rule.Actions.member.${actionNum}.StopAction.TopicArn`] = action.StopAction.TopicArn;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
await this.client.request({
|
|
577
|
+
service: "ses",
|
|
578
|
+
region: this.region,
|
|
579
|
+
method: "POST",
|
|
580
|
+
path: "/",
|
|
581
|
+
headers: {
|
|
582
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
583
|
+
},
|
|
584
|
+
body: this.buildFormBody(formParams)
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
async sendRawEmail(params) {
|
|
588
|
+
const rawMessageBase64 = Buffer.from(params.rawMessage).toString("base64");
|
|
589
|
+
const formParams = {
|
|
590
|
+
Action: "SendRawEmail",
|
|
591
|
+
Version: "2010-12-01",
|
|
592
|
+
Source: params.source,
|
|
593
|
+
"RawMessage.Data": rawMessageBase64
|
|
594
|
+
};
|
|
595
|
+
params.destinations.forEach((dest, index) => {
|
|
596
|
+
formParams[`Destinations.member.${index + 1}`] = dest;
|
|
597
|
+
});
|
|
598
|
+
const result = await this.client.request({
|
|
599
|
+
service: "ses",
|
|
600
|
+
region: this.region,
|
|
601
|
+
method: "POST",
|
|
602
|
+
path: "/",
|
|
603
|
+
headers: {
|
|
604
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
605
|
+
},
|
|
606
|
+
body: this.buildFormBody(formParams)
|
|
607
|
+
});
|
|
608
|
+
const response = result?.SendRawEmailResponse?.SendRawEmailResult || result?.SendRawEmailResult;
|
|
609
|
+
return {
|
|
610
|
+
MessageId: response?.MessageId
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
async getSuppressedDestination(emailAddress) {
|
|
614
|
+
try {
|
|
615
|
+
const result = await this.client.request({
|
|
616
|
+
service: "ses",
|
|
617
|
+
region: this.region,
|
|
618
|
+
method: "GET",
|
|
619
|
+
path: `/v2/email/suppression/addresses/${encodeURIComponent(emailAddress)}`
|
|
620
|
+
});
|
|
621
|
+
return result?.SuppressedDestination || null;
|
|
622
|
+
} catch (error) {
|
|
623
|
+
if (error.message?.includes("404") || error.message?.includes("NotFoundException")) {
|
|
624
|
+
return null;
|
|
625
|
+
}
|
|
626
|
+
throw error;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
async deleteSuppressedDestination(emailAddress) {
|
|
630
|
+
await this.client.request({
|
|
631
|
+
service: "ses",
|
|
632
|
+
region: this.region,
|
|
633
|
+
method: "DELETE",
|
|
634
|
+
path: `/v2/email/suppression/addresses/${encodeURIComponent(emailAddress)}`
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// src/aws/iam.ts
|
|
640
|
+
function buildQueryParams(action, params) {
|
|
641
|
+
const queryParams = [`Action=${action}`, "Version=2010-05-08"];
|
|
642
|
+
for (const [key, value] of Object.entries(params)) {
|
|
643
|
+
if (value === undefined || value === null)
|
|
644
|
+
continue;
|
|
645
|
+
if (Array.isArray(value)) {
|
|
646
|
+
value.forEach((item, index) => {
|
|
647
|
+
if (typeof item === "object" && item !== null) {
|
|
648
|
+
for (const [subKey, subValue] of Object.entries(item)) {
|
|
649
|
+
queryParams.push(`${key}.member.${index + 1}.${subKey}=${encodeURIComponent(String(subValue))}`);
|
|
650
|
+
}
|
|
651
|
+
} else {
|
|
652
|
+
queryParams.push(`${key}.member.${index + 1}=${encodeURIComponent(String(item))}`);
|
|
653
|
+
}
|
|
654
|
+
});
|
|
655
|
+
} else if (typeof value === "object") {
|
|
656
|
+
for (const [subKey, subValue] of Object.entries(value)) {
|
|
657
|
+
if (subValue !== undefined && subValue !== null) {
|
|
658
|
+
queryParams.push(`${key}.${subKey}=${encodeURIComponent(String(subValue))}`);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
} else {
|
|
662
|
+
queryParams.push(`${key}=${encodeURIComponent(String(value))}`);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
return queryParams.join("&");
|
|
666
|
+
}
|
|
667
|
+
function parseXmlValue(xml, tag) {
|
|
668
|
+
const regex = new RegExp(`<${tag}>([^<]*)</${tag}>`);
|
|
669
|
+
const match = xml.match(regex);
|
|
670
|
+
return match ? match[1] : undefined;
|
|
671
|
+
}
|
|
672
|
+
function parseXmlArray(xml, containerTag, itemTag) {
|
|
673
|
+
const containerRegex = new RegExp(`<${containerTag}>([\\s\\S]*?)</${containerTag}>`);
|
|
674
|
+
const containerMatch = xml.match(containerRegex);
|
|
675
|
+
if (!containerMatch)
|
|
676
|
+
return [];
|
|
677
|
+
const items = [];
|
|
678
|
+
const itemRegex = new RegExp(`<${itemTag}>([\\s\\S]*?)</${itemTag}>`, "g");
|
|
679
|
+
let match;
|
|
680
|
+
while ((match = itemRegex.exec(containerMatch[1])) !== null) {
|
|
681
|
+
items.push(match[1]);
|
|
682
|
+
}
|
|
683
|
+
return items;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
class IAMClient {
|
|
687
|
+
client;
|
|
688
|
+
region;
|
|
689
|
+
constructor(region = "us-east-1") {
|
|
690
|
+
this.client = new AWSClient;
|
|
691
|
+
this.region = region;
|
|
692
|
+
}
|
|
693
|
+
async request(action, params = {}) {
|
|
694
|
+
const body = buildQueryParams(action, params);
|
|
695
|
+
const response = await this.client.request({
|
|
696
|
+
service: "iam",
|
|
697
|
+
region: this.region,
|
|
698
|
+
method: "POST",
|
|
699
|
+
path: "/",
|
|
700
|
+
headers: {
|
|
701
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
702
|
+
},
|
|
703
|
+
body
|
|
704
|
+
});
|
|
705
|
+
return response;
|
|
706
|
+
}
|
|
707
|
+
async createUser(params) {
|
|
708
|
+
const response = await this.request("CreateUser", params);
|
|
709
|
+
return this.parseUser(response);
|
|
710
|
+
}
|
|
711
|
+
async getUser(params = {}) {
|
|
712
|
+
const response = await this.request("GetUser", params);
|
|
713
|
+
return this.parseUser(response);
|
|
714
|
+
}
|
|
715
|
+
async listUsers(params = {}) {
|
|
716
|
+
const response = await this.request("ListUsers", params);
|
|
717
|
+
const users = this.parseUsers(response);
|
|
718
|
+
const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
|
|
719
|
+
const marker = parseXmlValue(response, "Marker");
|
|
720
|
+
return { Users: users, IsTruncated: isTruncated, Marker: marker };
|
|
721
|
+
}
|
|
722
|
+
async updateUser(params) {
|
|
723
|
+
await this.request("UpdateUser", params);
|
|
724
|
+
}
|
|
725
|
+
async deleteUser(params) {
|
|
726
|
+
await this.request("DeleteUser", params);
|
|
727
|
+
}
|
|
728
|
+
parseUser(xml) {
|
|
729
|
+
return {
|
|
730
|
+
UserName: parseXmlValue(xml, "UserName") || "",
|
|
731
|
+
UserId: parseXmlValue(xml, "UserId") || "",
|
|
732
|
+
Arn: parseXmlValue(xml, "Arn") || "",
|
|
733
|
+
Path: parseXmlValue(xml, "Path"),
|
|
734
|
+
CreateDate: parseXmlValue(xml, "CreateDate"),
|
|
735
|
+
PasswordLastUsed: parseXmlValue(xml, "PasswordLastUsed")
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
parseUsers(xml) {
|
|
739
|
+
const memberXmls = parseXmlArray(xml, "Users", "member");
|
|
740
|
+
return memberXmls.map((memberXml) => ({
|
|
741
|
+
UserName: parseXmlValue(memberXml, "UserName") || "",
|
|
742
|
+
UserId: parseXmlValue(memberXml, "UserId") || "",
|
|
743
|
+
Arn: parseXmlValue(memberXml, "Arn") || "",
|
|
744
|
+
Path: parseXmlValue(memberXml, "Path"),
|
|
745
|
+
CreateDate: parseXmlValue(memberXml, "CreateDate"),
|
|
746
|
+
PasswordLastUsed: parseXmlValue(memberXml, "PasswordLastUsed")
|
|
747
|
+
}));
|
|
748
|
+
}
|
|
749
|
+
async createGroup(params) {
|
|
750
|
+
const response = await this.request("CreateGroup", params);
|
|
751
|
+
return this.parseGroup(response);
|
|
752
|
+
}
|
|
753
|
+
async getGroup(params) {
|
|
754
|
+
const response = await this.request("GetGroup", params);
|
|
755
|
+
const group = this.parseGroup(response);
|
|
756
|
+
const users = this.parseUsers(response);
|
|
757
|
+
const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
|
|
758
|
+
const marker = parseXmlValue(response, "Marker");
|
|
759
|
+
return { Group: group, Users: users, IsTruncated: isTruncated, Marker: marker };
|
|
760
|
+
}
|
|
761
|
+
async listGroups(params = {}) {
|
|
762
|
+
const response = await this.request("ListGroups", params);
|
|
763
|
+
const groups = this.parseGroups(response);
|
|
764
|
+
const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
|
|
765
|
+
const marker = parseXmlValue(response, "Marker");
|
|
766
|
+
return { Groups: groups, IsTruncated: isTruncated, Marker: marker };
|
|
767
|
+
}
|
|
768
|
+
async updateGroup(params) {
|
|
769
|
+
await this.request("UpdateGroup", params);
|
|
770
|
+
}
|
|
771
|
+
async deleteGroup(params) {
|
|
772
|
+
await this.request("DeleteGroup", params);
|
|
773
|
+
}
|
|
774
|
+
async addUserToGroup(params) {
|
|
775
|
+
await this.request("AddUserToGroup", params);
|
|
776
|
+
}
|
|
777
|
+
async removeUserFromGroup(params) {
|
|
778
|
+
await this.request("RemoveUserFromGroup", params);
|
|
779
|
+
}
|
|
780
|
+
async listGroupsForUser(params) {
|
|
781
|
+
const response = await this.request("ListGroupsForUser", params);
|
|
782
|
+
const groups = this.parseGroups(response);
|
|
783
|
+
const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
|
|
784
|
+
const marker = parseXmlValue(response, "Marker");
|
|
785
|
+
return { Groups: groups, IsTruncated: isTruncated, Marker: marker };
|
|
786
|
+
}
|
|
787
|
+
parseGroup(xml) {
|
|
788
|
+
return {
|
|
789
|
+
GroupName: parseXmlValue(xml, "GroupName") || "",
|
|
790
|
+
GroupId: parseXmlValue(xml, "GroupId") || "",
|
|
791
|
+
Arn: parseXmlValue(xml, "Arn") || "",
|
|
792
|
+
Path: parseXmlValue(xml, "Path"),
|
|
793
|
+
CreateDate: parseXmlValue(xml, "CreateDate")
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
parseGroups(xml) {
|
|
797
|
+
const memberXmls = parseXmlArray(xml, "Groups", "member");
|
|
798
|
+
return memberXmls.map((memberXml) => ({
|
|
799
|
+
GroupName: parseXmlValue(memberXml, "GroupName") || "",
|
|
800
|
+
GroupId: parseXmlValue(memberXml, "GroupId") || "",
|
|
801
|
+
Arn: parseXmlValue(memberXml, "Arn") || "",
|
|
802
|
+
Path: parseXmlValue(memberXml, "Path"),
|
|
803
|
+
CreateDate: parseXmlValue(memberXml, "CreateDate")
|
|
804
|
+
}));
|
|
805
|
+
}
|
|
806
|
+
async createRole(params) {
|
|
807
|
+
const response = await this.request("CreateRole", params);
|
|
808
|
+
return this.parseRole(response);
|
|
809
|
+
}
|
|
810
|
+
async getRole(params) {
|
|
811
|
+
const response = await this.request("GetRole", params);
|
|
812
|
+
return this.parseRole(response);
|
|
813
|
+
}
|
|
814
|
+
async listRoles(params = {}) {
|
|
815
|
+
const response = await this.request("ListRoles", params);
|
|
816
|
+
const roles = this.parseRoles(response);
|
|
817
|
+
const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
|
|
818
|
+
const marker = parseXmlValue(response, "Marker");
|
|
819
|
+
return { Roles: roles, IsTruncated: isTruncated, Marker: marker };
|
|
820
|
+
}
|
|
821
|
+
async updateRole(params) {
|
|
822
|
+
await this.request("UpdateRole", params);
|
|
823
|
+
}
|
|
824
|
+
async updateRoleDescription(params) {
|
|
825
|
+
const response = await this.request("UpdateRoleDescription", params);
|
|
826
|
+
return this.parseRole(response);
|
|
827
|
+
}
|
|
828
|
+
async updateAssumeRolePolicy(params) {
|
|
829
|
+
await this.request("UpdateAssumeRolePolicy", params);
|
|
830
|
+
}
|
|
831
|
+
async deleteRole(params) {
|
|
832
|
+
await this.request("DeleteRole", params);
|
|
833
|
+
}
|
|
834
|
+
async tagRole(params) {
|
|
835
|
+
await this.request("TagRole", params);
|
|
836
|
+
}
|
|
837
|
+
async untagRole(params) {
|
|
838
|
+
await this.request("UntagRole", params);
|
|
839
|
+
}
|
|
840
|
+
async listRoleTags(params) {
|
|
841
|
+
const response = await this.request("ListRoleTags", params);
|
|
842
|
+
const tags = this.parseTags(response);
|
|
843
|
+
const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
|
|
844
|
+
const marker = parseXmlValue(response, "Marker");
|
|
845
|
+
return { Tags: tags, IsTruncated: isTruncated, Marker: marker };
|
|
846
|
+
}
|
|
847
|
+
parseRole(xml) {
|
|
848
|
+
return {
|
|
849
|
+
RoleName: parseXmlValue(xml, "RoleName") || "",
|
|
850
|
+
RoleId: parseXmlValue(xml, "RoleId") || "",
|
|
851
|
+
Arn: parseXmlValue(xml, "Arn") || "",
|
|
852
|
+
Path: parseXmlValue(xml, "Path"),
|
|
853
|
+
CreateDate: parseXmlValue(xml, "CreateDate"),
|
|
854
|
+
AssumeRolePolicyDocument: parseXmlValue(xml, "AssumeRolePolicyDocument"),
|
|
855
|
+
Description: parseXmlValue(xml, "Description"),
|
|
856
|
+
MaxSessionDuration: parseXmlValue(xml, "MaxSessionDuration") ? Number.parseInt(parseXmlValue(xml, "MaxSessionDuration"), 10) : undefined
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
|
+
parseRoles(xml) {
|
|
860
|
+
const memberXmls = parseXmlArray(xml, "Roles", "member");
|
|
861
|
+
return memberXmls.map((memberXml) => ({
|
|
862
|
+
RoleName: parseXmlValue(memberXml, "RoleName") || "",
|
|
863
|
+
RoleId: parseXmlValue(memberXml, "RoleId") || "",
|
|
864
|
+
Arn: parseXmlValue(memberXml, "Arn") || "",
|
|
865
|
+
Path: parseXmlValue(memberXml, "Path"),
|
|
866
|
+
CreateDate: parseXmlValue(memberXml, "CreateDate"),
|
|
867
|
+
AssumeRolePolicyDocument: parseXmlValue(memberXml, "AssumeRolePolicyDocument"),
|
|
868
|
+
Description: parseXmlValue(memberXml, "Description"),
|
|
869
|
+
MaxSessionDuration: parseXmlValue(memberXml, "MaxSessionDuration") ? Number.parseInt(parseXmlValue(memberXml, "MaxSessionDuration"), 10) : undefined
|
|
870
|
+
}));
|
|
871
|
+
}
|
|
872
|
+
parseTags(xml) {
|
|
873
|
+
const memberXmls = parseXmlArray(xml, "Tags", "member");
|
|
874
|
+
return memberXmls.map((memberXml) => ({
|
|
875
|
+
Key: parseXmlValue(memberXml, "Key") || "",
|
|
876
|
+
Value: parseXmlValue(memberXml, "Value") || ""
|
|
877
|
+
}));
|
|
878
|
+
}
|
|
879
|
+
async createPolicy(params) {
|
|
880
|
+
const response = await this.request("CreatePolicy", params);
|
|
881
|
+
return this.parsePolicy(response);
|
|
882
|
+
}
|
|
883
|
+
async getPolicy(params) {
|
|
884
|
+
const response = await this.request("GetPolicy", params);
|
|
885
|
+
return this.parsePolicy(response);
|
|
886
|
+
}
|
|
887
|
+
async getPolicyVersion(params) {
|
|
888
|
+
const response = await this.request("GetPolicyVersion", params);
|
|
889
|
+
return {
|
|
890
|
+
VersionId: parseXmlValue(response, "VersionId") || "",
|
|
891
|
+
IsDefaultVersion: parseXmlValue(response, "IsDefaultVersion") === "true",
|
|
892
|
+
CreateDate: parseXmlValue(response, "CreateDate"),
|
|
893
|
+
Document: parseXmlValue(response, "Document")
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
async listPolicies(params = {}) {
|
|
897
|
+
const response = await this.request("ListPolicies", params);
|
|
898
|
+
const policies = this.parsePolicies(response);
|
|
899
|
+
const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
|
|
900
|
+
const marker = parseXmlValue(response, "Marker");
|
|
901
|
+
return { Policies: policies, IsTruncated: isTruncated, Marker: marker };
|
|
902
|
+
}
|
|
903
|
+
async listPolicyVersions(params) {
|
|
904
|
+
const response = await this.request("ListPolicyVersions", params);
|
|
905
|
+
const versions = this.parsePolicyVersions(response);
|
|
906
|
+
const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
|
|
907
|
+
const marker = parseXmlValue(response, "Marker");
|
|
908
|
+
return { Versions: versions, IsTruncated: isTruncated, Marker: marker };
|
|
909
|
+
}
|
|
910
|
+
async createPolicyVersion(params) {
|
|
911
|
+
const response = await this.request("CreatePolicyVersion", params);
|
|
912
|
+
return {
|
|
913
|
+
VersionId: parseXmlValue(response, "VersionId") || "",
|
|
914
|
+
IsDefaultVersion: parseXmlValue(response, "IsDefaultVersion") === "true",
|
|
915
|
+
CreateDate: parseXmlValue(response, "CreateDate")
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
async deletePolicyVersion(params) {
|
|
919
|
+
await this.request("DeletePolicyVersion", params);
|
|
920
|
+
}
|
|
921
|
+
async setDefaultPolicyVersion(params) {
|
|
922
|
+
await this.request("SetDefaultPolicyVersion", params);
|
|
923
|
+
}
|
|
924
|
+
async deletePolicy(params) {
|
|
925
|
+
await this.request("DeletePolicy", params);
|
|
926
|
+
}
|
|
927
|
+
async attachUserPolicy(params) {
|
|
928
|
+
await this.request("AttachUserPolicy", params);
|
|
929
|
+
}
|
|
930
|
+
async detachUserPolicy(params) {
|
|
931
|
+
await this.request("DetachUserPolicy", params);
|
|
932
|
+
}
|
|
933
|
+
async attachGroupPolicy(params) {
|
|
934
|
+
await this.request("AttachGroupPolicy", params);
|
|
935
|
+
}
|
|
936
|
+
async detachGroupPolicy(params) {
|
|
937
|
+
await this.request("DetachGroupPolicy", params);
|
|
938
|
+
}
|
|
939
|
+
async attachRolePolicy(params) {
|
|
940
|
+
await this.request("AttachRolePolicy", params);
|
|
941
|
+
}
|
|
942
|
+
async detachRolePolicy(params) {
|
|
943
|
+
await this.request("DetachRolePolicy", params);
|
|
944
|
+
}
|
|
945
|
+
async listAttachedUserPolicies(params) {
|
|
946
|
+
const response = await this.request("ListAttachedUserPolicies", params);
|
|
947
|
+
const policies = this.parseAttachedPolicies(response);
|
|
948
|
+
const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
|
|
949
|
+
const marker = parseXmlValue(response, "Marker");
|
|
950
|
+
return { AttachedPolicies: policies, IsTruncated: isTruncated, Marker: marker };
|
|
951
|
+
}
|
|
952
|
+
async listAttachedGroupPolicies(params) {
|
|
953
|
+
const response = await this.request("ListAttachedGroupPolicies", params);
|
|
954
|
+
const policies = this.parseAttachedPolicies(response);
|
|
955
|
+
const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
|
|
956
|
+
const marker = parseXmlValue(response, "Marker");
|
|
957
|
+
return { AttachedPolicies: policies, IsTruncated: isTruncated, Marker: marker };
|
|
958
|
+
}
|
|
959
|
+
async listAttachedRolePolicies(params) {
|
|
960
|
+
const response = await this.request("ListAttachedRolePolicies", params);
|
|
961
|
+
const policies = this.parseAttachedPolicies(response);
|
|
962
|
+
const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
|
|
963
|
+
const marker = parseXmlValue(response, "Marker");
|
|
964
|
+
return { AttachedPolicies: policies, IsTruncated: isTruncated, Marker: marker };
|
|
965
|
+
}
|
|
966
|
+
parsePolicy(xml) {
|
|
967
|
+
return {
|
|
968
|
+
PolicyName: parseXmlValue(xml, "PolicyName") || "",
|
|
969
|
+
PolicyId: parseXmlValue(xml, "PolicyId") || "",
|
|
970
|
+
Arn: parseXmlValue(xml, "Arn") || "",
|
|
971
|
+
Path: parseXmlValue(xml, "Path"),
|
|
972
|
+
DefaultVersionId: parseXmlValue(xml, "DefaultVersionId"),
|
|
973
|
+
AttachmentCount: parseXmlValue(xml, "AttachmentCount") ? Number.parseInt(parseXmlValue(xml, "AttachmentCount"), 10) : undefined,
|
|
974
|
+
PermissionsBoundaryUsageCount: parseXmlValue(xml, "PermissionsBoundaryUsageCount") ? Number.parseInt(parseXmlValue(xml, "PermissionsBoundaryUsageCount"), 10) : undefined,
|
|
975
|
+
IsAttachable: parseXmlValue(xml, "IsAttachable") === "true",
|
|
976
|
+
Description: parseXmlValue(xml, "Description"),
|
|
977
|
+
CreateDate: parseXmlValue(xml, "CreateDate"),
|
|
978
|
+
UpdateDate: parseXmlValue(xml, "UpdateDate")
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
parsePolicies(xml) {
|
|
982
|
+
const memberXmls = parseXmlArray(xml, "Policies", "member");
|
|
983
|
+
return memberXmls.map((memberXml) => ({
|
|
984
|
+
PolicyName: parseXmlValue(memberXml, "PolicyName") || "",
|
|
985
|
+
PolicyId: parseXmlValue(memberXml, "PolicyId") || "",
|
|
986
|
+
Arn: parseXmlValue(memberXml, "Arn") || "",
|
|
987
|
+
Path: parseXmlValue(memberXml, "Path"),
|
|
988
|
+
DefaultVersionId: parseXmlValue(memberXml, "DefaultVersionId"),
|
|
989
|
+
AttachmentCount: parseXmlValue(memberXml, "AttachmentCount") ? Number.parseInt(parseXmlValue(memberXml, "AttachmentCount"), 10) : undefined,
|
|
990
|
+
PermissionsBoundaryUsageCount: parseXmlValue(memberXml, "PermissionsBoundaryUsageCount") ? Number.parseInt(parseXmlValue(memberXml, "PermissionsBoundaryUsageCount"), 10) : undefined,
|
|
991
|
+
IsAttachable: parseXmlValue(memberXml, "IsAttachable") === "true",
|
|
992
|
+
Description: parseXmlValue(memberXml, "Description"),
|
|
993
|
+
CreateDate: parseXmlValue(memberXml, "CreateDate"),
|
|
994
|
+
UpdateDate: parseXmlValue(memberXml, "UpdateDate")
|
|
995
|
+
}));
|
|
996
|
+
}
|
|
997
|
+
parsePolicyVersions(xml) {
|
|
998
|
+
const memberXmls = parseXmlArray(xml, "Versions", "member");
|
|
999
|
+
return memberXmls.map((memberXml) => ({
|
|
1000
|
+
VersionId: parseXmlValue(memberXml, "VersionId") || "",
|
|
1001
|
+
IsDefaultVersion: parseXmlValue(memberXml, "IsDefaultVersion") === "true",
|
|
1002
|
+
CreateDate: parseXmlValue(memberXml, "CreateDate")
|
|
1003
|
+
}));
|
|
1004
|
+
}
|
|
1005
|
+
parseAttachedPolicies(xml) {
|
|
1006
|
+
const memberXmls = parseXmlArray(xml, "AttachedPolicies", "member");
|
|
1007
|
+
return memberXmls.map((memberXml) => ({
|
|
1008
|
+
PolicyName: parseXmlValue(memberXml, "PolicyName") || "",
|
|
1009
|
+
PolicyArn: parseXmlValue(memberXml, "PolicyArn") || ""
|
|
1010
|
+
}));
|
|
1011
|
+
}
|
|
1012
|
+
async putUserPolicy(params) {
|
|
1013
|
+
await this.request("PutUserPolicy", params);
|
|
1014
|
+
}
|
|
1015
|
+
async getUserPolicy(params) {
|
|
1016
|
+
const response = await this.request("GetUserPolicy", params);
|
|
1017
|
+
return {
|
|
1018
|
+
UserName: parseXmlValue(response, "UserName") || "",
|
|
1019
|
+
PolicyName: parseXmlValue(response, "PolicyName") || "",
|
|
1020
|
+
PolicyDocument: decodeURIComponent(parseXmlValue(response, "PolicyDocument") || "")
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
async deleteUserPolicy(params) {
|
|
1024
|
+
await this.request("DeleteUserPolicy", params);
|
|
1025
|
+
}
|
|
1026
|
+
async listUserPolicies(params) {
|
|
1027
|
+
const response = await this.request("ListUserPolicies", params);
|
|
1028
|
+
const policyNames = parseXmlArray(response, "PolicyNames", "member");
|
|
1029
|
+
const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
|
|
1030
|
+
const marker = parseXmlValue(response, "Marker");
|
|
1031
|
+
return { PolicyNames: policyNames, IsTruncated: isTruncated, Marker: marker };
|
|
1032
|
+
}
|
|
1033
|
+
async putGroupPolicy(params) {
|
|
1034
|
+
await this.request("PutGroupPolicy", params);
|
|
1035
|
+
}
|
|
1036
|
+
async getGroupPolicy(params) {
|
|
1037
|
+
const response = await this.request("GetGroupPolicy", params);
|
|
1038
|
+
return {
|
|
1039
|
+
GroupName: parseXmlValue(response, "GroupName") || "",
|
|
1040
|
+
PolicyName: parseXmlValue(response, "PolicyName") || "",
|
|
1041
|
+
PolicyDocument: decodeURIComponent(parseXmlValue(response, "PolicyDocument") || "")
|
|
1042
|
+
};
|
|
1043
|
+
}
|
|
1044
|
+
async deleteGroupPolicy(params) {
|
|
1045
|
+
await this.request("DeleteGroupPolicy", params);
|
|
1046
|
+
}
|
|
1047
|
+
async listGroupPolicies(params) {
|
|
1048
|
+
const response = await this.request("ListGroupPolicies", params);
|
|
1049
|
+
const policyNames = parseXmlArray(response, "PolicyNames", "member");
|
|
1050
|
+
const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
|
|
1051
|
+
const marker = parseXmlValue(response, "Marker");
|
|
1052
|
+
return { PolicyNames: policyNames, IsTruncated: isTruncated, Marker: marker };
|
|
1053
|
+
}
|
|
1054
|
+
async putRolePolicy(params) {
|
|
1055
|
+
await this.request("PutRolePolicy", params);
|
|
1056
|
+
}
|
|
1057
|
+
async getRolePolicy(params) {
|
|
1058
|
+
const response = await this.request("GetRolePolicy", params);
|
|
1059
|
+
return {
|
|
1060
|
+
RoleName: parseXmlValue(response, "RoleName") || "",
|
|
1061
|
+
PolicyName: parseXmlValue(response, "PolicyName") || "",
|
|
1062
|
+
PolicyDocument: decodeURIComponent(parseXmlValue(response, "PolicyDocument") || "")
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
1065
|
+
async deleteRolePolicy(params) {
|
|
1066
|
+
await this.request("DeleteRolePolicy", params);
|
|
1067
|
+
}
|
|
1068
|
+
async listRolePolicies(params) {
|
|
1069
|
+
const response = await this.request("ListRolePolicies", params);
|
|
1070
|
+
const policyNames = parseXmlArray(response, "PolicyNames", "member");
|
|
1071
|
+
const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
|
|
1072
|
+
const marker = parseXmlValue(response, "Marker");
|
|
1073
|
+
return { PolicyNames: policyNames, IsTruncated: isTruncated, Marker: marker };
|
|
1074
|
+
}
|
|
1075
|
+
async createAccessKey(params = {}) {
|
|
1076
|
+
const response = await this.request("CreateAccessKey", params);
|
|
1077
|
+
if (typeof response === "object") {
|
|
1078
|
+
const accessKey = response?.CreateAccessKeyResult?.AccessKey || response?.AccessKey;
|
|
1079
|
+
if (accessKey) {
|
|
1080
|
+
return {
|
|
1081
|
+
AccessKey: {
|
|
1082
|
+
UserName: accessKey.UserName || "",
|
|
1083
|
+
AccessKeyId: accessKey.AccessKeyId || "",
|
|
1084
|
+
Status: accessKey.Status || "Active",
|
|
1085
|
+
SecretAccessKey: accessKey.SecretAccessKey || "",
|
|
1086
|
+
CreateDate: accessKey.CreateDate
|
|
1087
|
+
}
|
|
1088
|
+
};
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
return {
|
|
1092
|
+
AccessKey: {
|
|
1093
|
+
UserName: parseXmlValue(response, "UserName") || "",
|
|
1094
|
+
AccessKeyId: parseXmlValue(response, "AccessKeyId") || "",
|
|
1095
|
+
Status: parseXmlValue(response, "Status") || "Active",
|
|
1096
|
+
SecretAccessKey: parseXmlValue(response, "SecretAccessKey") || "",
|
|
1097
|
+
CreateDate: parseXmlValue(response, "CreateDate")
|
|
1098
|
+
}
|
|
1099
|
+
};
|
|
1100
|
+
}
|
|
1101
|
+
async listAccessKeys(params = {}) {
|
|
1102
|
+
const response = await this.request("ListAccessKeys", params);
|
|
1103
|
+
const keys = this.parseAccessKeys(response);
|
|
1104
|
+
const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
|
|
1105
|
+
const marker = parseXmlValue(response, "Marker");
|
|
1106
|
+
return { AccessKeyMetadata: keys, IsTruncated: isTruncated, Marker: marker };
|
|
1107
|
+
}
|
|
1108
|
+
async updateAccessKey(params) {
|
|
1109
|
+
await this.request("UpdateAccessKey", params);
|
|
1110
|
+
}
|
|
1111
|
+
async deleteAccessKey(params) {
|
|
1112
|
+
await this.request("DeleteAccessKey", params);
|
|
1113
|
+
}
|
|
1114
|
+
async getAccessKeyLastUsed(params) {
|
|
1115
|
+
const response = await this.request("GetAccessKeyLastUsed", params);
|
|
1116
|
+
return {
|
|
1117
|
+
UserName: parseXmlValue(response, "UserName") || "",
|
|
1118
|
+
AccessKeyLastUsed: {
|
|
1119
|
+
LastUsedDate: parseXmlValue(response, "LastUsedDate"),
|
|
1120
|
+
ServiceName: parseXmlValue(response, "ServiceName"),
|
|
1121
|
+
Region: parseXmlValue(response, "Region")
|
|
1122
|
+
}
|
|
1123
|
+
};
|
|
1124
|
+
}
|
|
1125
|
+
parseAccessKeys(xml) {
|
|
1126
|
+
const memberXmls = parseXmlArray(xml, "AccessKeyMetadata", "member");
|
|
1127
|
+
return memberXmls.map((memberXml) => ({
|
|
1128
|
+
UserName: parseXmlValue(memberXml, "UserName"),
|
|
1129
|
+
AccessKeyId: parseXmlValue(memberXml, "AccessKeyId") || "",
|
|
1130
|
+
Status: parseXmlValue(memberXml, "Status") || "Active",
|
|
1131
|
+
CreateDate: parseXmlValue(memberXml, "CreateDate")
|
|
1132
|
+
}));
|
|
1133
|
+
}
|
|
1134
|
+
async createInstanceProfile(params) {
|
|
1135
|
+
const response = await this.request("CreateInstanceProfile", params);
|
|
1136
|
+
return this.parseInstanceProfile(response);
|
|
1137
|
+
}
|
|
1138
|
+
async getInstanceProfile(params) {
|
|
1139
|
+
const response = await this.request("GetInstanceProfile", params);
|
|
1140
|
+
return this.parseInstanceProfile(response);
|
|
1141
|
+
}
|
|
1142
|
+
async listInstanceProfiles(params = {}) {
|
|
1143
|
+
const response = await this.request("ListInstanceProfiles", params);
|
|
1144
|
+
const profiles = this.parseInstanceProfiles(response);
|
|
1145
|
+
const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
|
|
1146
|
+
const marker = parseXmlValue(response, "Marker");
|
|
1147
|
+
return { InstanceProfiles: profiles, IsTruncated: isTruncated, Marker: marker };
|
|
1148
|
+
}
|
|
1149
|
+
async listInstanceProfilesForRole(params) {
|
|
1150
|
+
const response = await this.request("ListInstanceProfilesForRole", params);
|
|
1151
|
+
const profiles = this.parseInstanceProfiles(response);
|
|
1152
|
+
const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
|
|
1153
|
+
const marker = parseXmlValue(response, "Marker");
|
|
1154
|
+
return { InstanceProfiles: profiles, IsTruncated: isTruncated, Marker: marker };
|
|
1155
|
+
}
|
|
1156
|
+
async addRoleToInstanceProfile(params) {
|
|
1157
|
+
await this.request("AddRoleToInstanceProfile", params);
|
|
1158
|
+
}
|
|
1159
|
+
async removeRoleFromInstanceProfile(params) {
|
|
1160
|
+
await this.request("RemoveRoleFromInstanceProfile", params);
|
|
1161
|
+
}
|
|
1162
|
+
async deleteInstanceProfile(params) {
|
|
1163
|
+
await this.request("DeleteInstanceProfile", params);
|
|
1164
|
+
}
|
|
1165
|
+
parseInstanceProfile(xml) {
|
|
1166
|
+
return {
|
|
1167
|
+
InstanceProfileName: parseXmlValue(xml, "InstanceProfileName") || "",
|
|
1168
|
+
InstanceProfileId: parseXmlValue(xml, "InstanceProfileId") || "",
|
|
1169
|
+
Arn: parseXmlValue(xml, "Arn") || "",
|
|
1170
|
+
Path: parseXmlValue(xml, "Path"),
|
|
1171
|
+
CreateDate: parseXmlValue(xml, "CreateDate")
|
|
1172
|
+
};
|
|
1173
|
+
}
|
|
1174
|
+
parseInstanceProfiles(xml) {
|
|
1175
|
+
const memberXmls = parseXmlArray(xml, "InstanceProfiles", "member");
|
|
1176
|
+
return memberXmls.map((memberXml) => ({
|
|
1177
|
+
InstanceProfileName: parseXmlValue(memberXml, "InstanceProfileName") || "",
|
|
1178
|
+
InstanceProfileId: parseXmlValue(memberXml, "InstanceProfileId") || "",
|
|
1179
|
+
Arn: parseXmlValue(memberXml, "Arn") || "",
|
|
1180
|
+
Path: parseXmlValue(memberXml, "Path"),
|
|
1181
|
+
CreateDate: parseXmlValue(memberXml, "CreateDate")
|
|
1182
|
+
}));
|
|
1183
|
+
}
|
|
1184
|
+
async getAccountPasswordPolicy() {
|
|
1185
|
+
const response = await this.request("GetAccountPasswordPolicy");
|
|
1186
|
+
return {
|
|
1187
|
+
MinimumPasswordLength: parseXmlValue(response, "MinimumPasswordLength") ? Number.parseInt(parseXmlValue(response, "MinimumPasswordLength"), 10) : undefined,
|
|
1188
|
+
RequireSymbols: parseXmlValue(response, "RequireSymbols") === "true",
|
|
1189
|
+
RequireNumbers: parseXmlValue(response, "RequireNumbers") === "true",
|
|
1190
|
+
RequireUppercaseCharacters: parseXmlValue(response, "RequireUppercaseCharacters") === "true",
|
|
1191
|
+
RequireLowercaseCharacters: parseXmlValue(response, "RequireLowercaseCharacters") === "true",
|
|
1192
|
+
AllowUsersToChangePassword: parseXmlValue(response, "AllowUsersToChangePassword") === "true",
|
|
1193
|
+
ExpirePasswords: parseXmlValue(response, "ExpirePasswords") === "true",
|
|
1194
|
+
MaxPasswordAge: parseXmlValue(response, "MaxPasswordAge") ? Number.parseInt(parseXmlValue(response, "MaxPasswordAge"), 10) : undefined,
|
|
1195
|
+
PasswordReusePrevention: parseXmlValue(response, "PasswordReusePrevention") ? Number.parseInt(parseXmlValue(response, "PasswordReusePrevention"), 10) : undefined,
|
|
1196
|
+
HardExpiry: parseXmlValue(response, "HardExpiry") === "true"
|
|
1197
|
+
};
|
|
1198
|
+
}
|
|
1199
|
+
async updateAccountPasswordPolicy(params) {
|
|
1200
|
+
await this.request("UpdateAccountPasswordPolicy", params);
|
|
1201
|
+
}
|
|
1202
|
+
async deleteAccountPasswordPolicy() {
|
|
1203
|
+
await this.request("DeleteAccountPasswordPolicy");
|
|
1204
|
+
}
|
|
1205
|
+
async getAccountSummary() {
|
|
1206
|
+
const response = await this.request("GetAccountSummary");
|
|
1207
|
+
const summary = {};
|
|
1208
|
+
const entries = parseXmlArray(response, "SummaryMap", "entry");
|
|
1209
|
+
for (const entry of entries) {
|
|
1210
|
+
const key = parseXmlValue(entry, "key");
|
|
1211
|
+
const value = parseXmlValue(entry, "value");
|
|
1212
|
+
if (key && value) {
|
|
1213
|
+
summary[key] = Number.parseInt(value, 10);
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
return summary;
|
|
1217
|
+
}
|
|
1218
|
+
async listAccountAliases() {
|
|
1219
|
+
const response = await this.request("ListAccountAliases");
|
|
1220
|
+
const aliases = parseXmlArray(response, "AccountAliases", "member");
|
|
1221
|
+
const isTruncated = parseXmlValue(response, "IsTruncated") === "true";
|
|
1222
|
+
const marker = parseXmlValue(response, "Marker");
|
|
1223
|
+
return { AccountAliases: aliases, IsTruncated: isTruncated, Marker: marker };
|
|
1224
|
+
}
|
|
1225
|
+
async createAccountAlias(params) {
|
|
1226
|
+
await this.request("CreateAccountAlias", params);
|
|
1227
|
+
}
|
|
1228
|
+
async deleteAccountAlias(params) {
|
|
1229
|
+
await this.request("DeleteAccountAlias", params);
|
|
1230
|
+
}
|
|
1231
|
+
async simulatePrincipalPolicy(params) {
|
|
1232
|
+
const response = await this.request("SimulatePrincipalPolicy", params);
|
|
1233
|
+
return this.parseSimulationResults(response);
|
|
1234
|
+
}
|
|
1235
|
+
parseSimulationResults(xml) {
|
|
1236
|
+
const resultXmls = parseXmlArray(xml, "EvaluationResults", "member");
|
|
1237
|
+
const evaluationResults = resultXmls.map((resultXml) => {
|
|
1238
|
+
const matchedStatementXmls = parseXmlArray(resultXml, "MatchedStatements", "member");
|
|
1239
|
+
const matchedStatements = matchedStatementXmls.map((stmtXml) => ({
|
|
1240
|
+
SourcePolicyId: parseXmlValue(stmtXml, "SourcePolicyId"),
|
|
1241
|
+
SourcePolicyType: parseXmlValue(stmtXml, "SourcePolicyType")
|
|
1242
|
+
}));
|
|
1243
|
+
return {
|
|
1244
|
+
EvalActionName: parseXmlValue(resultXml, "EvalActionName") || "",
|
|
1245
|
+
EvalResourceName: parseXmlValue(resultXml, "EvalResourceName"),
|
|
1246
|
+
EvalDecision: parseXmlValue(resultXml, "EvalDecision") || "",
|
|
1247
|
+
MatchedStatements: matchedStatements.length > 0 ? matchedStatements : undefined
|
|
1248
|
+
};
|
|
1249
|
+
});
|
|
1250
|
+
const isTruncated = parseXmlValue(xml, "IsTruncated") === "true";
|
|
1251
|
+
const marker = parseXmlValue(xml, "Marker");
|
|
1252
|
+
return { EvaluationResults: evaluationResults, IsTruncated: isTruncated, Marker: marker };
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
// src/aws/email.ts
|
|
1257
|
+
class EmailClient {
|
|
1258
|
+
ses;
|
|
1259
|
+
s3;
|
|
1260
|
+
iam;
|
|
1261
|
+
route53;
|
|
1262
|
+
region;
|
|
1263
|
+
domain;
|
|
1264
|
+
defaultFrom;
|
|
1265
|
+
constructor(options = {}) {
|
|
1266
|
+
this.region = options.region || "us-east-1";
|
|
1267
|
+
this.domain = options.domain;
|
|
1268
|
+
this.defaultFrom = options.defaultFrom;
|
|
1269
|
+
this.ses = new SESClient(this.region);
|
|
1270
|
+
this.s3 = new S3Client(this.region);
|
|
1271
|
+
this.iam = new IAMClient(this.region);
|
|
1272
|
+
this.route53 = new Route53Client(this.region);
|
|
1273
|
+
}
|
|
1274
|
+
async send(options) {
|
|
1275
|
+
const from = options.from || this.defaultFrom;
|
|
1276
|
+
if (!from) {
|
|
1277
|
+
throw new Error("From address is required. Set defaultFrom in constructor or provide from in options.");
|
|
1278
|
+
}
|
|
1279
|
+
const fromAddress = options.fromName ? `${options.fromName} <${from}>` : from;
|
|
1280
|
+
const toAddresses = Array.isArray(options.to) ? options.to : [options.to];
|
|
1281
|
+
if (!options.attachments || options.attachments.length === 0) {
|
|
1282
|
+
const result2 = await this.ses.sendSimpleEmail({
|
|
1283
|
+
from: fromAddress,
|
|
1284
|
+
to: toAddresses,
|
|
1285
|
+
subject: options.subject,
|
|
1286
|
+
text: options.text,
|
|
1287
|
+
html: options.html,
|
|
1288
|
+
replyTo: options.replyTo
|
|
1289
|
+
});
|
|
1290
|
+
return { messageId: result2.MessageId || "" };
|
|
1291
|
+
}
|
|
1292
|
+
const rawEmail = this.buildRawEmail(options, fromAddress, toAddresses);
|
|
1293
|
+
const result = await this.ses.sendEmail({
|
|
1294
|
+
FromEmailAddress: fromAddress,
|
|
1295
|
+
Destination: {
|
|
1296
|
+
ToAddresses: toAddresses,
|
|
1297
|
+
CcAddresses: options.cc ? Array.isArray(options.cc) ? options.cc : [options.cc] : undefined,
|
|
1298
|
+
BccAddresses: options.bcc ? Array.isArray(options.bcc) ? options.bcc : [options.bcc] : undefined
|
|
1299
|
+
},
|
|
1300
|
+
Content: {
|
|
1301
|
+
Raw: {
|
|
1302
|
+
Data: Buffer.from(rawEmail).toString("base64")
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
});
|
|
1306
|
+
return { messageId: result.MessageId || "" };
|
|
1307
|
+
}
|
|
1308
|
+
async sendTemplate(options) {
|
|
1309
|
+
const from = options.from || this.defaultFrom;
|
|
1310
|
+
if (!from) {
|
|
1311
|
+
throw new Error("From address is required");
|
|
1312
|
+
}
|
|
1313
|
+
const fromAddress = options.fromName ? `${options.fromName} <${from}>` : from;
|
|
1314
|
+
const result = await this.ses.sendTemplatedEmail({
|
|
1315
|
+
from: fromAddress,
|
|
1316
|
+
to: options.to,
|
|
1317
|
+
templateName: options.templateName,
|
|
1318
|
+
templateData: options.templateData,
|
|
1319
|
+
replyTo: options.replyTo
|
|
1320
|
+
});
|
|
1321
|
+
return { messageId: result.MessageId || "" };
|
|
1322
|
+
}
|
|
1323
|
+
async sendBulk(options) {
|
|
1324
|
+
const from = options.from || this.defaultFrom;
|
|
1325
|
+
if (!from) {
|
|
1326
|
+
throw new Error("From address is required");
|
|
1327
|
+
}
|
|
1328
|
+
const entries = options.recipients.map((r) => ({
|
|
1329
|
+
Destination: {
|
|
1330
|
+
ToAddresses: Array.isArray(r.to) ? r.to : [r.to]
|
|
1331
|
+
},
|
|
1332
|
+
ReplacementEmailContent: r.templateData ? {
|
|
1333
|
+
ReplacementTemplate: {
|
|
1334
|
+
ReplacementTemplateData: JSON.stringify(r.templateData)
|
|
1335
|
+
}
|
|
1336
|
+
} : undefined
|
|
1337
|
+
}));
|
|
1338
|
+
const result = await this.ses.sendBulkEmail({
|
|
1339
|
+
FromEmailAddress: from,
|
|
1340
|
+
BulkEmailEntries: entries,
|
|
1341
|
+
DefaultContent: {
|
|
1342
|
+
Template: {
|
|
1343
|
+
TemplateName: options.templateName,
|
|
1344
|
+
TemplateData: JSON.stringify(options.defaultTemplateData)
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
});
|
|
1348
|
+
return {
|
|
1349
|
+
results: result.BulkEmailEntryResults?.map((r) => ({
|
|
1350
|
+
status: r.Status || "UNKNOWN",
|
|
1351
|
+
messageId: r.MessageId,
|
|
1352
|
+
error: r.Error
|
|
1353
|
+
})) || []
|
|
1354
|
+
};
|
|
1355
|
+
}
|
|
1356
|
+
async setupDomain(domain) {
|
|
1357
|
+
let identity;
|
|
1358
|
+
try {
|
|
1359
|
+
identity = await this.ses.getEmailIdentity(domain);
|
|
1360
|
+
} catch {
|
|
1361
|
+
const createResult = await this.ses.createEmailIdentity({ EmailIdentity: domain });
|
|
1362
|
+
identity = {
|
|
1363
|
+
VerificationStatus: createResult.DkimAttributes?.Status,
|
|
1364
|
+
DkimAttributes: createResult.DkimAttributes,
|
|
1365
|
+
SendingEnabled: createResult.VerifiedForSendingStatus
|
|
1366
|
+
};
|
|
1367
|
+
}
|
|
1368
|
+
try {
|
|
1369
|
+
await this.ses.putEmailIdentityDkimAttributes({
|
|
1370
|
+
EmailIdentity: domain,
|
|
1371
|
+
SigningEnabled: true
|
|
1372
|
+
});
|
|
1373
|
+
} catch {}
|
|
1374
|
+
try {
|
|
1375
|
+
await this.ses.putEmailIdentityMailFromAttributes(domain, {
|
|
1376
|
+
MailFromDomain: `mail.${domain}`,
|
|
1377
|
+
BehaviorOnMxFailure: "USE_DEFAULT_VALUE"
|
|
1378
|
+
});
|
|
1379
|
+
} catch {}
|
|
1380
|
+
const dkimRecords = await this.ses.getDkimRecords(domain);
|
|
1381
|
+
return {
|
|
1382
|
+
domainVerified: identity.SendingEnabled === true,
|
|
1383
|
+
dkimStatus: identity.DkimAttributes?.Status || "UNKNOWN",
|
|
1384
|
+
dkimTokens: identity.DkimAttributes?.Tokens,
|
|
1385
|
+
mailFromStatus: identity.MailFromAttributes?.MailFromDomainStatus
|
|
1386
|
+
};
|
|
1387
|
+
}
|
|
1388
|
+
async getDnsRecords(domain) {
|
|
1389
|
+
const records = [];
|
|
1390
|
+
const dkimRecords = await this.ses.getDkimRecords(domain);
|
|
1391
|
+
for (const record of dkimRecords) {
|
|
1392
|
+
records.push({
|
|
1393
|
+
type: record.type,
|
|
1394
|
+
name: record.name,
|
|
1395
|
+
value: record.value,
|
|
1396
|
+
ttl: 1800
|
|
1397
|
+
});
|
|
1398
|
+
}
|
|
1399
|
+
records.push({
|
|
1400
|
+
type: "MX",
|
|
1401
|
+
name: domain,
|
|
1402
|
+
value: `inbound-smtp.${this.region}.amazonaws.com`,
|
|
1403
|
+
priority: 10,
|
|
1404
|
+
ttl: 3600
|
|
1405
|
+
});
|
|
1406
|
+
records.push({
|
|
1407
|
+
type: "MX",
|
|
1408
|
+
name: `mail.${domain}`,
|
|
1409
|
+
value: `feedback-smtp.${this.region}.amazonses.com`,
|
|
1410
|
+
priority: 10,
|
|
1411
|
+
ttl: 3600
|
|
1412
|
+
});
|
|
1413
|
+
records.push({
|
|
1414
|
+
type: "TXT",
|
|
1415
|
+
name: `mail.${domain}`,
|
|
1416
|
+
value: "v=spf1 include:amazonses.com ~all",
|
|
1417
|
+
ttl: 3600
|
|
1418
|
+
});
|
|
1419
|
+
records.push({
|
|
1420
|
+
type: "TXT",
|
|
1421
|
+
name: `_dmarc.${domain}`,
|
|
1422
|
+
value: `v=DMARC1;p=quarantine;pct=25;rua=mailto:dmarcreports@${domain}`,
|
|
1423
|
+
ttl: 3600
|
|
1424
|
+
});
|
|
1425
|
+
return records;
|
|
1426
|
+
}
|
|
1427
|
+
async isDomainReady(domain) {
|
|
1428
|
+
try {
|
|
1429
|
+
const identity = await this.ses.getEmailIdentity(domain);
|
|
1430
|
+
return identity.SendingEnabled === true && identity.DkimAttributes?.Status === "SUCCESS";
|
|
1431
|
+
} catch {
|
|
1432
|
+
return false;
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
async setupReceiving(config) {
|
|
1436
|
+
const policy = {
|
|
1437
|
+
Version: "2012-10-17",
|
|
1438
|
+
Statement: [
|
|
1439
|
+
{
|
|
1440
|
+
Sid: "AllowSESPuts",
|
|
1441
|
+
Effect: "Allow",
|
|
1442
|
+
Principal: { Service: "ses.amazonaws.com" },
|
|
1443
|
+
Action: "s3:PutObject",
|
|
1444
|
+
Resource: `arn:aws:s3:::${config.bucketName}/*`,
|
|
1445
|
+
Condition: {
|
|
1446
|
+
StringEquals: { "AWS:SourceAccount": config.accountId }
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
]
|
|
1450
|
+
};
|
|
1451
|
+
await this.s3.putBucketPolicy(config.bucketName, policy);
|
|
1452
|
+
try {
|
|
1453
|
+
await this.ses.createReceiptRuleSet(config.ruleSetName);
|
|
1454
|
+
} catch (e) {
|
|
1455
|
+
if (!e.message?.includes("already exists") && e.code !== "AlreadyExists") {
|
|
1456
|
+
throw e;
|
|
1457
|
+
}
|
|
1458
|
+
}
|
|
1459
|
+
const actions = [
|
|
1460
|
+
{
|
|
1461
|
+
S3Action: {
|
|
1462
|
+
BucketName: config.bucketName,
|
|
1463
|
+
ObjectKeyPrefix: config.prefix || "inbox/"
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
];
|
|
1467
|
+
if (config.lambdaArn) {
|
|
1468
|
+
actions.push({
|
|
1469
|
+
LambdaAction: {
|
|
1470
|
+
FunctionArn: config.lambdaArn,
|
|
1471
|
+
InvocationType: "Event"
|
|
1472
|
+
}
|
|
1473
|
+
});
|
|
1474
|
+
}
|
|
1475
|
+
try {
|
|
1476
|
+
await this.ses.createReceiptRule({
|
|
1477
|
+
RuleSetName: config.ruleSetName,
|
|
1478
|
+
Rule: {
|
|
1479
|
+
Name: config.ruleName,
|
|
1480
|
+
Enabled: true,
|
|
1481
|
+
TlsPolicy: "Optional",
|
|
1482
|
+
Recipients: config.recipients || [config.domain],
|
|
1483
|
+
ScanEnabled: config.scanEnabled !== false,
|
|
1484
|
+
Actions: actions
|
|
1485
|
+
}
|
|
1486
|
+
});
|
|
1487
|
+
} catch (e) {
|
|
1488
|
+
if (e.message?.includes("already exists")) {
|
|
1489
|
+
await this.ses.deleteReceiptRule(config.ruleSetName, config.ruleName);
|
|
1490
|
+
await this.ses.createReceiptRule({
|
|
1491
|
+
RuleSetName: config.ruleSetName,
|
|
1492
|
+
Rule: {
|
|
1493
|
+
Name: config.ruleName,
|
|
1494
|
+
Enabled: true,
|
|
1495
|
+
TlsPolicy: "Optional",
|
|
1496
|
+
Recipients: config.recipients || [config.domain],
|
|
1497
|
+
ScanEnabled: config.scanEnabled !== false,
|
|
1498
|
+
Actions: actions
|
|
1499
|
+
}
|
|
1500
|
+
});
|
|
1501
|
+
} else {
|
|
1502
|
+
throw e;
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
await this.ses.setActiveReceiptRuleSet(config.ruleSetName);
|
|
1506
|
+
}
|
|
1507
|
+
async getIncomingEmails(options) {
|
|
1508
|
+
const objects = await this.s3.list({
|
|
1509
|
+
bucket: options.bucket,
|
|
1510
|
+
prefix: options.prefix || "incoming/",
|
|
1511
|
+
maxKeys: options.maxResults || 100
|
|
1512
|
+
});
|
|
1513
|
+
return objects.map((obj) => ({
|
|
1514
|
+
key: obj.Key || "",
|
|
1515
|
+
lastModified: obj.LastModified || "",
|
|
1516
|
+
size: obj.Size || 0
|
|
1517
|
+
}));
|
|
1518
|
+
}
|
|
1519
|
+
async readEmail(options) {
|
|
1520
|
+
return await this.s3.getObject(options.bucket, options.key);
|
|
1521
|
+
}
|
|
1522
|
+
async createSmtpCredentials(options) {
|
|
1523
|
+
const iamUsername = `ses-smtp-${options.username.replace(/[^a-zA-Z0-9]/g, "-")}`;
|
|
1524
|
+
const { AWSClient: AWSClient2 } = await import("./chunk-mj1tmhte.js");
|
|
1525
|
+
const client = new AWSClient2;
|
|
1526
|
+
const buildBody = (action, params) => {
|
|
1527
|
+
const allParams = { Action: action, Version: "2010-05-08", ...params };
|
|
1528
|
+
return Object.entries(allParams).map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join("&");
|
|
1529
|
+
};
|
|
1530
|
+
try {
|
|
1531
|
+
await client.request({
|
|
1532
|
+
service: "iam",
|
|
1533
|
+
region: "us-east-1",
|
|
1534
|
+
method: "POST",
|
|
1535
|
+
path: "/",
|
|
1536
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
1537
|
+
body: buildBody("CreateUser", { UserName: iamUsername })
|
|
1538
|
+
});
|
|
1539
|
+
} catch (e) {
|
|
1540
|
+
if (!e.message?.includes("EntityAlreadyExists")) {
|
|
1541
|
+
throw e;
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
const policy = {
|
|
1545
|
+
Version: "2012-10-17",
|
|
1546
|
+
Statement: [
|
|
1547
|
+
{
|
|
1548
|
+
Effect: "Allow",
|
|
1549
|
+
Action: "ses:SendRawEmail",
|
|
1550
|
+
Resource: "*",
|
|
1551
|
+
Condition: {
|
|
1552
|
+
StringLike: {
|
|
1553
|
+
"ses:FromAddress": `*@${options.domain}`
|
|
1554
|
+
}
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
]
|
|
1558
|
+
};
|
|
1559
|
+
const policyName = `ses-smtp-policy-${options.domain.replace(/\./g, "-")}`;
|
|
1560
|
+
try {
|
|
1561
|
+
await client.request({
|
|
1562
|
+
service: "iam",
|
|
1563
|
+
region: "us-east-1",
|
|
1564
|
+
method: "POST",
|
|
1565
|
+
path: "/",
|
|
1566
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
1567
|
+
body: buildBody("PutUserPolicy", {
|
|
1568
|
+
UserName: iamUsername,
|
|
1569
|
+
PolicyName: policyName,
|
|
1570
|
+
PolicyDocument: JSON.stringify(policy)
|
|
1571
|
+
})
|
|
1572
|
+
});
|
|
1573
|
+
} catch {}
|
|
1574
|
+
const keyResponse = await client.request({
|
|
1575
|
+
service: "iam",
|
|
1576
|
+
region: "us-east-1",
|
|
1577
|
+
method: "POST",
|
|
1578
|
+
path: "/",
|
|
1579
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
1580
|
+
body: buildBody("CreateAccessKey", { UserName: iamUsername })
|
|
1581
|
+
});
|
|
1582
|
+
const accessKey = keyResponse?.CreateAccessKeyResult?.AccessKey;
|
|
1583
|
+
if (!accessKey?.AccessKeyId || !accessKey?.SecretAccessKey) {
|
|
1584
|
+
throw new Error("Failed to create access key");
|
|
1585
|
+
}
|
|
1586
|
+
const smtpPassword = this.deriveSmtpPassword(accessKey.SecretAccessKey);
|
|
1587
|
+
return {
|
|
1588
|
+
username: accessKey.AccessKeyId,
|
|
1589
|
+
password: smtpPassword,
|
|
1590
|
+
server: `email-smtp.${this.region}.amazonaws.com`,
|
|
1591
|
+
port: 587
|
|
1592
|
+
};
|
|
1593
|
+
}
|
|
1594
|
+
deriveSmtpPassword(secretAccessKey) {
|
|
1595
|
+
const crypto = __require("node:crypto");
|
|
1596
|
+
const message = "SendRawEmail";
|
|
1597
|
+
const versionInBytes = Buffer.from([4]);
|
|
1598
|
+
let signature = crypto.createHmac("sha256", `AWS4${secretAccessKey}`).update("11111111").digest();
|
|
1599
|
+
signature = crypto.createHmac("sha256", signature).update(this.region).digest();
|
|
1600
|
+
signature = crypto.createHmac("sha256", signature).update("ses").digest();
|
|
1601
|
+
signature = crypto.createHmac("sha256", signature).update("aws4_request").digest();
|
|
1602
|
+
signature = crypto.createHmac("sha256", signature).update(message).digest();
|
|
1603
|
+
const signatureWithVersion = Buffer.concat([versionInBytes, signature]);
|
|
1604
|
+
return signatureWithVersion.toString("base64");
|
|
1605
|
+
}
|
|
1606
|
+
async createTemplate(options) {
|
|
1607
|
+
await this.ses.createEmailTemplate({
|
|
1608
|
+
TemplateName: options.name,
|
|
1609
|
+
TemplateContent: {
|
|
1610
|
+
Subject: options.subject,
|
|
1611
|
+
Text: options.text,
|
|
1612
|
+
Html: options.html
|
|
1613
|
+
}
|
|
1614
|
+
});
|
|
1615
|
+
}
|
|
1616
|
+
async getTemplate(name) {
|
|
1617
|
+
try {
|
|
1618
|
+
const result = await this.ses.getEmailTemplate(name);
|
|
1619
|
+
return {
|
|
1620
|
+
name: result.TemplateName || name,
|
|
1621
|
+
subject: result.TemplateContent?.Subject,
|
|
1622
|
+
text: result.TemplateContent?.Text,
|
|
1623
|
+
html: result.TemplateContent?.Html
|
|
1624
|
+
};
|
|
1625
|
+
} catch {
|
|
1626
|
+
return null;
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
async deleteTemplate(name) {
|
|
1630
|
+
await this.ses.deleteEmailTemplate(name);
|
|
1631
|
+
}
|
|
1632
|
+
async listTemplates() {
|
|
1633
|
+
const result = await this.ses.listEmailTemplates();
|
|
1634
|
+
return result.TemplatesMetadata?.map((t) => ({
|
|
1635
|
+
name: t.TemplateName || "",
|
|
1636
|
+
createdAt: t.CreatedTimestamp
|
|
1637
|
+
})) || [];
|
|
1638
|
+
}
|
|
1639
|
+
async deploy(config) {
|
|
1640
|
+
const ruleSetName = `${config.appName}-${config.environment}-email-rules`;
|
|
1641
|
+
const ruleName = `${config.appName}-inbound-email`;
|
|
1642
|
+
const bucketName = config.storage?.bucketName || `${config.appName}-${config.environment}-email`;
|
|
1643
|
+
const domainSetup = await this.setupDomain(config.domain);
|
|
1644
|
+
const buckets = await this.s3.listBuckets();
|
|
1645
|
+
const bucketExists = buckets.Buckets?.some((b) => b.Name === bucketName);
|
|
1646
|
+
if (!bucketExists) {
|
|
1647
|
+
await this.s3.createBucket(bucketName);
|
|
1648
|
+
}
|
|
1649
|
+
await this.setupReceiving({
|
|
1650
|
+
domain: config.domain,
|
|
1651
|
+
ruleSetName,
|
|
1652
|
+
ruleName,
|
|
1653
|
+
bucketName,
|
|
1654
|
+
prefix: config.storage?.prefix || "inbox/",
|
|
1655
|
+
accountId: config.accountId,
|
|
1656
|
+
recipients: config.catchAll ? [config.domain] : config.mailboxes,
|
|
1657
|
+
scanEnabled: true
|
|
1658
|
+
});
|
|
1659
|
+
const dnsRecords = await this.getDnsRecords(config.domain);
|
|
1660
|
+
return {
|
|
1661
|
+
success: true,
|
|
1662
|
+
domainVerified: domainSetup.domainVerified,
|
|
1663
|
+
dkimStatus: domainSetup.dkimStatus,
|
|
1664
|
+
receiptRuleSet: ruleSetName,
|
|
1665
|
+
storageBucket: bucketName,
|
|
1666
|
+
dnsRecords
|
|
1667
|
+
};
|
|
1668
|
+
}
|
|
1669
|
+
async undeploy(config) {
|
|
1670
|
+
const ruleSetName = `${config.appName}-${config.environment}-email-rules`;
|
|
1671
|
+
const ruleName = `${config.appName}-inbound-email`;
|
|
1672
|
+
const bucketName = `${config.appName}-${config.environment}-email`;
|
|
1673
|
+
try {
|
|
1674
|
+
await this.ses.deleteReceiptRule(ruleSetName, ruleName);
|
|
1675
|
+
} catch {}
|
|
1676
|
+
try {
|
|
1677
|
+
await this.ses.deleteReceiptRuleSet(ruleSetName);
|
|
1678
|
+
} catch {}
|
|
1679
|
+
if (config.deleteBucket) {
|
|
1680
|
+
try {
|
|
1681
|
+
await this.s3.emptyAndDeleteBucket(bucketName);
|
|
1682
|
+
} catch {}
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
async getSendingStats() {
|
|
1686
|
+
const quota = await this.ses.getSendQuota();
|
|
1687
|
+
return {
|
|
1688
|
+
sentLast24Hours: quota.SentLast24Hours || 0,
|
|
1689
|
+
maxSendRate: quota.MaxSendRate || 0,
|
|
1690
|
+
max24HourSend: quota.Max24HourSend || 0
|
|
1691
|
+
};
|
|
1692
|
+
}
|
|
1693
|
+
buildRawEmail(options, from, to) {
|
|
1694
|
+
const boundary = `----=_Part_${Date.now()}_${Math.random().toString(36).substring(2)}`;
|
|
1695
|
+
let email = "";
|
|
1696
|
+
email += `From: ${from}\r
|
|
1697
|
+
`;
|
|
1698
|
+
email += `To: ${to.join(", ")}\r
|
|
1699
|
+
`;
|
|
1700
|
+
if (options.cc) {
|
|
1701
|
+
const ccList = Array.isArray(options.cc) ? options.cc : [options.cc];
|
|
1702
|
+
email += `Cc: ${ccList.join(", ")}\r
|
|
1703
|
+
`;
|
|
1704
|
+
}
|
|
1705
|
+
email += `Subject: ${options.subject}\r
|
|
1706
|
+
`;
|
|
1707
|
+
email += `MIME-Version: 1.0\r
|
|
1708
|
+
`;
|
|
1709
|
+
email += `Content-Type: multipart/mixed; boundary="${boundary}"\r
|
|
1710
|
+
`;
|
|
1711
|
+
email += `\r
|
|
1712
|
+
`;
|
|
1713
|
+
if (options.text || options.html) {
|
|
1714
|
+
email += `--${boundary}\r
|
|
1715
|
+
`;
|
|
1716
|
+
if (options.html && options.text) {
|
|
1717
|
+
const altBoundary = `----=_Alt_${Date.now()}`;
|
|
1718
|
+
email += `Content-Type: multipart/alternative; boundary="${altBoundary}"\r
|
|
1719
|
+
`;
|
|
1720
|
+
email += `\r
|
|
1721
|
+
`;
|
|
1722
|
+
email += `--${altBoundary}\r
|
|
1723
|
+
`;
|
|
1724
|
+
email += `Content-Type: text/plain; charset="UTF-8"\r
|
|
1725
|
+
`;
|
|
1726
|
+
email += `\r
|
|
1727
|
+
`;
|
|
1728
|
+
email += `${options.text}\r
|
|
1729
|
+
`;
|
|
1730
|
+
email += `--${altBoundary}\r
|
|
1731
|
+
`;
|
|
1732
|
+
email += `Content-Type: text/html; charset="UTF-8"\r
|
|
1733
|
+
`;
|
|
1734
|
+
email += `\r
|
|
1735
|
+
`;
|
|
1736
|
+
email += `${options.html}\r
|
|
1737
|
+
`;
|
|
1738
|
+
email += `--${altBoundary}--\r
|
|
1739
|
+
`;
|
|
1740
|
+
} else if (options.html) {
|
|
1741
|
+
email += `Content-Type: text/html; charset="UTF-8"\r
|
|
1742
|
+
`;
|
|
1743
|
+
email += `\r
|
|
1744
|
+
`;
|
|
1745
|
+
email += `${options.html}\r
|
|
1746
|
+
`;
|
|
1747
|
+
} else {
|
|
1748
|
+
email += `Content-Type: text/plain; charset="UTF-8"\r
|
|
1749
|
+
`;
|
|
1750
|
+
email += `\r
|
|
1751
|
+
`;
|
|
1752
|
+
email += `${options.text}\r
|
|
1753
|
+
`;
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
if (options.attachments) {
|
|
1757
|
+
for (const attachment of options.attachments) {
|
|
1758
|
+
email += `--${boundary}\r
|
|
1759
|
+
`;
|
|
1760
|
+
email += `Content-Type: ${attachment.contentType || "application/octet-stream"}; name="${attachment.filename}"\r
|
|
1761
|
+
`;
|
|
1762
|
+
email += `Content-Transfer-Encoding: base64\r
|
|
1763
|
+
`;
|
|
1764
|
+
email += `Content-Disposition: attachment; filename="${attachment.filename}"\r
|
|
1765
|
+
`;
|
|
1766
|
+
email += `\r
|
|
1767
|
+
`;
|
|
1768
|
+
email += `${attachment.content}\r
|
|
1769
|
+
`;
|
|
1770
|
+
}
|
|
1771
|
+
}
|
|
1772
|
+
email += `--${boundary}--\r
|
|
1773
|
+
`;
|
|
1774
|
+
return email;
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
var email = new EmailClient;
|
|
1778
|
+
|
|
1779
|
+
export { SESClient, IAMClient, EmailClient, email };
|