@develit-services/notification 0.5.1 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +62 -5
  2. package/dist/database/schema.cjs +4 -6
  3. package/dist/database/schema.d.cts +6 -2
  4. package/dist/database/schema.d.mts +6 -2
  5. package/dist/database/schema.d.ts +6 -2
  6. package/dist/database/schema.mjs +5 -3
  7. package/dist/export/worker.cjs +290 -66
  8. package/dist/export/worker.d.cts +22 -3
  9. package/dist/export/worker.d.mts +22 -3
  10. package/dist/export/worker.d.ts +22 -3
  11. package/dist/export/worker.mjs +282 -58
  12. package/dist/export/wrangler.cjs +19 -1
  13. package/dist/export/wrangler.d.cts +5 -1
  14. package/dist/export/wrangler.d.mts +5 -1
  15. package/dist/export/wrangler.d.ts +5 -1
  16. package/dist/export/wrangler.mjs +19 -1
  17. package/dist/shared/{notification.B0pktSz9.cjs → notification.BLPB8Ib2.cjs} +79 -0
  18. package/dist/shared/{notification.DWuoMDHY.d.ts → notification.BiG4Q650.d.cts} +98 -6
  19. package/dist/shared/{notification.B4ZLWDCP.d.cts → notification.BiG4Q650.d.mts} +98 -6
  20. package/dist/shared/{notification.CpFoKjoN.d.mts → notification.BiG4Q650.d.ts} +98 -6
  21. package/dist/shared/{notification.CmITLO7E.mjs → notification.CP_hFlNt.mjs} +75 -1
  22. package/dist/shared/{notification.BB9Jl8DI.d.mts → notification.CdlaOUd0.d.cts} +3 -0
  23. package/dist/shared/{notification.BB9Jl8DI.d.ts → notification.CdlaOUd0.d.mts} +3 -0
  24. package/dist/shared/{notification.BB9Jl8DI.d.cts → notification.CdlaOUd0.d.ts} +3 -0
  25. package/dist/types.cjs +19 -19
  26. package/dist/types.d.cts +5 -16
  27. package/dist/types.d.mts +5 -16
  28. package/dist/types.d.ts +5 -16
  29. package/dist/types.mjs +2 -10
  30. package/package.json +3 -3
  31. package/dist/shared/notification.4b3eUEIG.cjs +0 -22
  32. package/dist/shared/notification.BWLPh6Gb.d.cts +0 -140
  33. package/dist/shared/notification.BWLPh6Gb.d.mts +0 -140
  34. package/dist/shared/notification.BWLPh6Gb.d.ts +0 -140
  35. package/dist/shared/notification.C0X8Orrh.mjs +0 -19
package/README.md CHANGED
@@ -35,6 +35,7 @@ Bindings:
35
35
  | Email | Ecomail | Aktivni |
36
36
  | SMS | Twilio | Aktivni |
37
37
  | Slack | Slack Webhook | Aktivni |
38
+ | Webhook | WebhookConnector | Aktivni |
38
39
  | Push | - | Neimplementovano |
39
40
 
40
41
  ## Queue processing
@@ -51,10 +52,11 @@ NOTIFICATIONS_QUEUE
51
52
 
52
53
 
53
54
  Queue handler (switch dle type)
54
- ├─ email → _sendEmail()
55
- ├─ sms → _sendSms()
56
- ├─ slack → sendSlackNotification()
57
- └─ push _sendPushNotification() (501)
55
+ ├─ email → _sendEmail()
56
+ ├─ sms → _sendSms()
57
+ ├─ slack → sendSlackNotification()
58
+ ├─ webhook _sendWebhook()
59
+ └─ push → _sendPushNotification() (501)
58
60
 
59
61
  ├─ success → message.ack()
60
62
  └─ error → message.retry() (exponential backoff, base 60s)
@@ -68,7 +70,7 @@ Queue handler (switch dle type)
68
70
 
