@develit-services/notification 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +239 -0
  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 +270 -69
  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 +262 -61
  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.Dk_5TX9v.cjs → notification.BLPB8Ib2.cjs} +109 -11
  18. package/dist/shared/{notification.2rSOcFrr.d.cts → notification.BiG4Q650.d.cts} +99 -8
  19. package/dist/shared/{notification.C1quYqlT.d.mts → notification.BiG4Q650.d.mts} +99 -8
  20. package/dist/shared/{notification.oEdhCsu_.d.ts → notification.BiG4Q650.d.ts} +99 -8
  21. package/dist/shared/{notification.CM_WoR0y.mjs → notification.CP_hFlNt.mjs} +105 -12
  22. package/dist/shared/{notification.BB9Jl8DI.d.ts → notification.CdlaOUd0.d.cts} +3 -0
  23. package/dist/shared/{notification.BB9Jl8DI.d.cts → notification.CdlaOUd0.d.mts} +3 -0
  24. package/dist/shared/{notification.BB9Jl8DI.d.mts → notification.CdlaOUd0.d.ts} +3 -0
  25. package/dist/types.cjs +19 -19
  26. package/dist/types.d.cts +6 -18
  27. package/dist/types.d.mts +6 -18
  28. package/dist/types.d.ts +6 -18
  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
@@ -1,8 +1,9 @@
1
- import { createInternalError, uuidv4, develitWorker, cloudflareQueue, action, service } from '@develit-io/backend-sdk';
2
- import { s as schema } from '../shared/notification.C0X8Orrh.mjs';
1
+ import { createInternalError, develitWorker, uuidv4, cloudflareQueue, action, service } from '@develit-io/backend-sdk';
2
+ import { s as schema } from '../database/schema.mjs';
3
3
  import 'drizzle-orm';
4
4
  import 'drizzle-orm/sqlite-core';
5
- import { E as EcomailConnector, T as TwilioConnector, S as SlackConnector, s as sendEmailInputSchema, a as sendSmsInputSchema, b as sendSlackInputSchema } from '../shared/notification.CM_WoR0y.mjs';
5
+ import { E as EcomailConnector, T as TwilioConnector, S as SlackConnector, s as sendEmailInputSchema, f as sendSmsInputSchema, e as sendSlackInputSchema, h as sendWebhookInputSchema, W as WebhookConnector, g as getReceivedEmailsInputSchema, w as waitForEmailInputSchema, c as clearReceivedEmailsInputSchema } from '../shared/notification.CP_hFlNt.mjs';
6
+ import '@develit-io/backend-sdk/signature';
6
7
  import { WorkerEntrypoint } from 'cloudflare:workers';
7
8
  import { drizzle } from 'drizzle-orm/d1';
8
9
  import 'zod';
