@contentgrowth/content-emailing 0.6.2 → 0.7.2
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/backend/EmailService.cjs +315 -2
- package/dist/backend/EmailService.cjs.map +1 -1
- package/dist/backend/EmailService.d.cts +5 -1
- package/dist/backend/EmailService.d.ts +5 -1
- package/dist/backend/EmailService.js +315 -2
- package/dist/backend/EmailService.js.map +1 -1
- package/dist/backend/routes/index.cjs +315 -2
- package/dist/backend/routes/index.cjs.map +1 -1
- package/dist/backend/routes/index.js +315 -2
- package/dist/backend/routes/index.js.map +1 -1
- package/dist/frontend/index.cjs +144 -1
- package/dist/frontend/index.cjs.map +1 -1
- package/dist/frontend/index.d.cts +66 -1
- package/dist/frontend/index.d.ts +66 -1
- package/dist/frontend/index.js +143 -1
- package/dist/frontend/index.js.map +1 -1
- package/dist/index.cjs +324 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +65 -0
- package/dist/index.d.ts +65 -0
- package/dist/index.js +322 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/schema.sql +32 -0
package/dist/index.d.cts
CHANGED
|
@@ -5,3 +5,68 @@ export { createEmailRoutes, createTemplateRoutes, createTrackingRoutes } from '.
|
|
|
5
5
|
export { encodeTrackingLinks, extractVariables, getWebsiteUrl, markdownToPlainText, resetWebsiteUrlCache, wrapInEmailTemplate } from './common/index.cjs';
|
|
6
6
|
import 'react';
|
|
7
7
|
import 'hono';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Create a simple logger callback for EmailService that logs to D1.
|
|
11
|
+
* This is a convenience function for quick setup.
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* const emailService = new EmailService(env, {
|
|
15
|
+
* emailLogger: createEmailLoggerCallback(env.DB)
|
|
16
|
+
* });
|
|
17
|
+
*/
|
|
18
|
+
declare function createEmailLoggerCallback(db: any, tableName?: string): (entry: any) => Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Email Logger Utility
|
|
21
|
+
*
|
|
22
|
+
* Provides built-in email logging to D1 database.
|
|
23
|
+
* Can be used directly or passed to EmailService as the emailLogger callback.
|
|
24
|
+
*/
|
|
25
|
+
declare class EmailLogger {
|
|
26
|
+
/**
|
|
27
|
+
* @param {Object} db - D1 database binding
|
|
28
|
+
* @param {Object} options - Configuration options
|
|
29
|
+
* @param {string} [options.tableName='system_email_logs'] - Table name for logs
|
|
30
|
+
*/
|
|
31
|
+
constructor(db: any, options?: {
|
|
32
|
+
tableName?: string;
|
|
33
|
+
});
|
|
34
|
+
db: any;
|
|
35
|
+
tableName: string;
|
|
36
|
+
/**
|
|
37
|
+
* Creates a logger callback function for use with EmailService.
|
|
38
|
+
* Usage: new EmailService(env, { emailLogger: emailLogger.createCallback() })
|
|
39
|
+
*/
|
|
40
|
+
createCallback(): (entry: any) => Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Log an email event (pending, sent, or failed)
|
|
43
|
+
* @param {Object} entry - Log entry
|
|
44
|
+
*/
|
|
45
|
+
log(entry: any): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Query email logs with filtering
|
|
48
|
+
* @param {Object} options - Query options
|
|
49
|
+
*/
|
|
50
|
+
query(options?: any): Promise<{
|
|
51
|
+
logs: any;
|
|
52
|
+
total: any;
|
|
53
|
+
}>;
|
|
54
|
+
/**
|
|
55
|
+
* Get email sending statistics
|
|
56
|
+
* @param {number} sinceDays - Number of days to look back
|
|
57
|
+
*/
|
|
58
|
+
getStats(sinceDays?: number): Promise<{
|
|
59
|
+
total: number;
|
|
60
|
+
sent: number;
|
|
61
|
+
failed: number;
|
|
62
|
+
pending: number;
|
|
63
|
+
byTemplate: {};
|
|
64
|
+
}>;
|
|
65
|
+
/**
|
|
66
|
+
* Get recent failed emails for debugging
|
|
67
|
+
* @param {number} limit - Number of failed emails to retrieve
|
|
68
|
+
*/
|
|
69
|
+
getRecentFailures(limit?: number): Promise<any>;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export { EmailLogger, createEmailLoggerCallback };
|
package/dist/index.d.ts
CHANGED
|
@@ -5,3 +5,68 @@ export { createEmailRoutes, createTemplateRoutes, createTrackingRoutes } from '.
|
|
|
5
5
|
export { encodeTrackingLinks, extractVariables, getWebsiteUrl, markdownToPlainText, resetWebsiteUrlCache, wrapInEmailTemplate } from './common/index.js';
|
|
6
6
|
import 'react';
|
|
7
7
|
import 'hono';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Create a simple logger callback for EmailService that logs to D1.
|
|
11
|
+
* This is a convenience function for quick setup.
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* const emailService = new EmailService(env, {
|
|
15
|
+
* emailLogger: createEmailLoggerCallback(env.DB)
|
|
16
|
+
* });
|
|
17
|
+
*/
|
|
18
|
+
declare function createEmailLoggerCallback(db: any, tableName?: string): (entry: any) => Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Email Logger Utility
|
|
21
|
+
*
|
|
22
|
+
* Provides built-in email logging to D1 database.
|
|
23
|
+
* Can be used directly or passed to EmailService as the emailLogger callback.
|
|
24
|
+
*/
|
|
25
|
+
declare class EmailLogger {
|
|
26
|
+
/**
|
|
27
|
+
* @param {Object} db - D1 database binding
|
|
28
|
+
* @param {Object} options - Configuration options
|
|
29
|
+
* @param {string} [options.tableName='system_email_logs'] - Table name for logs
|
|
30
|
+
*/
|
|
31
|
+
constructor(db: any, options?: {
|
|
32
|
+
tableName?: string;
|
|
33
|
+
});
|
|
34
|
+
db: any;
|
|
35
|
+
tableName: string;
|
|
36
|
+
/**
|
|
37
|
+
* Creates a logger callback function for use with EmailService.
|
|
38
|
+
* Usage: new EmailService(env, { emailLogger: emailLogger.createCallback() })
|
|
39
|
+
*/
|
|
40
|
+
createCallback(): (entry: any) => Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Log an email event (pending, sent, or failed)
|
|
43
|
+
* @param {Object} entry - Log entry
|
|
44
|
+
*/
|
|
45
|
+
log(entry: any): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Query email logs with filtering
|
|
48
|
+
* @param {Object} options - Query options
|
|
49
|
+
*/
|
|
50
|
+
query(options?: any): Promise<{
|
|
51
|
+
logs: any;
|
|
52
|
+
total: any;
|
|
53
|
+
}>;
|
|
54
|
+
/**
|
|
55
|
+
* Get email sending statistics
|
|
56
|
+
* @param {number} sinceDays - Number of days to look back
|
|
57
|
+
*/
|
|
58
|
+
getStats(sinceDays?: number): Promise<{
|
|
59
|
+
total: number;
|
|
60
|
+
sent: number;
|
|
61
|
+
failed: number;
|
|
62
|
+
pending: number;
|
|
63
|
+
byTemplate: {};
|
|
64
|
+
}>;
|
|
65
|
+
/**
|
|
66
|
+
* Get recent failed emails for debugging
|
|
67
|
+
* @param {number} limit - Number of failed emails to retrieve
|
|
68
|
+
*/
|
|
69
|
+
getRecentFailures(limit?: number): Promise<any>;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export { EmailLogger, createEmailLoggerCallback };
|
package/dist/index.js
CHANGED
|
@@ -520,6 +520,226 @@ function createDOCacheProvider(doStub, instanceName = "global") {
|
|
|
520
520
|
};
|
|
521
521
|
}
|
|
522
522
|
|
|
523
|
+
// src/backend/EmailLogger.js
|
|
524
|
+
var EmailLogger = class {
|
|
525
|
+
/**
|
|
526
|
+
* @param {Object} db - D1 database binding
|
|
527
|
+
* @param {Object} options - Configuration options
|
|
528
|
+
* @param {string} [options.tableName='system_email_logs'] - Table name for logs
|
|
529
|
+
*/
|
|
530
|
+
constructor(db, options = {}) {
|
|
531
|
+
this.db = db;
|
|
532
|
+
this.tableName = options.tableName || "system_email_logs";
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Creates a logger callback function for use with EmailService.
|
|
536
|
+
* Usage: new EmailService(env, { emailLogger: emailLogger.createCallback() })
|
|
537
|
+
*/
|
|
538
|
+
createCallback() {
|
|
539
|
+
return async (entry) => {
|
|
540
|
+
await this.log(entry);
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* Log an email event (pending, sent, or failed)
|
|
545
|
+
* @param {Object} entry - Log entry
|
|
546
|
+
*/
|
|
547
|
+
async log(entry) {
|
|
548
|
+
const {
|
|
549
|
+
event,
|
|
550
|
+
recipientEmail,
|
|
551
|
+
recipientUserId,
|
|
552
|
+
templateId,
|
|
553
|
+
subject,
|
|
554
|
+
provider,
|
|
555
|
+
messageId,
|
|
556
|
+
batchId,
|
|
557
|
+
error,
|
|
558
|
+
errorCode,
|
|
559
|
+
metadata
|
|
560
|
+
} = entry;
|
|
561
|
+
try {
|
|
562
|
+
if (event === "pending") {
|
|
563
|
+
const id = crypto.randomUUID().replace(/-/g, "");
|
|
564
|
+
await this.db.prepare(`
|
|
565
|
+
INSERT INTO ${this.tableName}
|
|
566
|
+
(id, batch_id, recipient_email, recipient_user_id, template_id, subject, status, metadata, created_at)
|
|
567
|
+
VALUES (?, ?, ?, ?, ?, ?, 'pending', ?, strftime('%s', 'now'))
|
|
568
|
+
`).bind(
|
|
569
|
+
id,
|
|
570
|
+
batchId || null,
|
|
571
|
+
recipientEmail,
|
|
572
|
+
recipientUserId || null,
|
|
573
|
+
templateId || "direct",
|
|
574
|
+
subject || null,
|
|
575
|
+
metadata ? JSON.stringify(metadata) : null
|
|
576
|
+
).run();
|
|
577
|
+
} else if (event === "sent") {
|
|
578
|
+
await this.db.prepare(`
|
|
579
|
+
UPDATE ${this.tableName}
|
|
580
|
+
SET status = 'sent',
|
|
581
|
+
provider = ?,
|
|
582
|
+
provider_message_id = ?,
|
|
583
|
+
sent_at = strftime('%s', 'now')
|
|
584
|
+
WHERE recipient_email = ?
|
|
585
|
+
AND template_id = ?
|
|
586
|
+
AND status = 'pending'
|
|
587
|
+
ORDER BY created_at DESC
|
|
588
|
+
LIMIT 1
|
|
589
|
+
`).bind(
|
|
590
|
+
provider || null,
|
|
591
|
+
messageId || null,
|
|
592
|
+
recipientEmail,
|
|
593
|
+
templateId || "direct"
|
|
594
|
+
).run();
|
|
595
|
+
} else if (event === "failed") {
|
|
596
|
+
await this.db.prepare(`
|
|
597
|
+
UPDATE ${this.tableName}
|
|
598
|
+
SET status = 'failed',
|
|
599
|
+
provider = ?,
|
|
600
|
+
error_message = ?,
|
|
601
|
+
error_code = ?
|
|
602
|
+
WHERE recipient_email = ?
|
|
603
|
+
AND template_id = ?
|
|
604
|
+
AND status = 'pending'
|
|
605
|
+
ORDER BY created_at DESC
|
|
606
|
+
LIMIT 1
|
|
607
|
+
`).bind(
|
|
608
|
+
provider || null,
|
|
609
|
+
error || null,
|
|
610
|
+
errorCode || null,
|
|
611
|
+
recipientEmail,
|
|
612
|
+
templateId || "direct"
|
|
613
|
+
).run();
|
|
614
|
+
}
|
|
615
|
+
} catch (e) {
|
|
616
|
+
console.error("[EmailLogger] Failed to log:", e);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Query email logs with filtering
|
|
621
|
+
* @param {Object} options - Query options
|
|
622
|
+
*/
|
|
623
|
+
async query(options = {}) {
|
|
624
|
+
const {
|
|
625
|
+
recipientEmail,
|
|
626
|
+
recipientUserId,
|
|
627
|
+
templateId,
|
|
628
|
+
status,
|
|
629
|
+
batchId,
|
|
630
|
+
limit = 50,
|
|
631
|
+
offset = 0
|
|
632
|
+
} = options;
|
|
633
|
+
const conditions = [];
|
|
634
|
+
const bindings = [];
|
|
635
|
+
if (recipientEmail) {
|
|
636
|
+
conditions.push("recipient_email = ?");
|
|
637
|
+
bindings.push(recipientEmail);
|
|
638
|
+
}
|
|
639
|
+
if (recipientUserId) {
|
|
640
|
+
conditions.push("recipient_user_id = ?");
|
|
641
|
+
bindings.push(recipientUserId);
|
|
642
|
+
}
|
|
643
|
+
if (templateId) {
|
|
644
|
+
conditions.push("template_id = ?");
|
|
645
|
+
bindings.push(templateId);
|
|
646
|
+
}
|
|
647
|
+
if (status) {
|
|
648
|
+
conditions.push("status = ?");
|
|
649
|
+
bindings.push(status);
|
|
650
|
+
}
|
|
651
|
+
if (batchId) {
|
|
652
|
+
conditions.push("batch_id = ?");
|
|
653
|
+
bindings.push(batchId);
|
|
654
|
+
}
|
|
655
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
656
|
+
const countResult = await this.db.prepare(
|
|
657
|
+
`SELECT COUNT(*) as count FROM ${this.tableName} ${whereClause}`
|
|
658
|
+
).bind(...bindings).first();
|
|
659
|
+
const { results } = await this.db.prepare(`
|
|
660
|
+
SELECT id, batch_id, recipient_email, recipient_user_id, template_id, subject,
|
|
661
|
+
status, provider, provider_message_id, error_message, error_code, metadata,
|
|
662
|
+
created_at, sent_at
|
|
663
|
+
FROM ${this.tableName}
|
|
664
|
+
${whereClause}
|
|
665
|
+
ORDER BY created_at DESC
|
|
666
|
+
LIMIT ? OFFSET ?
|
|
667
|
+
`).bind(...bindings, limit, offset).all();
|
|
668
|
+
const logs = (results || []).map((row) => ({
|
|
669
|
+
id: row.id,
|
|
670
|
+
batchId: row.batch_id,
|
|
671
|
+
recipientEmail: row.recipient_email,
|
|
672
|
+
recipientUserId: row.recipient_user_id,
|
|
673
|
+
templateId: row.template_id,
|
|
674
|
+
subject: row.subject,
|
|
675
|
+
status: row.status,
|
|
676
|
+
provider: row.provider,
|
|
677
|
+
providerMessageId: row.provider_message_id,
|
|
678
|
+
errorMessage: row.error_message,
|
|
679
|
+
errorCode: row.error_code,
|
|
680
|
+
metadata: row.metadata ? JSON.parse(row.metadata) : void 0,
|
|
681
|
+
createdAt: row.created_at,
|
|
682
|
+
sentAt: row.sent_at
|
|
683
|
+
}));
|
|
684
|
+
return { logs, total: countResult?.count || 0 };
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Get email sending statistics
|
|
688
|
+
* @param {number} sinceDays - Number of days to look back
|
|
689
|
+
*/
|
|
690
|
+
async getStats(sinceDays = 7) {
|
|
691
|
+
const sinceTimestamp = Math.floor(Date.now() / 1e3) - sinceDays * 24 * 60 * 60;
|
|
692
|
+
const statusResult = await this.db.prepare(`
|
|
693
|
+
SELECT status, COUNT(*) as count
|
|
694
|
+
FROM ${this.tableName}
|
|
695
|
+
WHERE created_at >= ?
|
|
696
|
+
GROUP BY status
|
|
697
|
+
`).bind(sinceTimestamp).all();
|
|
698
|
+
const templateResult = await this.db.prepare(`
|
|
699
|
+
SELECT template_id, COUNT(*) as count
|
|
700
|
+
FROM ${this.tableName}
|
|
701
|
+
WHERE created_at >= ?
|
|
702
|
+
GROUP BY template_id
|
|
703
|
+
`).bind(sinceTimestamp).all();
|
|
704
|
+
const stats = {
|
|
705
|
+
total: 0,
|
|
706
|
+
sent: 0,
|
|
707
|
+
failed: 0,
|
|
708
|
+
pending: 0,
|
|
709
|
+
byTemplate: {}
|
|
710
|
+
};
|
|
711
|
+
(statusResult.results || []).forEach((row) => {
|
|
712
|
+
const count = row.count || 0;
|
|
713
|
+
stats.total += count;
|
|
714
|
+
if (row.status === "sent") stats.sent = count;
|
|
715
|
+
if (row.status === "failed") stats.failed = count;
|
|
716
|
+
if (row.status === "pending") stats.pending = count;
|
|
717
|
+
});
|
|
718
|
+
(templateResult.results || []).forEach((row) => {
|
|
719
|
+
stats.byTemplate[row.template_id] = row.count;
|
|
720
|
+
});
|
|
721
|
+
return stats;
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* Get recent failed emails for debugging
|
|
725
|
+
* @param {number} limit - Number of failed emails to retrieve
|
|
726
|
+
*/
|
|
727
|
+
async getRecentFailures(limit = 20) {
|
|
728
|
+
const { results } = await this.db.prepare(`
|
|
729
|
+
SELECT id, recipient_email, template_id, subject, error_message, error_code, created_at
|
|
730
|
+
FROM ${this.tableName}
|
|
731
|
+
WHERE status = 'failed'
|
|
732
|
+
ORDER BY created_at DESC
|
|
733
|
+
LIMIT ?
|
|
734
|
+
`).bind(limit).all();
|
|
735
|
+
return results || [];
|
|
736
|
+
}
|
|
737
|
+
};
|
|
738
|
+
function createEmailLoggerCallback(db, tableName = "system_email_logs") {
|
|
739
|
+
const logger = new EmailLogger(db, { tableName });
|
|
740
|
+
return logger.createCallback();
|
|
741
|
+
}
|
|
742
|
+
|
|
523
743
|
// src/backend/EmailService.js
|
|
524
744
|
var EmailService = class {
|
|
525
745
|
/**
|
|
@@ -527,6 +747,7 @@ var EmailService = class {
|
|
|
527
747
|
* @param {Object} config - Configuration options
|
|
528
748
|
* @param {string} [config.emailTablePrefix='system_email_'] - Prefix for D1 tables
|
|
529
749
|
* @param {Object} [config.defaults] - Default settings (fromName, fromAddress)
|
|
750
|
+
* @param {boolean|Function} [config.emailLogger] - Email logger: true (default), false (disabled), or custom callback
|
|
530
751
|
* @param {Object} [cacheProvider] - Optional cache interface (DO stub or KV wrapper)
|
|
531
752
|
*/
|
|
532
753
|
constructor(env, config = {}, cacheProvider = null) {
|
|
@@ -554,6 +775,16 @@ var EmailService = class {
|
|
|
554
775
|
},
|
|
555
776
|
...config
|
|
556
777
|
};
|
|
778
|
+
if (config.emailLogger === false) {
|
|
779
|
+
this.emailLogger = null;
|
|
780
|
+
} else if (typeof config.emailLogger === "function") {
|
|
781
|
+
this.emailLogger = config.emailLogger;
|
|
782
|
+
} else if (env.DB) {
|
|
783
|
+
const logger = new EmailLogger(env.DB);
|
|
784
|
+
this.emailLogger = logger.createCallback();
|
|
785
|
+
} else {
|
|
786
|
+
this.emailLogger = null;
|
|
787
|
+
}
|
|
557
788
|
if (!cacheProvider && env.EMAIL_TEMPLATE_CACHE) {
|
|
558
789
|
this.cache = createDOCacheProvider(env.EMAIL_TEMPLATE_CACHE);
|
|
559
790
|
} else {
|
|
@@ -803,13 +1034,30 @@ var EmailService = class {
|
|
|
803
1034
|
* @param {Object} [params.metadata] - Additional metadata
|
|
804
1035
|
* @returns {Promise<Object>} Delivery result
|
|
805
1036
|
*/
|
|
806
|
-
async sendEmail({ to, subject, html, htmlBody, text, textBody, provider, profile = "system", tenantId = null, metadata = {} }) {
|
|
1037
|
+
async sendEmail({ to, subject, html, htmlBody, text, textBody, provider, profile = "system", tenantId = null, metadata = {}, batchId = null, userId = null }) {
|
|
807
1038
|
const htmlContent = html || htmlBody;
|
|
808
1039
|
const textContent = text || textBody;
|
|
1040
|
+
const templateId = metadata?.templateId || "direct";
|
|
1041
|
+
if (this.emailLogger) {
|
|
1042
|
+
try {
|
|
1043
|
+
await this.emailLogger({
|
|
1044
|
+
event: "pending",
|
|
1045
|
+
recipientEmail: to,
|
|
1046
|
+
recipientUserId: userId,
|
|
1047
|
+
templateId,
|
|
1048
|
+
subject,
|
|
1049
|
+
batchId,
|
|
1050
|
+
metadata
|
|
1051
|
+
});
|
|
1052
|
+
} catch (e) {
|
|
1053
|
+
console.warn("[EmailService] emailLogger pending failed:", e);
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
809
1056
|
try {
|
|
810
1057
|
const settings = await this.loadSettings(profile, tenantId);
|
|
811
1058
|
const useProvider = provider || settings.provider || "mailchannels";
|
|
812
1059
|
let result;
|
|
1060
|
+
let providerMessageId = null;
|
|
813
1061
|
switch (useProvider) {
|
|
814
1062
|
case "mailchannels":
|
|
815
1063
|
result = await this.sendViaMailChannels(to, subject, htmlContent, textContent, settings, metadata);
|
|
@@ -819,22 +1067,91 @@ var EmailService = class {
|
|
|
819
1067
|
break;
|
|
820
1068
|
case "resend":
|
|
821
1069
|
result = await this.sendViaResend(to, subject, htmlContent, textContent, settings, metadata);
|
|
1070
|
+
if (result && typeof result === "object" && result.id) {
|
|
1071
|
+
providerMessageId = result.id;
|
|
1072
|
+
result = true;
|
|
1073
|
+
}
|
|
822
1074
|
break;
|
|
823
1075
|
case "sendpulse":
|
|
824
1076
|
result = await this.sendViaSendPulse(to, subject, htmlContent, textContent, settings, metadata);
|
|
825
1077
|
break;
|
|
826
1078
|
default:
|
|
827
1079
|
console.error(`[EmailService] Unknown provider: ${useProvider}`);
|
|
1080
|
+
if (this.emailLogger) {
|
|
1081
|
+
try {
|
|
1082
|
+
await this.emailLogger({
|
|
1083
|
+
event: "failed",
|
|
1084
|
+
recipientEmail: to,
|
|
1085
|
+
recipientUserId: userId,
|
|
1086
|
+
templateId,
|
|
1087
|
+
subject,
|
|
1088
|
+
provider: useProvider,
|
|
1089
|
+
batchId,
|
|
1090
|
+
error: `Unknown email provider: ${useProvider}`,
|
|
1091
|
+
metadata
|
|
1092
|
+
});
|
|
1093
|
+
} catch (e) {
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
828
1096
|
return { success: false, error: `Unknown email provider: ${useProvider}` };
|
|
829
1097
|
}
|
|
830
1098
|
if (result) {
|
|
831
|
-
|
|
1099
|
+
const messageId = providerMessageId || crypto.randomUUID();
|
|
1100
|
+
if (this.emailLogger) {
|
|
1101
|
+
try {
|
|
1102
|
+
await this.emailLogger({
|
|
1103
|
+
event: "sent",
|
|
1104
|
+
recipientEmail: to,
|
|
1105
|
+
recipientUserId: userId,
|
|
1106
|
+
templateId,
|
|
1107
|
+
subject,
|
|
1108
|
+
provider: useProvider,
|
|
1109
|
+
messageId,
|
|
1110
|
+
batchId,
|
|
1111
|
+
metadata
|
|
1112
|
+
});
|
|
1113
|
+
} catch (e) {
|
|
1114
|
+
console.warn("[EmailService] emailLogger sent failed:", e);
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
return { success: true, messageId };
|
|
832
1118
|
} else {
|
|
833
1119
|
console.error("[EmailService] Failed to send email to:", to);
|
|
1120
|
+
if (this.emailLogger) {
|
|
1121
|
+
try {
|
|
1122
|
+
await this.emailLogger({
|
|
1123
|
+
event: "failed",
|
|
1124
|
+
recipientEmail: to,
|
|
1125
|
+
recipientUserId: userId,
|
|
1126
|
+
templateId,
|
|
1127
|
+
subject,
|
|
1128
|
+
provider: useProvider,
|
|
1129
|
+
batchId,
|
|
1130
|
+
error: "Failed to send email",
|
|
1131
|
+
metadata
|
|
1132
|
+
});
|
|
1133
|
+
} catch (e) {
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
834
1136
|
return { success: false, error: "Failed to send email" };
|
|
835
1137
|
}
|
|
836
1138
|
} catch (error) {
|
|
837
1139
|
console.error("[EmailService] Error sending email:", error);
|
|
1140
|
+
if (this.emailLogger) {
|
|
1141
|
+
try {
|
|
1142
|
+
await this.emailLogger({
|
|
1143
|
+
event: "failed",
|
|
1144
|
+
recipientEmail: to,
|
|
1145
|
+
recipientUserId: userId,
|
|
1146
|
+
templateId,
|
|
1147
|
+
subject,
|
|
1148
|
+
batchId,
|
|
1149
|
+
error: error.message,
|
|
1150
|
+
metadata
|
|
1151
|
+
});
|
|
1152
|
+
} catch (e) {
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
838
1155
|
return { success: false, error: error.message };
|
|
839
1156
|
}
|
|
840
1157
|
}
|
|
@@ -1694,7 +2011,7 @@ var TemplateManager = ({
|
|
|
1694
2011
|
},
|
|
1695
2012
|
/* @__PURE__ */ React3.createElement("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24" }, /* @__PURE__ */ React3.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 4v16m8-8H4" })),
|
|
1696
2013
|
"Create Template"
|
|
1697
|
-
)), error && /* @__PURE__ */ React3.createElement("div", { className: "mb-4 p-3 rounded-lg bg-red-50 text-red-800 border border-red-200" }, error, /* @__PURE__ */ React3.createElement("button", { onClick: () => setError(null), className: "ml-2 underline" }, "Dismiss")), /* @__PURE__ */ React3.createElement("div", { className: "
|
|
2014
|
+
)), error && /* @__PURE__ */ React3.createElement("div", { className: "mb-4 p-3 rounded-lg bg-red-50 text-red-800 border border-red-200" }, error, /* @__PURE__ */ React3.createElement("button", { onClick: () => setError(null), className: "ml-2 underline" }, "Dismiss")), /* @__PURE__ */ React3.createElement("div", { className: "flex gap-4 mb-6" }, /* @__PURE__ */ React3.createElement("div", { className: "bg-white rounded-lg border border-gray-200 p-4 flex-1" }, /* @__PURE__ */ React3.createElement("div", { className: "text-2xl font-bold text-gray-900" }, templates.length), /* @__PURE__ */ React3.createElement("div", { className: "text-sm text-gray-500" }, "Total")), /* @__PURE__ */ React3.createElement("div", { className: "bg-white rounded-lg border border-gray-200 p-4 flex-1" }, /* @__PURE__ */ React3.createElement("div", { className: "text-2xl font-bold text-green-600" }, templates.filter((t) => t.is_active).length), /* @__PURE__ */ React3.createElement("div", { className: "text-sm text-gray-500" }, "Active")), /* @__PURE__ */ React3.createElement("div", { className: "bg-white rounded-lg border border-gray-200 p-4 flex-1" }, /* @__PURE__ */ React3.createElement("div", { className: "text-2xl font-bold text-yellow-600" }, templates.filter((t) => !t.is_active).length), /* @__PURE__ */ React3.createElement("div", { className: "text-sm text-gray-500" }, "Inactive")), /* @__PURE__ */ React3.createElement("div", { className: "bg-white rounded-lg border border-gray-200 p-4 flex-1" }, /* @__PURE__ */ React3.createElement("div", { className: "text-2xl font-bold text-blue-600" }, new Set(templates.map((t) => t.template_type)).size), /* @__PURE__ */ React3.createElement("div", { className: "text-sm text-gray-500" }, "Types"))), /* @__PURE__ */ React3.createElement("div", { className: "flex gap-2 mb-6 border-b border-gray-200 pb-3 flex-wrap" }, uniqueTypes.map((type) => /* @__PURE__ */ React3.createElement(
|
|
1698
2015
|
"button",
|
|
1699
2016
|
{
|
|
1700
2017
|
key: type,
|
|
@@ -1818,11 +2135,13 @@ var TemplateManager = ({
|
|
|
1818
2135
|
)))));
|
|
1819
2136
|
};
|
|
1820
2137
|
export {
|
|
2138
|
+
EmailLogger,
|
|
1821
2139
|
EmailService,
|
|
1822
2140
|
EmailingCacheDO,
|
|
1823
2141
|
TemplateEditor,
|
|
1824
2142
|
TemplateManager,
|
|
1825
2143
|
createDOCacheProvider,
|
|
2144
|
+
createEmailLoggerCallback,
|
|
1826
2145
|
createEmailRoutes,
|
|
1827
2146
|
createTemplateRoutes,
|
|
1828
2147
|
createTrackingRoutes,
|