69
71
  ```typescript
70
72
  {
71
- type: 'email' | 'sms' | 'pushNotification' | 'slack'
73
+ type: 'email' | 'sms' | 'pushNotification' | 'slack' | 'webhook'
72
74
  metadata: {
73
75
  userAgent?: string
74
76
  ip?: string
@@ -79,6 +81,7 @@ Queue handler (switch dle type)
79
81
  sms?: ISms
80
82
  pushNotification?: IPushNotification
81
83
  slack?: ISlack
84
+ webhook?: IWebhook
82
85
  }
83
86
  }
84
87
  ```
@@ -103,6 +106,53 @@ Provider pro SMS pres Twilio Messaging Service.
103
106
 
104
107
  Odesilani notifikaci pres Slack Incoming Webhook. Timeout 3s.
105
108
 
109
+ ### Webhook (HTTP callback)
110
+
111
+ Odesilani podepsanych HTTP POST callbacku na URL zadanou callerem. Timeout 10s.
112
+
113
+ Kazdy webhook je automaticky podepsan RSA-PKCS1-v1_5 (SHA-256) podpisem. Privatni klic se nacita ze Secrets Store (`NOTIFICATION_SERVICE_WEBHOOK_SIGNING_KEY`). Podpis se odesila v hlavicce `X-Webhook-Signature` (base64).
114
+
115
+ #### Generovani klicu
116
+
117
+ ```bash
118
+ # Private key (base64, jedna radka) → ulozit do Secrets Store jako NOTIFICATION_SERVICE_WEBHOOK_SIGNING_KEY
119
+ openssl genrsa 4096 2>/dev/null | base64 -w0
120
+
121
+ # Public key z private key → poskytnout prijemcum webhooku v API docs
122
+ echo "<PRIVATE_KEY_BASE64>" | base64 -d | openssl rsa -pubout 2>/dev/null | base64 -w0
123
+ ```
124
+
125
+ > Na macOS pouzit `base64` bez `-w0` (macOS verze nevypisuje newlines).
126
+
127
+ #### Overeni podpisu na strane prijemce
128
+
129
+ Podpis je `RSA-PKCS1-v1_5` se `SHA-256`, odeslany v hlavicce `X-Webhook-Signature` jako base64 string. Overeni:
130
+
131
+ ```bash
132
+ # Ulozit raw body requestu do souboru
133
+ echo -n '{"type":"payment.created","data":{...}}' > body.json
134
+
135
+ # Dekodovat podpis z hlavicky
136
+ echo -n "<hodnota X-Webhook-Signature>" | base64 -d > signature.bin
137
+
138
+ # Overit
139
+ openssl dgst -sha256 -verify webhook_public.pem -signature signature.bin body.json
140
+ # Vystup: "Verified OK" nebo "Verification Failure"
141
+ ```
142
+
143
+ Nebo programove (Node.js):
144
+
145
+ ```javascript
146
+ const crypto = require('crypto')
147
+
148
+ function verifyWebhook(rawBody, signatureBase64, publicKeyBase64) {
149
+ const publicKey = Buffer.from(publicKeyBase64, 'base64').toString('utf-8')
150
+ const verifier = crypto.createVerify('RSA-SHA256')
151
+ verifier.update(rawBody)
152
+ return verifier.verify(publicKey, signatureBase64, 'base64')
153
+ }
154
+ ```
155
+
106
156
  ## RPC akce
107
157
 
108
158
  Notification service je **RPC worker** - vsechny akce dostupne pres Cloudflare Worker binding.
@@ -115,6 +165,7 @@ Notification service je **RPC worker** - vsechny akce dostupne pres Cloudflare W
115
165
  | `public-send-email-sync` | Odesle email primo (sync) |
116
166
  | `public-send-sms` | Zaradi SMS do fronty (async) |