@@ -18,6 +19,7 @@ const initiateEmailConnector = async (provider, apiKey, smtpHost, senderEmail, s
18
19
  if (!connector)
19
20
  throw createInternalError(null, {
20
21
  message: "Unsupported email provider",
22
+ code: "VALID-N-01",
21
23
  status: 404
22
24
  });
23
25
  return new connector({
@@ -28,31 +30,9 @@ const initiateEmailConnector = async (provider, apiKey, smtpHost, senderEmail, s
28
30
  });
29
31
  };
30
32
 
31
- const createAuditLogCommand = async ({
32
- db,
33
- auditLog: {
34
- event,
35
- description,
36
- initiatorService,
37
- initiatorUserId,
38
- ip,
39
- userAgent
40
- }
41
- }) => {
42
- const command = db.insert(tables.auditLog).values({
43
- id: uuidv4(),
44
- createdAt: /* @__PURE__ */ new Date(),
45
- event,
46
- description,
47
- ip,
48
- userAgent,
49
- initiatorService,
50
- initiatorUserId
51
- });
52
- return {
53
- command
54
- };
55
- };
33
+ function generateEmailKVPattern(recipient) {
34
+ return `email:${recipient}:`;
35
+ }
56
36
 
57
37
  const initiateSmsConnector = async (provider, accountId, authToken, serviceId) => {
58
38
  const connector = [TwilioConnector].find(
@@ -60,7 +40,8 @@ const initiateSmsConnector = async (provider, accountId, authToken, serviceId) =
60
40
  );
61
41
  if (!connector)
62
42
  throw createInternalError(null, {
63
- message: "Unsupported sms provider",
43
+ message: "Unsupported SMS provider",
44
+ code: "VALID-N-02",
64
45
  status: 404
65
46
  });
66
47
  return new connector({
@@ -115,6 +96,12 @@ let NotificationServiceBase = class extends develitWorker(
115
96
  metadata
116
97
  });
117
98
  break;
99
+ case "webhook":
100
+ notificationAction = async () => this._sendWebhook({
101
+ webhook: payload.webhook,
102
+ metadata
103
+ });
104
+ break;
118
105
  default:
119
106
  this.logError({ error: `Unknown notification type: ${type}` });
120
107
  message.retry();
@@ -139,7 +126,7 @@ let NotificationServiceBase = class extends develitWorker(
139
126
  metadata: {
140
127
  ip,
141
128
  userAgent,
142
- initiator: { service: service2, userId }
129
+ initiator: { userId }
143
130
  }
144
131
  } = params;
145
132
  if (!this.emailConnector) {
@@ -154,19 +141,24 @@ let NotificationServiceBase = class extends develitWorker(
154
141
  );
155
142
  }
156
143
  await this.emailConnector.sendEmail(email);
157
- const { command } = await createAuditLogCommand({
158
- db: this.db,
159
- auditLog: {
160
- id: uuidv4(),
161
- event: "EMAIL",
162
- ip,
163
- initiatorService: service2,
164
- initiatorUserId: userId,
165
- userAgent,
166
- description: JSON.stringify(input)
167
- }
144
+ await this.pushToQueue(this.env.AUDIT_LOGS_QUEUE, {
145
+ logType: "event",
146
+ service: "notification",
147
+ correlationId: uuidv4(),
148
+ eventType: "notification.email_sent",
149
+ actorUserId: userId,
150
+ actorEmail: userId || "SYSTEM",
151
+ targetType: "email",
152
+ targetId: email.to,
153
+ sourceIp: ip,
154
+ userAgent,
155
+ outcome: "success",
156
+ description: `Email sent to ${email.to}`,
157
+ details: JSON.stringify({
158
+ subject: email.subject,
159
+ templateId: email.templateId
160
+ })
168
161
  });
169
- await this.db.batch([command]);
170
162
  return {};
171
163
  }
172
164
  );
@@ -181,7 +173,7 @@ let NotificationServiceBase = class extends develitWorker(
181
173
  metadata: {
182
174
  ip,
183
175
  userAgent,
184
- initiator: { service: service2, userId }
176
+ initiator: { userId }
185
177
  }
186
178
  } = params;
187
179
  if (!this.smsConnector) {
@@ -199,19 +191,21 @@ let NotificationServiceBase = class extends develitWorker(
199
191
  );
200
192
  }
201
193
  await this.smsConnector.sendSms({ message, to });
202
- const { command } = await createAuditLogCommand({
203
- db: this.db,
204
- auditLog: {
205
- id: uuidv4(),
206
- event: "SMS",
207
- ip,
208
- userAgent,
209
- initiatorService: service2,
210
- initiatorUserId: userId,
211
- description: JSON.stringify(input)
212
- }
194
+ await this.pushToQueue(this.env.AUDIT_LOGS_QUEUE, {
195
+ logType: "event",
196
+ service: "notification",
197
+ correlationId: uuidv4(),
198
+ eventType: "notification.sms_sent",
199
+ actorUserId: userId,
200
+ actorEmail: userId || "SYSTEM",
201
+ targetType: "sms",
202
+ targetId: to,
203
+ sourceIp: ip,
204
+ userAgent,
205
+ outcome: "success",
206
+ description: `SMS sent to ${to}`,
207
+ details: JSON.stringify({ message })
213
208
  });
214
- await this.db.batch([command]);
215
209
  return {};
216
210
  }
217
211
  );
@@ -276,9 +270,13 @@ let NotificationServiceBase = class extends develitWorker(
276
270
  );
277
271
  }
278
272
  async _sendPushNotification() {
279
- this.logInput({});
280
- this.logError({ error: "Method not implemented." });
281
- throw new Error("Method not implemented.");
273
+ return this.handleAction(null, { successMessage: "" }, async () => {
274
+ throw createInternalError(null, {
275
+ message: "Push notifications not implemented",
276
+ code: "SYS-N-01",
277
+ status: 501
278
+ });
279
+ });
282
280
  }
283
281
  async sendSlackNotification(input) {
284
282
  return this.handleAction(
@@ -294,6 +292,194 @@ let NotificationServiceBase = class extends develitWorker(
294
292
  }
295
293
  );
296
294
  }
295
+ async sendWebhook(input) {
296
+ return this.handleAction(
297
+ { data: input, schema: sendWebhookInputSchema },
298
+ { successMessage: "Webhook queued." },
299
+ async (params) => {
300
+ const { webhook, metadata } = params;
301
+ await this.pushToQueue(
302
+ this.env.NOTIFICATIONS_QUEUE,
303
+ {
304
+ type: "webhook",
305
+ payload: {
306
+ webhook
307
+ },
308
+ metadata
309
+ }
310
+ );
311
+ return {};
312
+ }
313
+ );
314
+ }
315
+ async _sendWebhook(input) {
316
+ return this.handleAction(
317
+ { data: input, schema: sendWebhookInputSchema },
318
+ { successMessage: "Webhook sent." },
319
+ async (params) => {
320
+ const {
321
+ webhook,
322
+ metadata: {
323
+ ip,
324
+ userAgent,
325
+ initiator: { userId }
326
+ }
327
+ } = params;
328
+ if (!this.webhookConnector) {
329
+ const privateKey = (await this.env.SECRETS_STORE.get({
330
+ secretName: "NOTIFICATION_SERVICE_WEBHOOK_SIGNING_KEY"
331
+ })).data?.secretValue;
332
+ if (!privateKey) {
333
+ throw createInternalError(null, {
334
+ message: "NOTIFICATION_SERVICE_WEBHOOK_SIGNING_KEY is not configured",
335
+ code: "SYS-N-02",
336
+ status: 500
337
+ });
338
+ }
339
+ this.webhookConnector = new WebhookConnector(privateKey);
340
+ }
341
+ await this.webhookConnector.sendWebhook(webhook);
342
+ await this.pushToQueue(this.env.AUDIT_LOGS_QUEUE, {
343
+ logType: "event",
344
+ service: "notification",
345
+ correlationId: uuidv4(),
346
+ eventType: "notification.webhook_sent",
347
+ actorUserId: userId,
348
+ actorEmail: userId || "SYSTEM",
349
+ targetType: "webhook",
350
+ targetId: webhook.url,
351
+ sourceIp: ip,
352
+ userAgent,
353
+ outcome: "success",
354
+ description: `Webhook sent to ${webhook.url}`,
355
+ details: JSON.stringify({
356
+ url: webhook.url
357
+ })
358
+ });
359
+ return {};
360
+ }
361
+ );
362
+ }
363
+ async getReceivedEmails(input) {
364
+ return this.handleAction(
365
+ {
366
+ data: input,
367
+ schema: getReceivedEmailsInputSchema
368
+ },
369
+ { successMessage: "Emails retrieved." },
370
+ async (params) => {
371
+ const { recipient, limit, includeContent } = params;
372
+ if (!this.env.RECEIVED_EMAILS_KV) {
373
+ throw new Error("RECEIVED_EMAILS_KV binding is not configured");
374
+ }
375
+ const pattern = generateEmailKVPattern(recipient);
376
+ const list = await this.env.RECEIVED_EMAILS_KV.list({
377
+ prefix: pattern,
378
+ limit
379
+ });
380
+ const emails = [];
381
+ for (const key of list.keys) {
382
+ const emailData = await this.env.RECEIVED_EMAILS_KV.get(
383
+ key.name,
384
+ "json"
385
+ );
386
+ if (emailData) {
387
+ const email = emailData;
388
+ if (!includeContent) {
389
+ delete email.text;
390
+ delete email.html;
391
+ delete email.raw;
392
+ }
393
+ emails.push(email);
394
+ }
395
+ }
396
+ emails.sort(
397
+ (a, b) => new Date(b.receivedAt).getTime() - new Date(a.receivedAt).getTime()
398
+ );
399
+ return {
400
+ emails,
401
+ count: emails.length
402
+ };
403
+ }
404
+ );
405
+ }
406
+ async waitForEmail(input) {
407
+ return this.handleAction(
408
+ {
409
+ data: input,
410
+ schema: waitForEmailInputSchema
411
+ },
412
+ { successMessage: "Email check completed." },
413
+ async (params) => {
414
+ const { recipient, subject, timeout, pollInterval } = params;
415
+ if (!this.env.RECEIVED_EMAILS_KV) {
416
+ throw new Error("RECEIVED_EMAILS_KV binding is not configured");
417
+ }
418
+ const maxTimeout = Math.min(timeout || 5e3, 5e3);
419
+ const safeInterval = Math.max(pollInterval || 500, 100);
420
+ const startTime = Date.now();
421
+ const maxIterations = Math.ceil(maxTimeout / safeInterval);
422
+ let iterations = 0;
423
+ while (Date.now() - startTime < maxTimeout && iterations < maxIterations) {
424
+ const pattern = generateEmailKVPattern(recipient);
425
+ const list = await this.env.RECEIVED_EMAILS_KV.list({
426
+ prefix: pattern,
427
+ limit: 50
428
+ });
429
+ for (const key of list.keys) {
430
+ const emailData = await this.env.RECEIVED_EMAILS_KV.get(
431
+ key.name,
432
+ "json"
433
+ );
434
+ if (emailData) {
435
+ const email = emailData;
436
+ if (!subject || email.subject.includes(subject)) {
437
+ return {
438
+ email,
439
+ found: true
440
+ };
441
+ }
442
+ }
443
+ }
444
+ iterations++;
445
+ if (iterations < maxIterations && Date.now() - startTime < maxTimeout - safeInterval) {
446
+ await new Promise((resolve) => setTimeout(resolve, safeInterval));
447
+ }
448
+ }
449
+ return {
450
+ email: null,
451
+ found: false
452
+ };
453
+ }
454
+ );
455
+ }
456
+ async clearReceivedEmails(input) {
457
+ return this.handleAction(
458
+ {
459
+ data: input,
460
+ schema: clearReceivedEmailsInputSchema
461
+ },
462
+ { successMessage: "Emails cleared." },
463
+ async (params) => {
464
+ const { recipient } = params;
465
+ if (!this.env.RECEIVED_EMAILS_KV) {
466
+ throw new Error("RECEIVED_EMAILS_KV binding is not configured");
467
+ }
468
+ const pattern = generateEmailKVPattern(recipient);
469
+ const list = await this.env.RECEIVED_EMAILS_KV.list({
470
+ prefix: pattern
471
+ });
472
+ let deleted = 0;
473
+ for (const key of list.keys) {
474
+ await this.env.RECEIVED_EMAILS_KV.delete(key.name);
475
+ deleted++;
476
+ }
477
+ return {
478
+ deleted
479
+ };
480
+ }
481
+ );
482
+ }
297
483
  };
298
484
  __decorateClass([
299
485
  cloudflareQueue({ baseDelay: 60 })
@@ -305,13 +491,13 @@ __decorateClass([
305
491
  action("private-send-sms")
306
492
  ], NotificationServiceBase.prototype, "_sendSms", 1);
307
493
  __decorateClass([
308
- action("public-send-email")
494
+ action("send-email")
309
495
  ], NotificationServiceBase.prototype, "sendEmail", 1);
310
496
  __decorateClass([
311
- action("public-send-email-sync")
497
+ action("send-email-sync")
312
498
  ], NotificationServiceBase.prototype, "sendEmailSync", 1);
313
499
  __decorateClass([
314
- action("public-send-sms")
500
+ action("send-sms")
315
501
  ], NotificationServiceBase.prototype, "sendSms", 1);
316
502
  __decorateClass([
317
503
  action("send-push-notification")
@@ -319,6 +505,21 @@ __decorateClass([
319
505
  __decorateClass([
320
506
  action("send-slack-notification")
321
507
  ], NotificationServiceBase.prototype, "sendSlackNotification", 1);
508
+ __decorateClass([
509
+ action("send-webhook")
510
+ ], NotificationServiceBase.prototype, "sendWebhook", 1);
511
+ __decorateClass([
512
+ action("private-send-webhook")
513
+ ], NotificationServiceBase.prototype, "_sendWebhook", 1);
514
+ __decorateClass([
515
+ action("get-received-emails")
516
+ ], NotificationServiceBase.prototype, "getReceivedEmails", 1);
517
+ __decorateClass([
518
+ action("wait-for-email")
519
+ ], NotificationServiceBase.prototype, "waitForEmail", 1);
520
+ __decorateClass([
521
+ action("clear-received-emails")
522
+ ], NotificationServiceBase.prototype, "clearReceivedEmails", 1);
322
523
  NotificationServiceBase = __decorateClass([
323
524
  service("notification")
324
525
  ], NotificationServiceBase);
@@ -27,11 +27,21 @@ function defineNotificationServiceWrangler(config) {
27
27
  migrations_dir: "./src/database/migrations"
28
28
  }
29
29
  ],
30
+ kv_namespaces: envs.local.kv ? [
31
+ {
32
+ binding: "RECEIVED_EMAILS_KV",
33
+ id: envs.local.kv.id
34
+ }
35
+ ] : [],
30
36
  queues: {
31
37
  producers: [
32
38
  {
33
39
  binding: "NOTIFICATIONS_QUEUE",
34
40
  queue: `${project}-notifications`
41
+ },
42
+ {
43
+ binding: "AUDIT_LOGS_QUEUE",
44
+ queue: `${project}-audit-logs`
35
45
  }
36
46
  ],
37
47
  consumers: [
@@ -60,7 +70,15 @@ function defineNotificationServiceWrangler(config) {
60
70
  database_id: envCfg.d1.id,
61
71
  migrations_dir: "./src/database/migrations"
62
72
  }
63
- ]
73
+ ],
74
+ ...envCfg.kv && {
75
+ kv_namespaces: [
76
+ {
77
+ binding: "RECEIVED_EMAILS_KV",
78
+ id: envCfg.kv.id
79
+ }
80
+ ]
81
+ }
64
82
  };
65
83
  }
66
84
  return base;
@@ -1,4 +1,4 @@
1
- import { N as NotificationServiceWranglerConfig } from '../shared/notification.BB9Jl8DI.cjs';
1
+ import { N as NotificationServiceWranglerConfig } from '../shared/notification.CdlaOUd0.cjs';
2
2
 
3
3
  declare function defineNotificationServiceWrangler(config: NotificationServiceWranglerConfig): {
4
4
  vars: {
@@ -20,6 +20,10 @@ declare function defineNotificationServiceWrangler(config: NotificationServiceWr
20
20
  database_id: string;
21
21
  migrations_dir: string;
22
22
  }[];
23
+ kv_namespaces: {
24
+ binding: string;
25
+ id: string;
26
+ }[];
23
27
  queues: {
24
28
  producers: {
25
29
  binding: string;
@@ -1,4 +1,4 @@
1
- import { N as NotificationServiceWranglerConfig } from '../shared/notification.BB9Jl8DI.mjs';
1
+ import { N as NotificationServiceWranglerConfig } from '../shared/notification.CdlaOUd0.mjs';
2
2
 
3
3
  declare function defineNotificationServiceWrangler(config: NotificationServiceWranglerConfig): {
4
4
  vars: {
@@ -20,6 +20,10 @@ declare function defineNotificationServiceWrangler(config: NotificationServiceWr
20
20
  database_id: string;
21
21
  migrations_dir: string;
22
22
  }[];
23
+ kv_namespaces: {
24
+ binding: string;
25
+ id: string;
26
+ }[];
23
27
  queues: {
24
28
  producers: {
25
29
  binding: string;
@@ -1,4 +1,4 @@
1
- import { N as NotificationServiceWranglerConfig } from '../shared/notification.BB9Jl8DI.js';
1
+ import { N as NotificationServiceWranglerConfig } from '../shared/notification.CdlaOUd0.js';
2
2
 
3
3
  declare function defineNotificationServiceWrangler(config: NotificationServiceWranglerConfig): {
4
4
  vars: {
@@ -20,6 +20,10 @@ declare function defineNotificationServiceWrangler(config: NotificationServiceWr
20
20
  database_id: string;
21
21
  migrations_dir: string;
22
22
  }[];
23
+ kv_namespaces: {
24
+ binding: string;
25
+ id: string;
26
+ }[];
23
27
  queues: {
24
28
  producers: {
25
29
  binding: string;
@@ -25,11 +25,21 @@ function defineNotificationServiceWrangler(config) {
25
25
  migrations_dir: "./src/database/migrations"
26
26
  }
27
27
  ],
28
+ kv_namespaces: envs.local.kv ? [
29
+ {
30
+ binding: "RECEIVED_EMAILS_KV",
31
+ id: envs.local.kv.id
32
+ }
33
+ ] : [],
28
34
  queues: {
29
35
  producers: [
30
36
  {
31
37
  binding: "NOTIFICATIONS_QUEUE",
32
38
  queue: `${project}-notifications`
39
+ },
40
+ {
41
+ binding: "AUDIT_LOGS_QUEUE",
42
+ queue: `${project}-audit-logs`
33
43
  }
34
44
  ],
35
45
  consumers: [
@@ -58,7 +68,15 @@ function defineNotificationServiceWrangler(config) {
58
68
  database_id: envCfg.d1.id,
59
69
  migrations_dir: "./src/database/migrations"
60
70
  }
61
- ]
71
+ ],
72
+ ...envCfg.kv && {
73
+ kv_namespaces: [
74
+ {
75
+ binding: "RECEIVED_EMAILS_KV",
76
+ id: envCfg.kv.id
77
+ }
78
+ ]
79
+ }
62
80
  };
63
81
  }
64
82
  return base;