117
167
  | `send-slack-notification` | Odesle Slack notifikaci (sync) |
168
+ | `send-webhook` | Zaradi webhook do fronty (async) |
118
169
 
119
170
  ### Interni akce
120
171
 
@@ -122,6 +173,7 @@ Notification service je **RPC worker** - vsechny akce dostupne pres Cloudflare W
122
173
  |------|-------|
123
174
  | `private-send-email` | Odesle email pres konektor + audit log |
124
175
  | `private-send-sms` | Odesle SMS pres konektor + audit log |
176
+ | `private-send-webhook` | Odesle webhook pres konektor + audit log |
125
177
  | `send-push-notification` | Neimplementovano (501) |
126
178
 
127
179
  ### Vstupy
@@ -145,6 +197,8 @@ metadata: {
145
197
 
146
198
  **Slack** podporuje: `message`.
147
199
 
200
+ **Webhook** podporuje: `url`, `payload` (libovolny JSON objekt), `headers` (volitelne custom HTTP headers).
201
+
148
202
  ## Audit logging
149
203
 
150
204
  Kazdy uspesne odeslany email a SMS se zaznamenava do audit logu.
@@ -177,6 +231,9 @@ Format: `{CATEGORY}-N-{NUMBER}`
177
231
  | `CONN-N-03` | 502 | Twilio: failed to send SMS |
178
232
  | `CONN-N-04` | 502 | Slack: failed to send notification |
179
233
  | `CONN-N-05` | 504 | Slack: request timed out |
234
+ | `CONN-N-06` | 502 | Webhook: delivery failed |
235
+ | `CONN-N-07` | 504 | Webhook: request timed out |
180
236
  | `VALID-N-01` | 404 | Unsupported email provider |
181
237
  | `VALID-N-02` | 404 | Unsupported SMS provider |
182
238
  | `SYS-N-01` | 501 | Push notifications not implemented |
239
+ | `SYS-N-02` | 500 | Webhook signing key not configured |
@@ -1,9 +1,7 @@
1
1
  'use strict';
2
2
 
3
- const database_schema = require('../shared/notification.4b3eUEIG.cjs');
4
- require('@develit-io/backend-sdk');
5
- require('drizzle-orm/sqlite-core');
3
+ const schema = {
4
+ __proto__: null
5
+ };
6
6
 
7
-
8
-
9
- exports.auditLog = database_schema.auditLog;
7
+ exports.schema = schema;
@@ -1,2 +1,6 @@
1
- export { a as auditLog } from '../shared/notification.BWLPh6Gb.cjs';
2
- import 'drizzle-orm/sqlite-core';
1
+ declare namespace schema {
2
+ export {
3
+ };
4
+ }
5
+
6
+ export { schema as s };
@@ -1,2 +1,6 @@
1
- export { a as auditLog } from '../shared/notification.BWLPh6Gb.mjs';
2
- import 'drizzle-orm/sqlite-core';
1
+ declare namespace schema {
2
+ export {
3
+ };
4
+ }
5
+
6
+ export { schema as s };
@@ -1,2 +1,6 @@
1
- export { a as auditLog } from '../shared/notification.BWLPh6Gb.js';
2
- import 'drizzle-orm/sqlite-core';
1
+ declare namespace schema {
2
+ export {
3
+ };
4
+ }
5
+
6
+ export { schema as s };
@@ -1,3 +1,5 @@
1
- export { a as auditLog } from '../shared/notification.C0X8Orrh.mjs';
2
- import '@develit-io/backend-sdk';
3
- import 'drizzle-orm/sqlite-core';
1
+ const schema = {
2
+ __proto__: null
3
+ };
4
+
5
+ export { schema as s };
@@ -3,10 +3,11 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  const backendSdk = require('@develit-io/backend-sdk');
6
- const database_schema = require('../shared/notification.4b3eUEIG.cjs');
6
+ const database_schema = require('../database/schema.cjs');
7
7
  require('drizzle-orm');
8
8
  require('drizzle-orm/sqlite-core');
9
- const slack_connector = require('../shared/notification.B0pktSz9.cjs');
9
+ const webhook_connector = require('../shared/notification.BLPB8Ib2.cjs');
10
+ require('@develit-io/backend-sdk/signature');
10
11
  const cloudflare_workers = require('cloudflare:workers');
11
12
  const d1 = require('drizzle-orm/d1');
12
13
  require('zod');
@@ -16,7 +17,7 @@ require('twilio');
16
17
  const tables = database_schema.schema;
17
18
 
18
19
  const initiateEmailConnector = async (provider, apiKey, smtpHost, senderEmail, senderName) => {
19
- const connector = [slack_connector.EcomailConnector].find(
20
+ const connector = [webhook_connector.EcomailConnector].find(
20
21
  (conn) => conn.providerName === provider
21
22
  );
22
23
  if (!connector)
@@ -33,34 +34,12 @@ const initiateEmailConnector = async (provider, apiKey, smtpHost, senderEmail, s
33
34
  });
34
35
  };
35
36
 
36
- const createAuditLogCommand = async ({
37
- db,
38
- auditLog: {
39
- event,
40
- description,
41
- initiatorService,
42
- initiatorUserId,
43
- ip,
44
- userAgent
45
- }
46
- }) => {
47
- const command = db.insert(tables.auditLog).values({
48
- id: backendSdk.uuidv4(),
49
- createdAt: /* @__PURE__ */ new Date(),
50
- event,
51
- description,
52
- ip,
53
- userAgent,
54
- initiatorService,
55
- initiatorUserId
56
- });
57
- return {
58
- command
59
- };
60
- };
37
+ function generateEmailKVPattern(recipient) {
38
+ return `email:${recipient}:`;
39
+ }
61
40
 
62
41
  const initiateSmsConnector = async (provider, accountId, authToken, serviceId) => {
63
- const connector = [slack_connector.TwilioConnector].find(
42
+ const connector = [webhook_connector.TwilioConnector].find(
64
43
  (conn) => conn.providerName === provider
65
44
  );
66
45
  if (!connector)
@@ -91,7 +70,7 @@ let NotificationServiceBase = class extends backendSdk.develitWorker(
91
70
  ) {
92
71
  constructor(ctx, env) {
93
72
  super(ctx, env);
94
- this.slackConnector = new slack_connector.SlackConnector(this.env.SLACK_WEBHOOK);
73
+ this.slackConnector = new webhook_connector.SlackConnector(this.env.SLACK_WEBHOOK);
95
74
  this.db = d1.drizzle(this.env.NOTIFICATION_D1, { schema: tables });
96
75
  }
97
76
  async queue(batch) {
@@ -121,6 +100,12 @@ let NotificationServiceBase = class extends backendSdk.develitWorker(
121
100
  metadata
122
101
  });
123
102
  break;
103
+ case "webhook":
104
+ notificationAction = async () => this._sendWebhook({
105
+ webhook: payload.webhook,
106
+ metadata
107
+ });
108
+ break;
124
109
  default:
125
110
  this.logError({ error: `Unknown notification type: ${type}` });
126
111
  message.retry();
@@ -137,7 +122,7 @@ let NotificationServiceBase = class extends backendSdk.develitWorker(
137
122
  }
138
123
  async _sendEmail(input) {
139
124
  return this.handleAction(
140
- { data: input, schema: slack_connector.sendEmailInputSchema },
125
+ { data: input, schema: webhook_connector.sendEmailInputSchema },
141
126
  { successMessage: "Email sent." },
142
127
  async (params) => {
143
128
  const {
@@ -145,7 +130,7 @@ let NotificationServiceBase = class extends backendSdk.develitWorker(
145
130
  metadata: {
146
131
  ip,
147
132
  userAgent,
148
- initiator: { service: service2, userId }
133
+ initiator: { userId }
149
134
  }
150
135
  } = params;
151
136
  if (!this.emailConnector) {
@@ -160,26 +145,35 @@ let NotificationServiceBase = class extends backendSdk.develitWorker(
160
145
  );
161
146
  }
162
147
  await this.emailConnector.sendEmail(email);
163
- const { command } = await createAuditLogCommand({
164
- db: this.db,
165
- auditLog: {
166
- id: backendSdk.uuidv4(),
167
- event: "EMAIL",
168
- ip,
169
- initiatorService: service2,
170
- initiatorUserId: userId,
148
+ try {
149
+ await this.pushToQueue(this.env.AUDIT_LOGS_QUEUE, {
150
+ logType: "event",
151
+ service: "notification",
152
+ correlationId: backendSdk.uuidv4(),
153
+ eventType: "notification.email_sent",
154
+ actorUserId: userId,
155
+ actorEmail: userId || "SYSTEM",
156
+ targetType: "email",
157
+ targetId: email.to,
158
+ sourceIp: ip,
171
159
  userAgent,
172
- description: JSON.stringify(input)
173
- }
174
- });
175
- await this.db.batch([command]);
160
+ outcome: "success",
161
+ description: `Email sent to ${email.to}`,
162
+ details: JSON.stringify({
163
+ subject: email.subject,
164
+ templateId: email.templateId
165
+ })
166
+ });
167
+ } catch (e) {
168
+ console.error("Failed to push email audit log", e);
169
+ }
176
170
  return {};
177
171
  }
178
172
  );
179
173
  }
180
174
  async _sendSms(input) {
181
175
  return this.handleAction(
182
- { data: input, schema: slack_connector.sendSmsInputSchema },
176
+ { data: input, schema: webhook_connector.sendSmsInputSchema },
183
177
  { successMessage: "Sms sent." },
184
178
  async (params) => {
185
179
  const {
@@ -187,7 +181,7 @@ let NotificationServiceBase = class extends backendSdk.develitWorker(
187
181
  metadata: {
188
182
  ip,
189
183
  userAgent,
190
- initiator: { service: service2, userId }
184
+ initiator: { userId }
191
185
  }
192
186
  } = params;
193
187
  if (!this.smsConnector) {
@@ -205,26 +199,32 @@ let NotificationServiceBase = class extends backendSdk.develitWorker(
205
199
  );
206
200
  }
207
201
  await this.smsConnector.sendSms({ message, to });
208
- const { command } = await createAuditLogCommand({
209
- db: this.db,
210
- auditLog: {
211
- id: backendSdk.uuidv4(),
212
- event: "SMS",
213
- ip,
202
+ try {
203
+ await this.pushToQueue(this.env.AUDIT_LOGS_QUEUE, {
204
+ logType: "event",
205
+ service: "notification",
206
+ correlationId: backendSdk.uuidv4(),
207
+ eventType: "notification.sms_sent",
208
+ actorUserId: userId,
209
+ actorEmail: userId || "SYSTEM",
210
+ targetType: "sms",
211
+ targetId: to,
212
+ sourceIp: ip,
214
213
  userAgent,
215
- initiatorService: service2,
216
- initiatorUserId: userId,
217
- description: JSON.stringify(input)
218
- }
219
- });
220
- await this.db.batch([command]);
214
+ outcome: "success",
215
+ description: `SMS sent to ${to}`,
216
+ details: JSON.stringify({ message })
217
+ });
218
+ } catch (e) {
219
+ console.error("Failed to push sms audit log", e);
220
+ }
221
221
  return {};
222
222
  }
223
223
  );
224
224
  }
225
225
  async sendEmail(input) {
226
226
  return this.handleAction(
227
- { data: input, schema: slack_connector.sendEmailInputSchema },
227
+ { data: input, schema: webhook_connector.sendEmailInputSchema },
228
228
  { successMessage: "Email sent." },
229
229
  async (params) => {
230
230
  const { email, metadata } = params;
@@ -244,7 +244,7 @@ let NotificationServiceBase = class extends backendSdk.develitWorker(
244
244
  }
245
245
  async sendEmailSync(input) {
246
246
  return this.handleAction(
247
- { data: input, schema: slack_connector.sendEmailInputSchema },
247
+ { data: input, schema: webhook_connector.sendEmailInputSchema },
248
248
  { successMessage: "Email sent." },
249
249
  async (params) => {
250
250
  const { email, metadata } = params;
@@ -257,7 +257,7 @@ let NotificationServiceBase = class extends backendSdk.develitWorker(
257
257
  }
258
258
  async sendSms(input) {
259
259
  return this.handleAction(
260
- { data: input, schema: slack_connector.sendSmsInputSchema },
260
+ { data: input, schema: webhook_connector.sendSmsInputSchema },
261
261
  { successMessage: "SMS sent." },
262
262
  async (params) => {
263
263
  const {
@@ -294,16 +294,225 @@ let NotificationServiceBase = class extends backendSdk.develitWorker(
294
294
  return this.handleAction(
295
295
  {
296
296
  data: input,
297
- schema: slack_connector.sendSlackInputSchema
297
+ schema: webhook_connector.sendSlackInputSchema
298
298
  },
299
299
  { successMessage: "Slack sent." },
300
300
  async (params) => {
301
- const { slack } = params;
301
+ const { slack, metadata } = params;
302
302
  await this.slackConnector.sendNotificationToAllSlack(slack.message);
303
+ await this.pushToQueue(this.env.AUDIT_LOGS_QUEUE, {
304
+ logType: "event",
305
+ service: "notification",
306
+ correlationId: backendSdk.uuidv4(),
307
+ eventType: "notification.slack_sent",
308
+ actorUserId: metadata?.initiator?.userId,
309
+ actorEmail: metadata?.initiator?.userId || "SYSTEM",
310
+ targetType: "slack",
311
+ targetId: "all-channels",
312
+ sourceIp: metadata?.ip,
313
+ userAgent: metadata?.userAgent,
314
+ outcome: "success",
315
+ description: "Slack notification sent",
316
+ details: JSON.stringify({
317
+ message: slack.message
318
+ })
319
+ });
320
+ return {};
321
+ }
322
+ );
323
+ }
324
+ async sendWebhook(input) {
325
+ return this.handleAction(
326
+ { data: input, schema: webhook_connector.sendWebhookInputSchema },
327
+ { successMessage: "Webhook queued." },
328
+ async (params) => {
329
+ const { webhook, metadata } = params;
330
+ await this.pushToQueue(
331
+ this.env.NOTIFICATIONS_QUEUE,
332
+ {
333
+ type: "webhook",
334
+ payload: {
335
+ webhook
336
+ },
337
+ metadata
338
+ }
339
+ );
340
+ return {};
341
+ }
342
+ );
343
+ }
344
+ async _sendWebhook(input) {
345
+ return this.handleAction(
346
+ { data: input, schema: webhook_connector.sendWebhookInputSchema },
347
+ { successMessage: "Webhook sent." },
348
+ async (params) => {
349
+ const {
350
+ webhook,
351
+ metadata: {
352
+ ip,
353
+ userAgent,
354
+ initiator: { userId }
355
+ }
356
+ } = params;
357
+ if (!this.webhookConnector) {
358
+ const privateKey = (await this.env.SECRETS_STORE.get({
359
+ secretName: "NOTIFICATION_SERVICE_WEBHOOK_SIGNING_KEY"
360
+ })).data?.secretValue;
361
+ if (!privateKey) {
362
+ throw backendSdk.createInternalError(null, {
363
+ message: "NOTIFICATION_SERVICE_WEBHOOK_SIGNING_KEY is not configured",
364
+ code: "SYS-N-02",
365
+ status: 500
366
+ });
367
+ }
368
+ this.webhookConnector = new webhook_connector.WebhookConnector(privateKey);
369
+ }
370
+ await this.webhookConnector.sendWebhook(webhook);
371
+ try {
372
+ await this.pushToQueue(this.env.AUDIT_LOGS_QUEUE, {
373
+ logType: "event",
374
+ service: "notification",
375
+ correlationId: backendSdk.uuidv4(),
376
+ eventType: "notification.webhook_sent",
377
+ actorUserId: userId,
378
+ actorEmail: userId || "SYSTEM",
379
+ targetType: "webhook",
380
+ targetId: webhook.url,
381
+ sourceIp: ip,
382
+ userAgent,
383
+ outcome: "success",
384
+ description: `Webhook sent to ${webhook.url}`,
385
+ details: JSON.stringify({
386
+ url: webhook.url
387
+ })
388
+ });
389
+ } catch (e) {
390
+ console.error("Failed to push webhook audit log", e);
391
+ }
303
392
  return {};
304
393
  }
305
394
  );
306
395
  }
396
+ async getReceivedEmails(input) {
397
+ return this.handleAction(
398
+ {
399
+ data: input,
400
+ schema: webhook_connector.getReceivedEmailsInputSchema
401
+ },
402
+ { successMessage: "Emails retrieved." },
403
+ async (params) => {
404
+ const { recipient, limit, includeContent } = params;
405
+ if (!this.env.RECEIVED_EMAILS_KV) {
406
+ throw new Error("RECEIVED_EMAILS_KV binding is not configured");
407
+ }
408
+ const pattern = generateEmailKVPattern(recipient);
409
+ const list = await this.env.RECEIVED_EMAILS_KV.list({
410
+ prefix: pattern,
411
+ limit
412
+ });
413
+ const emails = [];
414
+ for (const key of list.keys) {
415
+ const emailData = await this.env.RECEIVED_EMAILS_KV.get(
416
+ key.name,
417
+ "json"
418
+ );
419
+ if (emailData) {
420
+ const email = emailData;
421
+ if (!includeContent) {
422
+ delete email.text;
423
+ delete email.html;
424
+ delete email.raw;
425
+ }
426
+ emails.push(email);
427
+ }
428
+ }
429
+ emails.sort(
430
+ (a, b) => new Date(b.receivedAt).getTime() - new Date(a.receivedAt).getTime()
431
+ );
432
+ return {
433
+ emails,
434
+ count: emails.length
435
+ };
436
+ }
437
+ );
438
+ }
439
+ async waitForEmail(input) {
440
+ return this.handleAction(
441
+ {
442
+ data: input,
443
+ schema: webhook_connector.waitForEmailInputSchema
444
+ },
445
+ { successMessage: "Email check completed." },
446
+ async (params) => {
447
+ const { recipient, subject, timeout, pollInterval } = params;
448
+ if (!this.env.RECEIVED_EMAILS_KV) {
449
+ throw new Error("RECEIVED_EMAILS_KV binding is not configured");
450
+ }
451
+ const maxTimeout = Math.min(timeout || 5e3, 5e3);
452
+ const safeInterval = Math.max(pollInterval || 500, 100);
453
+ const startTime = Date.now();
454
+ const maxIterations = Math.ceil(maxTimeout / safeInterval);
455
+ let iterations = 0;
456
+ while (Date.now() - startTime < maxTimeout && iterations < maxIterations) {
457
+ const pattern = generateEmailKVPattern(recipient);
458
+ const list = await this.env.RECEIVED_EMAILS_KV.list({
459
+ prefix: pattern,
460
+ limit: 50
461
+ });
462
+ for (const key of list.keys) {
463
+ const emailData = await this.env.RECEIVED_EMAILS_KV.get(
464
+ key.name,
465
+ "json"
466
+ );
467
+ if (emailData) {
468
+ const email = emailData;
469
+ if (!subject || email.subject.includes(subject)) {
470
+ return {
471
+ email,
472
+ found: true
473
+ };
474
+ }
475
+ }
476
+ }
477
+ iterations++;
478
+ if (iterations < maxIterations && Date.now() - startTime < maxTimeout - safeInterval) {
479
+ await new Promise((resolve) => setTimeout(resolve, safeInterval));
480
+ }
481
+ }
482
+ return {
483
+ email: null,
484
+ found: false
485
+ };
486
+ }
487
+ );
488
+ }
489
+ async clearReceivedEmails(input) {
490
+ return this.handleAction(
491
+ {
492
+ data: input,
493
+ schema: webhook_connector.clearReceivedEmailsInputSchema
494
+ },
495
+ { successMessage: "Emails cleared." },
496
+ async (params) => {
497
+ const { recipient } = params;
498
+ if (!this.env.RECEIVED_EMAILS_KV) {
499
+ throw new Error("RECEIVED_EMAILS_KV binding is not configured");
500
+ }
501
+ const pattern = generateEmailKVPattern(recipient);
502
+ const list = await this.env.RECEIVED_EMAILS_KV.list({
503
+ prefix: pattern
504
+ });
505
+ let deleted = 0;
506
+ for (const key of list.keys) {
507
+ await this.env.RECEIVED_EMAILS_KV.delete(key.name);
508
+ deleted++;
509
+ }
510
+ return {
511
+ deleted
512
+ };
513
+ }
514
+ );
515
+ }
307
516
  };
308
517
  __decorateClass([
309
518
  backendSdk.cloudflareQueue({ baseDelay: 60 })
@@ -315,13 +524,13 @@ __decorateClass([
315
524
  backendSdk.action("private-send-sms")
316
525
  ], NotificationServiceBase.prototype, "_sendSms", 1);
317
526
  __decorateClass([
318
- backendSdk.action("public-send-email")
527
+ backendSdk.action("send-email")
319
528
  ], NotificationServiceBase.prototype, "sendEmail", 1);
320
529
  __decorateClass([
321
- backendSdk.action("public-send-email-sync")
530
+ backendSdk.action("send-email-sync")
322
531
  ], NotificationServiceBase.prototype, "sendEmailSync", 1);
323
532
  __decorateClass([
324
- backendSdk.action("public-send-sms")
533
+ backendSdk.action("send-sms")
325
534
  ], NotificationServiceBase.prototype, "sendSms", 1);
326
535
  __decorateClass([
327
536
  backendSdk.action("send-push-notification")
@@ -329,6 +538,21 @@ __decorateClass([
329
538
  __decorateClass([
330
539
  backendSdk.action("send-slack-notification")
331
540
  ], NotificationServiceBase.prototype, "sendSlackNotification", 1);
541
+ __decorateClass([
542
+ backendSdk.action("send-webhook")
543
+ ], NotificationServiceBase.prototype, "sendWebhook", 1);
544
+ __decorateClass([
545
+ backendSdk.action("private-send-webhook")
546
+ ], NotificationServiceBase.prototype, "_sendWebhook", 1);
547
+ __decorateClass([
548
+ backendSdk.action("get-received-emails")
549
+ ], NotificationServiceBase.prototype, "getReceivedEmails", 1);
550
+ __decorateClass([
551
+ backendSdk.action("wait-for-email")
552
+ ], NotificationServiceBase.prototype, "waitForEmail", 1);
553
+ __decorateClass([
554
+ backendSdk.action("clear-received-emails")
555
+ ], NotificationServiceBase.prototype, "clearReceivedEmails", 1);
332
556
  NotificationServiceBase = __decorateClass([
333
557
  backendSdk.service("notification")
334
558
  ], NotificationServiceBase);