payload-plugin-newsletter 0.9.2 → 0.10.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.
- package/CHANGELOG.md +43 -0
- package/README.md +6 -5
- package/dist/components.cjs +3 -7
- package/dist/components.cjs.map +1 -1
- package/dist/components.d.cts +0 -2
- package/dist/components.d.ts +0 -2
- package/dist/components.js +3 -7
- package/dist/components.js.map +1 -1
- package/dist/index.cjs +300 -846
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +299 -845
- package/dist/index.js.map +1 -1
- package/dist/types.cjs +0 -2
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +3 -83
- package/dist/types.d.ts +3 -83
- package/dist/types.js +0 -2
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -65,13 +65,6 @@ var init_broadcast = __esm({
|
|
|
65
65
|
}
|
|
66
66
|
});
|
|
67
67
|
|
|
68
|
-
// src/types/channel.ts
|
|
69
|
-
var init_channel = __esm({
|
|
70
|
-
"src/types/channel.ts"() {
|
|
71
|
-
"use strict";
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
|
|
75
68
|
// src/types/providers.ts
|
|
76
69
|
var BaseBroadcastProvider;
|
|
77
70
|
var init_providers = __esm({
|
|
@@ -176,7 +169,6 @@ var init_types = __esm({
|
|
|
176
169
|
"src/types/index.ts"() {
|
|
177
170
|
"use strict";
|
|
178
171
|
init_broadcast();
|
|
179
|
-
init_channel();
|
|
180
172
|
init_providers();
|
|
181
173
|
init_newsletter();
|
|
182
174
|
}
|
|
@@ -197,8 +189,7 @@ var init_broadcast2 = __esm({
|
|
|
197
189
|
super(config);
|
|
198
190
|
this.name = "broadcast";
|
|
199
191
|
this.apiUrl = config.apiUrl.replace(/\/$/, "");
|
|
200
|
-
this.
|
|
201
|
-
this.token = this.isDevelopment ? config.tokens.development || config.tokens.production || "" : config.tokens.production || config.tokens.development || "";
|
|
192
|
+
this.token = config.token;
|
|
202
193
|
if (!this.token) {
|
|
203
194
|
throw new BroadcastProviderError(
|
|
204
195
|
"Broadcast API token is required",
|
|
@@ -207,158 +198,6 @@ var init_broadcast2 = __esm({
|
|
|
207
198
|
);
|
|
208
199
|
}
|
|
209
200
|
}
|
|
210
|
-
// Channel Management Methods
|
|
211
|
-
async listChannels(options) {
|
|
212
|
-
try {
|
|
213
|
-
const params = new URLSearchParams();
|
|
214
|
-
if (options?.limit) params.append("limit", options.limit.toString());
|
|
215
|
-
if (options?.offset) params.append("offset", options.offset.toString());
|
|
216
|
-
const response = await fetch(`${this.apiUrl}/api/v1/channels?${params}`, {
|
|
217
|
-
method: "GET",
|
|
218
|
-
headers: {
|
|
219
|
-
"Authorization": `Bearer ${this.token}`,
|
|
220
|
-
"Content-Type": "application/json"
|
|
221
|
-
}
|
|
222
|
-
});
|
|
223
|
-
if (!response.ok) {
|
|
224
|
-
const error = await response.text();
|
|
225
|
-
throw new Error(`Broadcast API error: ${response.status} - ${error}`);
|
|
226
|
-
}
|
|
227
|
-
const data = await response.json();
|
|
228
|
-
const channels = data.data.map((channel) => this.transformChannelFromApi(channel));
|
|
229
|
-
return {
|
|
230
|
-
channels,
|
|
231
|
-
total: data.total || channels.length,
|
|
232
|
-
limit: options?.limit || 20,
|
|
233
|
-
offset: options?.offset || 0
|
|
234
|
-
};
|
|
235
|
-
} catch (error) {
|
|
236
|
-
throw new BroadcastProviderError(
|
|
237
|
-
`Failed to list channels: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
238
|
-
"PROVIDER_ERROR" /* PROVIDER_ERROR */,
|
|
239
|
-
this.name,
|
|
240
|
-
error
|
|
241
|
-
);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
async getChannel(id) {
|
|
245
|
-
try {
|
|
246
|
-
const response = await fetch(`${this.apiUrl}/api/v1/channels/${id}`, {
|
|
247
|
-
method: "GET",
|
|
248
|
-
headers: {
|
|
249
|
-
"Authorization": `Bearer ${this.token}`,
|
|
250
|
-
"Content-Type": "application/json"
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
if (!response.ok) {
|
|
254
|
-
if (response.status === 404) {
|
|
255
|
-
throw new BroadcastProviderError(
|
|
256
|
-
`Channel not found: ${id}`,
|
|
257
|
-
"CHANNEL_NOT_FOUND" /* CHANNEL_NOT_FOUND */,
|
|
258
|
-
this.name
|
|
259
|
-
);
|
|
260
|
-
}
|
|
261
|
-
const error = await response.text();
|
|
262
|
-
throw new Error(`Broadcast API error: ${response.status} - ${error}`);
|
|
263
|
-
}
|
|
264
|
-
const channel = await response.json();
|
|
265
|
-
return this.transformChannelFromApi(channel);
|
|
266
|
-
} catch (error) {
|
|
267
|
-
if (error instanceof BroadcastProviderError) throw error;
|
|
268
|
-
throw new BroadcastProviderError(
|
|
269
|
-
`Failed to get channel: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
270
|
-
"PROVIDER_ERROR" /* PROVIDER_ERROR */,
|
|
271
|
-
this.name,
|
|
272
|
-
error
|
|
273
|
-
);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
async createChannel(data) {
|
|
277
|
-
try {
|
|
278
|
-
const response = await fetch(`${this.apiUrl}/api/v1/channels`, {
|
|
279
|
-
method: "POST",
|
|
280
|
-
headers: {
|
|
281
|
-
"Authorization": `Bearer ${this.token}`,
|
|
282
|
-
"Content-Type": "application/json"
|
|
283
|
-
},
|
|
284
|
-
body: JSON.stringify({
|
|
285
|
-
channel: {
|
|
286
|
-
name: data.name,
|
|
287
|
-
description: data.description,
|
|
288
|
-
from: data.fromName,
|
|
289
|
-
address: data.fromEmail,
|
|
290
|
-
reply_to: data.replyTo
|
|
291
|
-
}
|
|
292
|
-
})
|
|
293
|
-
});
|
|
294
|
-
if (!response.ok) {
|
|
295
|
-
const error = await response.text();
|
|
296
|
-
throw new Error(`Broadcast API error: ${response.status} - ${error}`);
|
|
297
|
-
}
|
|
298
|
-
const channel = await response.json();
|
|
299
|
-
return this.transformChannelFromApi(channel);
|
|
300
|
-
} catch (error) {
|
|
301
|
-
throw new BroadcastProviderError(
|
|
302
|
-
`Failed to create channel: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
303
|
-
"PROVIDER_ERROR" /* PROVIDER_ERROR */,
|
|
304
|
-
this.name,
|
|
305
|
-
error
|
|
306
|
-
);
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
async updateChannel(id, data) {
|
|
310
|
-
try {
|
|
311
|
-
const updateData = { channel: {} };
|
|
312
|
-
if (data.name !== void 0) updateData.channel.name = data.name;
|
|
313
|
-
if (data.description !== void 0) updateData.channel.description = data.description;
|
|
314
|
-
if (data.fromName !== void 0) updateData.channel.from = data.fromName;
|
|
315
|
-
if (data.fromEmail !== void 0) updateData.channel.address = data.fromEmail;
|
|
316
|
-
if (data.replyTo !== void 0) updateData.channel.reply_to = data.replyTo;
|
|
317
|
-
const response = await fetch(`${this.apiUrl}/api/v1/channels/${id}`, {
|
|
318
|
-
method: "PATCH",
|
|
319
|
-
headers: {
|
|
320
|
-
"Authorization": `Bearer ${this.token}`,
|
|
321
|
-
"Content-Type": "application/json"
|
|
322
|
-
},
|
|
323
|
-
body: JSON.stringify(updateData)
|
|
324
|
-
});
|
|
325
|
-
if (!response.ok) {
|
|
326
|
-
const error = await response.text();
|
|
327
|
-
throw new Error(`Broadcast API error: ${response.status} - ${error}`);
|
|
328
|
-
}
|
|
329
|
-
const channel = await response.json();
|
|
330
|
-
return this.transformChannelFromApi(channel);
|
|
331
|
-
} catch (error) {
|
|
332
|
-
throw new BroadcastProviderError(
|
|
333
|
-
`Failed to update channel: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
334
|
-
"PROVIDER_ERROR" /* PROVIDER_ERROR */,
|
|
335
|
-
this.name,
|
|
336
|
-
error
|
|
337
|
-
);
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
async deleteChannel(id) {
|
|
341
|
-
try {
|
|
342
|
-
const response = await fetch(`${this.apiUrl}/api/v1/channels/${id}`, {
|
|
343
|
-
method: "DELETE",
|
|
344
|
-
headers: {
|
|
345
|
-
"Authorization": `Bearer ${this.token}`,
|
|
346
|
-
"Content-Type": "application/json"
|
|
347
|
-
}
|
|
348
|
-
});
|
|
349
|
-
if (!response.ok) {
|
|
350
|
-
const error = await response.text();
|
|
351
|
-
throw new Error(`Broadcast API error: ${response.status} - ${error}`);
|
|
352
|
-
}
|
|
353
|
-
} catch (error) {
|
|
354
|
-
throw new BroadcastProviderError(
|
|
355
|
-
`Failed to delete channel: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
356
|
-
"PROVIDER_ERROR" /* PROVIDER_ERROR */,
|
|
357
|
-
this.name,
|
|
358
|
-
error
|
|
359
|
-
);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
201
|
// Broadcast Management Methods
|
|
363
202
|
async list(options) {
|
|
364
203
|
try {
|
|
@@ -422,7 +261,7 @@ var init_broadcast2 = __esm({
|
|
|
422
261
|
}
|
|
423
262
|
async create(data) {
|
|
424
263
|
try {
|
|
425
|
-
this.validateRequiredFields(data, ["
|
|
264
|
+
this.validateRequiredFields(data, ["name", "subject", "content"]);
|
|
426
265
|
const response = await fetch(`${this.apiUrl}/api/v1/broadcasts`, {
|
|
427
266
|
method: "POST",
|
|
428
267
|
headers: {
|
|
@@ -431,8 +270,6 @@ var init_broadcast2 = __esm({
|
|
|
431
270
|
},
|
|
432
271
|
body: JSON.stringify({
|
|
433
272
|
broadcast: {
|
|
434
|
-
channel_id: parseInt(data.channelId),
|
|
435
|
-
// Broadcast API uses numeric IDs
|
|
436
273
|
name: data.name,
|
|
437
274
|
subject: data.subject,
|
|
438
275
|
preheader: data.preheader,
|
|
@@ -649,8 +486,8 @@ var init_broadcast2 = __esm({
|
|
|
649
486
|
supportsABTesting: false,
|
|
650
487
|
supportsTemplates: false,
|
|
651
488
|
supportsPersonalization: true,
|
|
652
|
-
supportsMultipleChannels:
|
|
653
|
-
supportsChannelSegmentation:
|
|
489
|
+
supportsMultipleChannels: false,
|
|
490
|
+
supportsChannelSegmentation: false,
|
|
654
491
|
editableStatuses: ["draft" /* DRAFT */, "scheduled" /* SCHEDULED */],
|
|
655
492
|
supportedContentTypes: ["html", "text"]
|
|
656
493
|
};
|
|
@@ -666,406 +503,37 @@ var init_broadcast2 = __esm({
|
|
|
666
503
|
transformBroadcastFromApi(broadcast) {
|
|
667
504
|
return {
|
|
668
505
|
id: broadcast.id.toString(),
|
|
669
|
-
channelId: "1",
|
|
670
|
-
// TODO: Get from API response when available
|
|
671
506
|
name: broadcast.name,
|
|
672
507
|
subject: broadcast.subject,
|
|
673
508
|
preheader: broadcast.preheader,
|
|
674
509
|
content: broadcast.body,
|
|
675
|
-
status: this.mapBroadcastStatus(broadcast.status),
|
|
676
|
-
trackOpens: broadcast.track_opens,
|
|
677
|
-
trackClicks: broadcast.track_clicks,
|
|
678
|
-
replyTo: broadcast.reply_to,
|
|
679
|
-
recipientCount: broadcast.total_recipients,
|
|
680
|
-
sentAt: broadcast.sent_at ? new Date(broadcast.sent_at) : void 0,
|
|
681
|
-
scheduledAt: broadcast.scheduled_send_at ? new Date(broadcast.scheduled_send_at) : void 0,
|
|
682
|
-
createdAt: new Date(broadcast.created_at),
|
|
683
|
-
updatedAt: new Date(broadcast.updated_at),
|
|
684
|
-
providerData: { broadcast },
|
|
685
|
-
providerId: broadcast.id.toString(),
|
|
686
|
-
providerType: "broadcast"
|
|
687
|
-
};
|
|
688
|
-
}
|
|
689
|
-
transformChannelFromApi(channel) {
|
|
690
|
-
return {
|
|
691
|
-
id: channel.id.toString(),
|
|
692
|
-
name: channel.name,
|
|
693
|
-
description: channel.description,
|
|
694
|
-
fromName: channel.from,
|
|
695
|
-
fromEmail: channel.address,
|
|
696
|
-
replyTo: channel.reply_to,
|
|
697
|
-
providerId: channel.id.toString(),
|
|
698
|
-
providerType: "broadcast",
|
|
699
|
-
subscriberCount: channel.total_active_subscribers,
|
|
700
|
-
active: true,
|
|
701
|
-
// Broadcast API doesn't have an active field
|
|
702
|
-
createdAt: new Date(channel.created_at),
|
|
703
|
-
updatedAt: new Date(channel.updated_at)
|
|
704
|
-
};
|
|
705
|
-
}
|
|
706
|
-
mapBroadcastStatus(status) {
|
|
707
|
-
const statusMap = {
|
|
708
|
-
"draft": "draft" /* DRAFT */,
|
|
709
|
-
"scheduled": "scheduled" /* SCHEDULED */,
|
|
710
|
-
"queueing": "sending" /* SENDING */,
|
|
711
|
-
"sending": "sending" /* SENDING */,
|
|
712
|
-
"sent": "sent" /* SENT */,
|
|
713
|
-
"failed": "failed" /* FAILED */,
|
|
714
|
-
"partial_failure": "failed" /* FAILED */,
|
|
715
|
-
"paused": "paused" /* PAUSED */,
|
|
716
|
-
"aborted": "canceled" /* CANCELED */
|
|
717
|
-
};
|
|
718
|
-
return statusMap[status] || "draft" /* DRAFT */;
|
|
719
|
-
}
|
|
720
|
-
};
|
|
721
|
-
}
|
|
722
|
-
});
|
|
723
|
-
|
|
724
|
-
// src/providers/resend/broadcast.ts
|
|
725
|
-
var broadcast_exports2 = {};
|
|
726
|
-
__export(broadcast_exports2, {
|
|
727
|
-
ResendBroadcastProvider: () => ResendBroadcastProvider
|
|
728
|
-
});
|
|
729
|
-
var import_resend3, ResendBroadcastProvider;
|
|
730
|
-
var init_broadcast3 = __esm({
|
|
731
|
-
"src/providers/resend/broadcast.ts"() {
|
|
732
|
-
"use strict";
|
|
733
|
-
import_resend3 = require("resend");
|
|
734
|
-
init_types();
|
|
735
|
-
ResendBroadcastProvider = class extends BaseBroadcastProvider {
|
|
736
|
-
constructor(config) {
|
|
737
|
-
super(config);
|
|
738
|
-
this.name = "resend";
|
|
739
|
-
this.client = new import_resend3.Resend(config.apiKey);
|
|
740
|
-
this.audienceIds = config.audienceIds || {};
|
|
741
|
-
this.isDevelopment = process.env.NODE_ENV !== "production";
|
|
742
|
-
if (!config.apiKey) {
|
|
743
|
-
throw new BroadcastProviderError(
|
|
744
|
-
"Resend API key is required",
|
|
745
|
-
"CONFIGURATION_ERROR" /* CONFIGURATION_ERROR */,
|
|
746
|
-
this.name
|
|
747
|
-
);
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
// Channel Management Methods (map to Resend Audiences)
|
|
751
|
-
async listChannels(options) {
|
|
752
|
-
try {
|
|
753
|
-
const response = await this.client.audiences.list();
|
|
754
|
-
const channels = response.data?.data?.map((audience) => ({
|
|
755
|
-
id: audience.id,
|
|
756
|
-
name: audience.name,
|
|
757
|
-
description: void 0,
|
|
758
|
-
// Resend doesn't have description
|
|
759
|
-
fromName: this.config.fromName || "",
|
|
760
|
-
fromEmail: this.config.fromEmail || "",
|
|
761
|
-
replyTo: this.config.replyTo,
|
|
762
|
-
providerId: audience.id,
|
|
763
|
-
providerType: "resend",
|
|
764
|
-
subscriberCount: void 0,
|
|
765
|
-
// Not available in list API
|
|
766
|
-
active: true,
|
|
767
|
-
createdAt: new Date(audience.created_at),
|
|
768
|
-
updatedAt: new Date(audience.created_at)
|
|
769
|
-
// No updated_at in Resend
|
|
770
|
-
})) || [];
|
|
771
|
-
return {
|
|
772
|
-
channels,
|
|
773
|
-
total: channels.length,
|
|
774
|
-
limit: options?.limit || 100,
|
|
775
|
-
offset: options?.offset || 0
|
|
776
|
-
};
|
|
777
|
-
} catch (error) {
|
|
778
|
-
throw new BroadcastProviderError(
|
|
779
|
-
`Failed to list channels (audiences): ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
780
|
-
"PROVIDER_ERROR" /* PROVIDER_ERROR */,
|
|
781
|
-
this.name,
|
|
782
|
-
error
|
|
783
|
-
);
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
async getChannel(id) {
|
|
787
|
-
try {
|
|
788
|
-
const response = await this.client.audiences.get(id);
|
|
789
|
-
if (!response.data) {
|
|
790
|
-
throw new BroadcastProviderError(
|
|
791
|
-
`Channel (audience) not found: ${id}`,
|
|
792
|
-
"CHANNEL_NOT_FOUND" /* CHANNEL_NOT_FOUND */,
|
|
793
|
-
this.name
|
|
794
|
-
);
|
|
795
|
-
}
|
|
796
|
-
return {
|
|
797
|
-
id: response.data.id,
|
|
798
|
-
name: response.data.name,
|
|
799
|
-
description: void 0,
|
|
800
|
-
fromName: this.config.fromName || "",
|
|
801
|
-
fromEmail: this.config.fromEmail || "",
|
|
802
|
-
replyTo: this.config.replyTo,
|
|
803
|
-
providerId: response.data.id,
|
|
804
|
-
providerType: "resend",
|
|
805
|
-
subscriberCount: void 0,
|
|
806
|
-
// Not available
|
|
807
|
-
active: true,
|
|
808
|
-
createdAt: new Date(response.data.created_at),
|
|
809
|
-
updatedAt: new Date(response.data.created_at)
|
|
810
|
-
};
|
|
811
|
-
} catch (error) {
|
|
812
|
-
if (error instanceof BroadcastProviderError) throw error;
|
|
813
|
-
throw new BroadcastProviderError(
|
|
814
|
-
`Failed to get channel (audience): ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
815
|
-
"PROVIDER_ERROR" /* PROVIDER_ERROR */,
|
|
816
|
-
this.name,
|
|
817
|
-
error
|
|
818
|
-
);
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
async createChannel(data) {
|
|
822
|
-
try {
|
|
823
|
-
const response = await this.client.audiences.create({
|
|
824
|
-
name: data.name
|
|
825
|
-
});
|
|
826
|
-
if (!response.data) {
|
|
827
|
-
throw new Error("Failed to create audience");
|
|
828
|
-
}
|
|
829
|
-
return {
|
|
830
|
-
id: response.data.id,
|
|
831
|
-
name: response.data.name,
|
|
832
|
-
description: data.description,
|
|
833
|
-
fromName: data.fromName,
|
|
834
|
-
fromEmail: data.fromEmail,
|
|
835
|
-
replyTo: data.replyTo,
|
|
836
|
-
providerId: response.data.id,
|
|
837
|
-
providerType: "resend",
|
|
838
|
-
subscriberCount: 0,
|
|
839
|
-
active: true,
|
|
840
|
-
createdAt: /* @__PURE__ */ new Date(),
|
|
841
|
-
updatedAt: /* @__PURE__ */ new Date()
|
|
842
|
-
};
|
|
843
|
-
} catch (error) {
|
|
844
|
-
throw new BroadcastProviderError(
|
|
845
|
-
`Failed to create channel (audience): ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
846
|
-
"PROVIDER_ERROR" /* PROVIDER_ERROR */,
|
|
847
|
-
this.name,
|
|
848
|
-
error
|
|
849
|
-
);
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
async updateChannel(_id, _data) {
|
|
853
|
-
throw new BroadcastProviderError(
|
|
854
|
-
"Updating channels (audiences) is not supported by Resend API",
|
|
855
|
-
"NOT_SUPPORTED" /* NOT_SUPPORTED */,
|
|
856
|
-
this.name
|
|
857
|
-
);
|
|
858
|
-
}
|
|
859
|
-
async deleteChannel(id) {
|
|
860
|
-
try {
|
|
861
|
-
await this.client.audiences.remove(id);
|
|
862
|
-
} catch (error) {
|
|
863
|
-
throw new BroadcastProviderError(
|
|
864
|
-
`Failed to delete channel (audience): ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
865
|
-
"PROVIDER_ERROR" /* PROVIDER_ERROR */,
|
|
866
|
-
this.name,
|
|
867
|
-
error
|
|
868
|
-
);
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
// Broadcast Management Methods
|
|
872
|
-
async list(_options) {
|
|
873
|
-
throw new BroadcastProviderError(
|
|
874
|
-
"Listing broadcasts is not currently supported by Resend API. This feature may be available in the dashboard only.",
|
|
875
|
-
"NOT_SUPPORTED" /* NOT_SUPPORTED */,
|
|
876
|
-
this.name
|
|
877
|
-
);
|
|
878
|
-
}
|
|
879
|
-
async get(_id) {
|
|
880
|
-
throw new BroadcastProviderError(
|
|
881
|
-
"Getting individual broadcasts is not currently supported by Resend API. This feature may be available in the dashboard only.",
|
|
882
|
-
"NOT_SUPPORTED" /* NOT_SUPPORTED */,
|
|
883
|
-
this.name
|
|
884
|
-
);
|
|
885
|
-
}
|
|
886
|
-
async create(data) {
|
|
887
|
-
try {
|
|
888
|
-
this.validateRequiredFields(data, ["channelId", "name", "subject", "content"]);
|
|
889
|
-
const locale = "en";
|
|
890
|
-
const audienceConfig = this.audienceIds?.[locale];
|
|
891
|
-
const audienceId = this.isDevelopment ? audienceConfig?.development || audienceConfig?.production : audienceConfig?.production || audienceConfig?.development;
|
|
892
|
-
if (!audienceId && data.audienceIds?.length) {
|
|
893
|
-
}
|
|
894
|
-
const resendClient = this.client;
|
|
895
|
-
if (resendClient.broadcasts?.create) {
|
|
896
|
-
const broadcast = await resendClient.broadcasts.create({
|
|
897
|
-
name: data.name,
|
|
898
|
-
subject: data.subject,
|
|
899
|
-
from: `${this.config.fromName || "Newsletter"} <${this.config.fromEmail || "noreply@example.com"}>`,
|
|
900
|
-
reply_to: data.replyTo,
|
|
901
|
-
audience_id: audienceId || data.audienceIds?.[0],
|
|
902
|
-
content: {
|
|
903
|
-
html: data.content
|
|
904
|
-
// TODO: Handle plain text version
|
|
905
|
-
}
|
|
906
|
-
});
|
|
907
|
-
return this.transformResendToBroadcast(broadcast);
|
|
908
|
-
}
|
|
909
|
-
throw new BroadcastProviderError(
|
|
910
|
-
"Creating broadcasts via API is not currently supported. Please check if Resend has released their Broadcasts API.",
|
|
911
|
-
"NOT_SUPPORTED" /* NOT_SUPPORTED */,
|
|
912
|
-
this.name
|
|
913
|
-
);
|
|
914
|
-
} catch (error) {
|
|
915
|
-
if (error instanceof BroadcastProviderError) throw error;
|
|
916
|
-
throw new BroadcastProviderError(
|
|
917
|
-
`Failed to create broadcast: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
918
|
-
"PROVIDER_ERROR" /* PROVIDER_ERROR */,
|
|
919
|
-
this.name,
|
|
920
|
-
error
|
|
921
|
-
);
|
|
922
|
-
}
|
|
923
|
-
}
|
|
924
|
-
async update(_id, _data) {
|
|
925
|
-
throw new BroadcastProviderError(
|
|
926
|
-
"Updating broadcasts is not currently supported by Resend API. Note: Resend broadcasts can only be edited where they were created.",
|
|
927
|
-
"NOT_SUPPORTED" /* NOT_SUPPORTED */,
|
|
928
|
-
this.name
|
|
929
|
-
);
|
|
930
|
-
}
|
|
931
|
-
async delete(_id) {
|
|
932
|
-
throw new BroadcastProviderError(
|
|
933
|
-
"Deleting broadcasts is not currently supported by Resend API.",
|
|
934
|
-
"NOT_SUPPORTED" /* NOT_SUPPORTED */,
|
|
935
|
-
this.name
|
|
936
|
-
);
|
|
937
|
-
}
|
|
938
|
-
async send(id, options) {
|
|
939
|
-
try {
|
|
940
|
-
const resendClient = this.client;
|
|
941
|
-
if (resendClient.broadcasts?.send) {
|
|
942
|
-
await resendClient.broadcasts.send(id, {
|
|
943
|
-
audience_id: options?.audienceIds?.[0]
|
|
944
|
-
// TODO: Handle test mode if supported
|
|
945
|
-
});
|
|
946
|
-
return {
|
|
947
|
-
id,
|
|
948
|
-
channelId: options?.audienceIds?.[0] || "1",
|
|
949
|
-
name: "Unknown",
|
|
950
|
-
subject: "Unknown",
|
|
951
|
-
content: "",
|
|
952
|
-
status: "sending" /* SENDING */,
|
|
953
|
-
trackOpens: true,
|
|
954
|
-
trackClicks: true,
|
|
955
|
-
createdAt: /* @__PURE__ */ new Date(),
|
|
956
|
-
updatedAt: /* @__PURE__ */ new Date(),
|
|
957
|
-
providerType: "resend"
|
|
958
|
-
};
|
|
959
|
-
}
|
|
960
|
-
throw new BroadcastProviderError(
|
|
961
|
-
"Sending broadcasts via API is not currently supported. Please check if Resend has released their Broadcasts API.",
|
|
962
|
-
"NOT_SUPPORTED" /* NOT_SUPPORTED */,
|
|
963
|
-
this.name
|
|
964
|
-
);
|
|
965
|
-
} catch (error) {
|
|
966
|
-
if (error instanceof BroadcastProviderError) throw error;
|
|
967
|
-
throw new BroadcastProviderError(
|
|
968
|
-
`Failed to send broadcast: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
969
|
-
"PROVIDER_ERROR" /* PROVIDER_ERROR */,
|
|
970
|
-
this.name,
|
|
971
|
-
error
|
|
972
|
-
);
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
async schedule(_id, _scheduledAt) {
|
|
976
|
-
throw new BroadcastProviderError(
|
|
977
|
-
"Scheduling broadcasts is not supported by Resend",
|
|
978
|
-
"NOT_SUPPORTED" /* NOT_SUPPORTED */,
|
|
979
|
-
this.name
|
|
980
|
-
);
|
|
981
|
-
}
|
|
982
|
-
async getAnalytics(_id) {
|
|
983
|
-
throw new BroadcastProviderError(
|
|
984
|
-
"Getting broadcast analytics via API is not currently supported. Analytics may be available in the Resend dashboard.",
|
|
985
|
-
"NOT_SUPPORTED" /* NOT_SUPPORTED */,
|
|
986
|
-
this.name
|
|
987
|
-
);
|
|
988
|
-
}
|
|
989
|
-
getCapabilities() {
|
|
990
|
-
return {
|
|
991
|
-
supportsScheduling: false,
|
|
992
|
-
// Not documented
|
|
993
|
-
supportsSegmentation: true,
|
|
994
|
-
// Via Audiences
|
|
995
|
-
supportsAnalytics: true,
|
|
996
|
-
// Available in dashboard, API unclear
|
|
997
|
-
supportsABTesting: false,
|
|
998
|
-
supportsTemplates: false,
|
|
999
|
-
// Not clear from docs
|
|
1000
|
-
supportsPersonalization: true,
|
|
1001
|
-
// Via merge tags
|
|
1002
|
-
supportsMultipleChannels: true,
|
|
1003
|
-
// Via multiple audiences
|
|
1004
|
-
supportsChannelSegmentation: false,
|
|
1005
|
-
// Not within a single audience
|
|
1006
|
-
editableStatuses: [],
|
|
1007
|
-
// Unclear which statuses can be edited
|
|
1008
|
-
supportedContentTypes: ["html"]
|
|
1009
|
-
// React components via SDK
|
|
1010
|
-
};
|
|
1011
|
-
}
|
|
1012
|
-
async validateConfiguration() {
|
|
1013
|
-
try {
|
|
1014
|
-
const resendClient = this.client;
|
|
1015
|
-
if (resendClient.audiences?.list) {
|
|
1016
|
-
await resendClient.audiences.list({ limit: 1 });
|
|
1017
|
-
return true;
|
|
1018
|
-
}
|
|
1019
|
-
await this.client.emails.send({
|
|
1020
|
-
from: "onboarding@resend.dev",
|
|
1021
|
-
to: "delivered@resend.dev",
|
|
1022
|
-
subject: "Configuration Test",
|
|
1023
|
-
html: "<p>Testing configuration</p>"
|
|
1024
|
-
});
|
|
1025
|
-
return true;
|
|
1026
|
-
} catch {
|
|
1027
|
-
return false;
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
1030
|
-
/**
|
|
1031
|
-
* Transform Resend broadcast to our Broadcast type
|
|
1032
|
-
* NOTE: This is speculative based on what the API might return
|
|
1033
|
-
*/
|
|
1034
|
-
transformResendToBroadcast(broadcast) {
|
|
1035
|
-
return {
|
|
1036
|
-
id: broadcast.id,
|
|
1037
|
-
channelId: broadcast.audience_id || "1",
|
|
1038
|
-
// Map audience_id to channelId
|
|
1039
|
-
name: broadcast.name || "Untitled",
|
|
1040
|
-
subject: broadcast.subject,
|
|
1041
|
-
preheader: broadcast.preheader,
|
|
1042
|
-
content: broadcast.content?.html || broadcast.html || "",
|
|
1043
|
-
status: this.mapResendStatus(broadcast.status),
|
|
1044
|
-
trackOpens: true,
|
|
1045
|
-
// Resend tracks by default
|
|
1046
|
-
trackClicks: true,
|
|
1047
|
-
// Resend tracks by default
|
|
510
|
+
status: this.mapBroadcastStatus(broadcast.status),
|
|
511
|
+
trackOpens: broadcast.track_opens,
|
|
512
|
+
trackClicks: broadcast.track_clicks,
|
|
1048
513
|
replyTo: broadcast.reply_to,
|
|
1049
|
-
recipientCount: broadcast.
|
|
514
|
+
recipientCount: broadcast.total_recipients,
|
|
1050
515
|
sentAt: broadcast.sent_at ? new Date(broadcast.sent_at) : void 0,
|
|
1051
|
-
scheduledAt: broadcast.
|
|
1052
|
-
createdAt: new Date(broadcast.created_at
|
|
1053
|
-
updatedAt: new Date(broadcast.updated_at
|
|
516
|
+
scheduledAt: broadcast.scheduled_send_at ? new Date(broadcast.scheduled_send_at) : void 0,
|
|
517
|
+
createdAt: new Date(broadcast.created_at),
|
|
518
|
+
updatedAt: new Date(broadcast.updated_at),
|
|
1054
519
|
providerData: { broadcast },
|
|
1055
|
-
providerId: broadcast.id,
|
|
1056
|
-
providerType: "
|
|
520
|
+
providerId: broadcast.id.toString(),
|
|
521
|
+
providerType: "broadcast"
|
|
1057
522
|
};
|
|
1058
523
|
}
|
|
1059
|
-
|
|
1060
|
-
if (!status) return "draft" /* DRAFT */;
|
|
524
|
+
mapBroadcastStatus(status) {
|
|
1061
525
|
const statusMap = {
|
|
1062
526
|
"draft": "draft" /* DRAFT */,
|
|
1063
527
|
"scheduled": "scheduled" /* SCHEDULED */,
|
|
528
|
+
"queueing": "sending" /* SENDING */,
|
|
1064
529
|
"sending": "sending" /* SENDING */,
|
|
1065
530
|
"sent": "sent" /* SENT */,
|
|
1066
|
-
"failed": "failed" /* FAILED
|
|
531
|
+
"failed": "failed" /* FAILED */,
|
|
532
|
+
"partial_failure": "failed" /* FAILED */,
|
|
533
|
+
"paused": "paused" /* PAUSED */,
|
|
534
|
+
"aborted": "canceled" /* CANCELED */
|
|
1067
535
|
};
|
|
1068
|
-
return statusMap[status
|
|
536
|
+
return statusMap[status] || "draft" /* DRAFT */;
|
|
1069
537
|
}
|
|
1070
538
|
};
|
|
1071
539
|
}
|
|
@@ -1752,19 +1220,12 @@ var createNewsletterSettingsGlobal = (pluginConfig) => {
|
|
|
1752
1220
|
}
|
|
1753
1221
|
},
|
|
1754
1222
|
{
|
|
1755
|
-
name: "
|
|
1756
|
-
type: "text",
|
|
1757
|
-
label: "Production Token",
|
|
1758
|
-
admin: {
|
|
1759
|
-
description: "Token for production environment"
|
|
1760
|
-
}
|
|
1761
|
-
},
|
|
1762
|
-
{
|
|
1763
|
-
name: "developmentToken",
|
|
1223
|
+
name: "token",
|
|
1764
1224
|
type: "text",
|
|
1765
|
-
label: "
|
|
1225
|
+
label: "API Token",
|
|
1226
|
+
required: true,
|
|
1766
1227
|
admin: {
|
|
1767
|
-
description: "
|
|
1228
|
+
description: "Your Broadcast API token"
|
|
1768
1229
|
}
|
|
1769
1230
|
}
|
|
1770
1231
|
]
|
|
@@ -2090,10 +1551,10 @@ var ResendProvider = class {
|
|
|
2090
1551
|
var BroadcastProvider = class {
|
|
2091
1552
|
constructor(config) {
|
|
2092
1553
|
this.apiUrl = config.apiUrl.replace(/\/$/, "");
|
|
2093
|
-
this.
|
|
2094
|
-
this.token = this.isDevelopment ? config.tokens.development || config.tokens.production || "" : config.tokens.production || config.tokens.development || "";
|
|
1554
|
+
this.token = config.token;
|
|
2095
1555
|
this.fromAddress = config.fromAddress;
|
|
2096
1556
|
this.fromName = config.fromName;
|
|
1557
|
+
this.replyTo = config.replyTo;
|
|
2097
1558
|
}
|
|
2098
1559
|
getProvider() {
|
|
2099
1560
|
return "broadcast";
|
|
@@ -2116,7 +1577,7 @@ var BroadcastProvider = class {
|
|
|
2116
1577
|
// Broadcast API expects a single recipient for transactional emails
|
|
2117
1578
|
subject: params.subject,
|
|
2118
1579
|
body: params.html || params.text || "",
|
|
2119
|
-
reply_to: params.replyTo || from.email
|
|
1580
|
+
reply_to: params.replyTo || this.replyTo || from.email
|
|
2120
1581
|
})
|
|
2121
1582
|
});
|
|
2122
1583
|
if (!response.ok) {
|
|
@@ -3293,8 +2754,7 @@ var createSendBroadcastEndpoint = (config, collectionSlug) => {
|
|
|
3293
2754
|
error: auth.error
|
|
3294
2755
|
}, { status: 401 });
|
|
3295
2756
|
}
|
|
3296
|
-
|
|
3297
|
-
if (!broadcastProvider) {
|
|
2757
|
+
if (!config.features?.newsletterManagement?.enabled) {
|
|
3298
2758
|
return Response.json({
|
|
3299
2759
|
success: false,
|
|
3300
2760
|
error: "Broadcast management is not enabled"
|
|
@@ -3321,7 +2781,16 @@ var createSendBroadcastEndpoint = (config, collectionSlug) => {
|
|
|
3321
2781
|
error: "Broadcast not found or not synced with provider"
|
|
3322
2782
|
}, { status: 404 });
|
|
3323
2783
|
}
|
|
3324
|
-
const
|
|
2784
|
+
const providerConfig = config.providers?.broadcast;
|
|
2785
|
+
if (!providerConfig) {
|
|
2786
|
+
return Response.json({
|
|
2787
|
+
success: false,
|
|
2788
|
+
error: "Broadcast provider not configured"
|
|
2789
|
+
}, { status: 500 });
|
|
2790
|
+
}
|
|
2791
|
+
const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
|
|
2792
|
+
const provider = new BroadcastApiProvider2(providerConfig);
|
|
2793
|
+
const broadcast = await provider.send(broadcastDoc.providerId, data);
|
|
3325
2794
|
await req.payload.update({
|
|
3326
2795
|
collection: collectionSlug,
|
|
3327
2796
|
id,
|
|
@@ -3369,8 +2838,7 @@ var createScheduleBroadcastEndpoint = (config, collectionSlug) => {
|
|
|
3369
2838
|
error: auth.error
|
|
3370
2839
|
}, { status: 401 });
|
|
3371
2840
|
}
|
|
3372
|
-
|
|
3373
|
-
if (!broadcastProvider) {
|
|
2841
|
+
if (!config.features?.newsletterManagement?.enabled) {
|
|
3374
2842
|
return Response.json({
|
|
3375
2843
|
success: false,
|
|
3376
2844
|
error: "Broadcast management is not enabled"
|
|
@@ -3417,7 +2885,16 @@ var createScheduleBroadcastEndpoint = (config, collectionSlug) => {
|
|
|
3417
2885
|
error: "Broadcast not found or not synced with provider"
|
|
3418
2886
|
}, { status: 404 });
|
|
3419
2887
|
}
|
|
3420
|
-
const
|
|
2888
|
+
const providerConfig = config.providers?.broadcast;
|
|
2889
|
+
if (!providerConfig) {
|
|
2890
|
+
return Response.json({
|
|
2891
|
+
success: false,
|
|
2892
|
+
error: "Broadcast provider not configured"
|
|
2893
|
+
}, { status: 500 });
|
|
2894
|
+
}
|
|
2895
|
+
const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
|
|
2896
|
+
const provider = new BroadcastApiProvider2(providerConfig);
|
|
2897
|
+
const broadcast = await provider.schedule(broadcastDoc.providerId, scheduledDate);
|
|
3421
2898
|
await req.payload.update({
|
|
3422
2899
|
collection: collectionSlug,
|
|
3423
2900
|
id,
|
|
@@ -3696,11 +3173,6 @@ var createTestBroadcastEndpoint = (config, collectionSlug) => {
|
|
|
3696
3173
|
error: "Broadcast not found"
|
|
3697
3174
|
}, { status: 404 });
|
|
3698
3175
|
}
|
|
3699
|
-
const channel = await req.payload.findByID({
|
|
3700
|
-
collection: "channels",
|
|
3701
|
-
id: typeof broadcast.channel === "string" ? broadcast.channel : broadcast.channel.id,
|
|
3702
|
-
user: auth.user
|
|
3703
|
-
});
|
|
3704
3176
|
const htmlContent = await convertToEmailSafeHtml(broadcast.content, {
|
|
3705
3177
|
wrapInTemplate: true,
|
|
3706
3178
|
preheader: broadcast.preheader
|
|
@@ -3712,11 +3184,15 @@ var createTestBroadcastEndpoint = (config, collectionSlug) => {
|
|
|
3712
3184
|
error: "Email service is not configured"
|
|
3713
3185
|
}, { status: 500 });
|
|
3714
3186
|
}
|
|
3187
|
+
const providerConfig = config.providers.default === "resend" ? config.providers.resend : config.providers.broadcast;
|
|
3188
|
+
const fromEmail = providerConfig?.fromAddress || providerConfig?.fromEmail || "noreply@example.com";
|
|
3189
|
+
const fromName = providerConfig?.fromName || "Newsletter";
|
|
3190
|
+
const replyTo = broadcast.settings?.replyTo || providerConfig?.replyTo;
|
|
3715
3191
|
await emailService.send({
|
|
3716
3192
|
to: testEmail,
|
|
3717
|
-
from:
|
|
3718
|
-
fromName
|
|
3719
|
-
replyTo
|
|
3193
|
+
from: fromEmail,
|
|
3194
|
+
fromName,
|
|
3195
|
+
replyTo,
|
|
3720
3196
|
subject: `[TEST] ${broadcast.subject}`,
|
|
3721
3197
|
html: htmlContent,
|
|
3722
3198
|
trackOpens: false,
|
|
@@ -3964,7 +3440,7 @@ var createUnsubscribeSyncJob = (pluginConfig) => {
|
|
|
3964
3440
|
throw new Error("Broadcast configuration not found");
|
|
3965
3441
|
}
|
|
3966
3442
|
const apiUrl = broadcastConfig.apiUrl.replace(/\/$/, "");
|
|
3967
|
-
const token =
|
|
3443
|
+
const token = broadcastConfig.token;
|
|
3968
3444
|
let page = 1;
|
|
3969
3445
|
let hasMore = true;
|
|
3970
3446
|
while (hasMore) {
|
|
@@ -4044,216 +3520,6 @@ var createUnsubscribeSyncJob = (pluginConfig) => {
|
|
|
4044
3520
|
};
|
|
4045
3521
|
};
|
|
4046
3522
|
|
|
4047
|
-
// src/collections/Channels.ts
|
|
4048
|
-
var createChannelsCollection = (pluginConfig) => {
|
|
4049
|
-
const hasProviders = !!(pluginConfig.providers?.broadcast || pluginConfig.providers?.resend);
|
|
4050
|
-
return {
|
|
4051
|
-
slug: "channels",
|
|
4052
|
-
labels: {
|
|
4053
|
-
singular: "Channel",
|
|
4054
|
-
plural: "Channels"
|
|
4055
|
-
},
|
|
4056
|
-
admin: {
|
|
4057
|
-
useAsTitle: "name",
|
|
4058
|
-
description: "Newsletter channels/publications that can send broadcasts",
|
|
4059
|
-
defaultColumns: ["name", "fromEmail", "subscriberCount", "active"]
|
|
4060
|
-
},
|
|
4061
|
-
fields: [
|
|
4062
|
-
{
|
|
4063
|
-
name: "name",
|
|
4064
|
-
type: "text",
|
|
4065
|
-
required: true,
|
|
4066
|
-
admin: {
|
|
4067
|
-
description: "The name of this newsletter channel"
|
|
4068
|
-
}
|
|
4069
|
-
},
|
|
4070
|
-
{
|
|
4071
|
-
name: "description",
|
|
4072
|
-
type: "textarea",
|
|
4073
|
-
admin: {
|
|
4074
|
-
description: "A brief description of what this channel is about"
|
|
4075
|
-
}
|
|
4076
|
-
},
|
|
4077
|
-
{
|
|
4078
|
-
name: "fromName",
|
|
4079
|
-
type: "text",
|
|
4080
|
-
required: true,
|
|
4081
|
-
admin: {
|
|
4082
|
-
description: "The sender name that appears in emails"
|
|
4083
|
-
}
|
|
4084
|
-
},
|
|
4085
|
-
{
|
|
4086
|
-
name: "fromEmail",
|
|
4087
|
-
type: "email",
|
|
4088
|
-
required: true,
|
|
4089
|
-
admin: {
|
|
4090
|
-
description: "The sender email address"
|
|
4091
|
-
}
|
|
4092
|
-
},
|
|
4093
|
-
{
|
|
4094
|
-
name: "replyTo",
|
|
4095
|
-
type: "email",
|
|
4096
|
-
admin: {
|
|
4097
|
-
description: "Reply-to email address (optional)"
|
|
4098
|
-
}
|
|
4099
|
-
},
|
|
4100
|
-
{
|
|
4101
|
-
name: "providerType",
|
|
4102
|
-
type: "select",
|
|
4103
|
-
required: true,
|
|
4104
|
-
options: [
|
|
4105
|
-
...pluginConfig.providers?.broadcast ? [{ label: "Broadcast", value: "broadcast" }] : [],
|
|
4106
|
-
...pluginConfig.providers?.resend ? [{ label: "Resend", value: "resend" }] : []
|
|
4107
|
-
],
|
|
4108
|
-
admin: {
|
|
4109
|
-
description: "Which email provider manages this channel"
|
|
4110
|
-
}
|
|
4111
|
-
},
|
|
4112
|
-
{
|
|
4113
|
-
name: "providerId",
|
|
4114
|
-
type: "text",
|
|
4115
|
-
admin: {
|
|
4116
|
-
readOnly: true,
|
|
4117
|
-
description: "ID from the email provider",
|
|
4118
|
-
condition: (data) => hasProviders && data?.providerId
|
|
4119
|
-
}
|
|
4120
|
-
},
|
|
4121
|
-
{
|
|
4122
|
-
name: "subscriberCount",
|
|
4123
|
-
type: "number",
|
|
4124
|
-
admin: {
|
|
4125
|
-
readOnly: true,
|
|
4126
|
-
description: "Number of active subscribers"
|
|
4127
|
-
},
|
|
4128
|
-
defaultValue: 0
|
|
4129
|
-
},
|
|
4130
|
-
{
|
|
4131
|
-
name: "active",
|
|
4132
|
-
type: "checkbox",
|
|
4133
|
-
defaultValue: true,
|
|
4134
|
-
admin: {
|
|
4135
|
-
description: "Whether this channel is currently active"
|
|
4136
|
-
}
|
|
4137
|
-
},
|
|
4138
|
-
{
|
|
4139
|
-
name: "settings",
|
|
4140
|
-
type: "group",
|
|
4141
|
-
fields: [
|
|
4142
|
-
{
|
|
4143
|
-
name: "defaultTrackOpens",
|
|
4144
|
-
type: "checkbox",
|
|
4145
|
-
defaultValue: true,
|
|
4146
|
-
admin: {
|
|
4147
|
-
description: "Track email opens by default for broadcasts in this channel"
|
|
4148
|
-
}
|
|
4149
|
-
},
|
|
4150
|
-
{
|
|
4151
|
-
name: "defaultTrackClicks",
|
|
4152
|
-
type: "checkbox",
|
|
4153
|
-
defaultValue: true,
|
|
4154
|
-
admin: {
|
|
4155
|
-
description: "Track link clicks by default for broadcasts in this channel"
|
|
4156
|
-
}
|
|
4157
|
-
},
|
|
4158
|
-
{
|
|
4159
|
-
name: "requireDoubleOptIn",
|
|
4160
|
-
type: "checkbox",
|
|
4161
|
-
defaultValue: false,
|
|
4162
|
-
admin: {
|
|
4163
|
-
description: "Require double opt-in for new subscribers"
|
|
4164
|
-
}
|
|
4165
|
-
}
|
|
4166
|
-
]
|
|
4167
|
-
}
|
|
4168
|
-
],
|
|
4169
|
-
hooks: {
|
|
4170
|
-
// Sync with provider on create
|
|
4171
|
-
afterChange: [
|
|
4172
|
-
async ({ doc, operation, req }) => {
|
|
4173
|
-
if (!hasProviders || operation !== "create") return doc;
|
|
4174
|
-
try {
|
|
4175
|
-
const provider = await getProvider(doc.providerType, pluginConfig);
|
|
4176
|
-
if (!provider) return doc;
|
|
4177
|
-
const providerChannel = await provider.createChannel({
|
|
4178
|
-
name: doc.name,
|
|
4179
|
-
description: doc.description,
|
|
4180
|
-
fromName: doc.fromName,
|
|
4181
|
-
fromEmail: doc.fromEmail,
|
|
4182
|
-
replyTo: doc.replyTo
|
|
4183
|
-
});
|
|
4184
|
-
await req.payload.update({
|
|
4185
|
-
collection: "channels",
|
|
4186
|
-
id: doc.id,
|
|
4187
|
-
data: {
|
|
4188
|
-
providerId: providerChannel.id,
|
|
4189
|
-
subscriberCount: providerChannel.subscriberCount || 0
|
|
4190
|
-
},
|
|
4191
|
-
req
|
|
4192
|
-
});
|
|
4193
|
-
return {
|
|
4194
|
-
...doc,
|
|
4195
|
-
providerId: providerChannel.id,
|
|
4196
|
-
subscriberCount: providerChannel.subscriberCount || 0
|
|
4197
|
-
};
|
|
4198
|
-
} catch (error) {
|
|
4199
|
-
req.payload.logger.error("Failed to create channel in provider:", error);
|
|
4200
|
-
return doc;
|
|
4201
|
-
}
|
|
4202
|
-
}
|
|
4203
|
-
],
|
|
4204
|
-
// Sync updates with provider
|
|
4205
|
-
beforeChange: [
|
|
4206
|
-
async ({ data, originalDoc, operation, req }) => {
|
|
4207
|
-
if (!hasProviders || !originalDoc?.providerId || operation !== "update") return data;
|
|
4208
|
-
try {
|
|
4209
|
-
const provider = await getProvider(originalDoc.providerType, pluginConfig);
|
|
4210
|
-
if (!provider) return data;
|
|
4211
|
-
const updates = {};
|
|
4212
|
-
if (data.name !== originalDoc.name) updates.name = data.name;
|
|
4213
|
-
if (data.description !== originalDoc.description) updates.description = data.description;
|
|
4214
|
-
if (data.fromName !== originalDoc.fromName) updates.fromName = data.fromName;
|
|
4215
|
-
if (data.fromEmail !== originalDoc.fromEmail) updates.fromEmail = data.fromEmail;
|
|
4216
|
-
if (data.replyTo !== originalDoc.replyTo) updates.replyTo = data.replyTo;
|
|
4217
|
-
if (Object.keys(updates).length > 0) {
|
|
4218
|
-
await provider.updateChannel(originalDoc.providerId, updates);
|
|
4219
|
-
}
|
|
4220
|
-
} catch (error) {
|
|
4221
|
-
req.payload.logger.error("Failed to update channel in provider:", error);
|
|
4222
|
-
}
|
|
4223
|
-
return data;
|
|
4224
|
-
}
|
|
4225
|
-
],
|
|
4226
|
-
// Handle deletion
|
|
4227
|
-
afterDelete: [
|
|
4228
|
-
async ({ doc, req }) => {
|
|
4229
|
-
if (!hasProviders || !doc?.providerId) return doc;
|
|
4230
|
-
try {
|
|
4231
|
-
const provider = await getProvider(doc.providerType, pluginConfig);
|
|
4232
|
-
if (!provider) return doc;
|
|
4233
|
-
await provider.deleteChannel(doc.providerId);
|
|
4234
|
-
} catch (error) {
|
|
4235
|
-
req.payload.logger.error("Failed to delete channel from provider:", error);
|
|
4236
|
-
}
|
|
4237
|
-
return doc;
|
|
4238
|
-
}
|
|
4239
|
-
]
|
|
4240
|
-
}
|
|
4241
|
-
};
|
|
4242
|
-
};
|
|
4243
|
-
async function getProvider(providerType, config) {
|
|
4244
|
-
if (providerType === "broadcast") {
|
|
4245
|
-
const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
|
|
4246
|
-
const providerConfig = config.providers?.broadcast;
|
|
4247
|
-
return providerConfig ? new BroadcastApiProvider2(providerConfig) : null;
|
|
4248
|
-
}
|
|
4249
|
-
if (providerType === "resend") {
|
|
4250
|
-
const { ResendBroadcastProvider: ResendBroadcastProvider2 } = await Promise.resolve().then(() => (init_broadcast3(), broadcast_exports2));
|
|
4251
|
-
const providerConfig = config.providers?.resend;
|
|
4252
|
-
return providerConfig ? new ResendBroadcastProvider2(providerConfig) : null;
|
|
4253
|
-
}
|
|
4254
|
-
return null;
|
|
4255
|
-
}
|
|
4256
|
-
|
|
4257
3523
|
// src/collections/Broadcasts.ts
|
|
4258
3524
|
init_types();
|
|
4259
3525
|
|
|
@@ -4317,18 +3583,9 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
4317
3583
|
admin: {
|
|
4318
3584
|
useAsTitle: "name",
|
|
4319
3585
|
description: "Individual email campaigns sent to subscribers",
|
|
4320
|
-
defaultColumns: ["name", "subject", "status", "
|
|
3586
|
+
defaultColumns: ["name", "subject", "status", "sentAt", "actions"]
|
|
4321
3587
|
},
|
|
4322
3588
|
fields: [
|
|
4323
|
-
{
|
|
4324
|
-
name: "channel",
|
|
4325
|
-
type: "relationship",
|
|
4326
|
-
relationTo: "channels",
|
|
4327
|
-
required: true,
|
|
4328
|
-
admin: {
|
|
4329
|
-
description: "The channel this broadcast belongs to"
|
|
4330
|
-
}
|
|
4331
|
-
},
|
|
4332
3589
|
{
|
|
4333
3590
|
name: "name",
|
|
4334
3591
|
type: "text",
|
|
@@ -4540,23 +3797,22 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
4540
3797
|
async ({ doc, operation, req }) => {
|
|
4541
3798
|
if (!hasProviders || operation !== "create") return doc;
|
|
4542
3799
|
try {
|
|
4543
|
-
const
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
}
|
|
4548
|
-
const
|
|
4549
|
-
|
|
3800
|
+
const providerConfig = pluginConfig.providers?.broadcast;
|
|
3801
|
+
if (!providerConfig) {
|
|
3802
|
+
req.payload.logger.error("Broadcast provider not configured");
|
|
3803
|
+
return doc;
|
|
3804
|
+
}
|
|
3805
|
+
const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
|
|
3806
|
+
const provider = new BroadcastApiProvider2(providerConfig);
|
|
4550
3807
|
const htmlContent = await convertToEmailSafeHtml(doc.content);
|
|
4551
3808
|
const providerBroadcast = await provider.create({
|
|
4552
|
-
channelId: channel.providerId || channel.id,
|
|
4553
3809
|
name: doc.name,
|
|
4554
3810
|
subject: doc.subject,
|
|
4555
3811
|
preheader: doc.preheader,
|
|
4556
3812
|
content: htmlContent,
|
|
4557
3813
|
trackOpens: doc.settings?.trackOpens,
|
|
4558
3814
|
trackClicks: doc.settings?.trackClicks,
|
|
4559
|
-
replyTo: doc.settings?.replyTo,
|
|
3815
|
+
replyTo: doc.settings?.replyTo || providerConfig.replyTo,
|
|
4560
3816
|
audienceIds: doc.audienceIds?.map((a) => a.audienceId)
|
|
4561
3817
|
});
|
|
4562
3818
|
await req.payload.update({
|
|
@@ -4584,14 +3840,13 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
4584
3840
|
async ({ data, originalDoc, operation, req }) => {
|
|
4585
3841
|
if (!hasProviders || !originalDoc?.providerId || operation !== "update") return data;
|
|
4586
3842
|
try {
|
|
4587
|
-
const
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
});
|
|
4593
|
-
const provider =
|
|
4594
|
-
if (!provider) return data;
|
|
3843
|
+
const providerConfig = pluginConfig.providers?.broadcast;
|
|
3844
|
+
if (!providerConfig) {
|
|
3845
|
+
req.payload.logger.error("Broadcast provider not configured");
|
|
3846
|
+
return data;
|
|
3847
|
+
}
|
|
3848
|
+
const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
|
|
3849
|
+
const provider = new BroadcastApiProvider2(providerConfig);
|
|
4595
3850
|
const capabilities = provider.getCapabilities();
|
|
4596
3851
|
if (!capabilities.editableStatuses.includes(originalDoc.status)) {
|
|
4597
3852
|
return data;
|
|
@@ -4610,7 +3865,7 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
4610
3865
|
updates.trackClicks = data.settings.trackClicks;
|
|
4611
3866
|
}
|
|
4612
3867
|
if (data.settings?.replyTo !== originalDoc.settings?.replyTo) {
|
|
4613
|
-
updates.replyTo = data.settings.replyTo;
|
|
3868
|
+
updates.replyTo = data.settings.replyTo || providerConfig.replyTo;
|
|
4614
3869
|
}
|
|
4615
3870
|
if (JSON.stringify(data.audienceIds) !== JSON.stringify(originalDoc.audienceIds)) {
|
|
4616
3871
|
updates.audienceIds = data.audienceIds?.map((a) => a.audienceId);
|
|
@@ -4629,13 +3884,13 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
4629
3884
|
async ({ doc, req }) => {
|
|
4630
3885
|
if (!hasProviders || !doc?.providerId) return doc;
|
|
4631
3886
|
try {
|
|
4632
|
-
const
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
}
|
|
4637
|
-
const
|
|
4638
|
-
|
|
3887
|
+
const providerConfig = pluginConfig.providers?.broadcast;
|
|
3888
|
+
if (!providerConfig) {
|
|
3889
|
+
req.payload.logger.error("Broadcast provider not configured");
|
|
3890
|
+
return doc;
|
|
3891
|
+
}
|
|
3892
|
+
const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
|
|
3893
|
+
const provider = new BroadcastApiProvider2(providerConfig);
|
|
4639
3894
|
const capabilities = provider.getCapabilities();
|
|
4640
3895
|
if (capabilities.editableStatuses.includes(doc.status)) {
|
|
4641
3896
|
await provider.delete(doc.providerId);
|
|
@@ -4649,23 +3904,223 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
4649
3904
|
}
|
|
4650
3905
|
};
|
|
4651
3906
|
};
|
|
4652
|
-
async function getProvider2(providerType, config) {
|
|
4653
|
-
if (providerType === "broadcast") {
|
|
4654
|
-
const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
|
|
4655
|
-
const providerConfig = config.providers?.broadcast;
|
|
4656
|
-
return providerConfig ? new BroadcastApiProvider2(providerConfig) : null;
|
|
4657
|
-
}
|
|
4658
|
-
if (providerType === "resend") {
|
|
4659
|
-
const { ResendBroadcastProvider: ResendBroadcastProvider2 } = await Promise.resolve().then(() => (init_broadcast3(), broadcast_exports2));
|
|
4660
|
-
const providerConfig = config.providers?.resend;
|
|
4661
|
-
return providerConfig ? new ResendBroadcastProvider2(providerConfig) : null;
|
|
4662
|
-
}
|
|
4663
|
-
return null;
|
|
4664
|
-
}
|
|
4665
3907
|
|
|
4666
3908
|
// src/index.ts
|
|
4667
3909
|
init_broadcast2();
|
|
4668
|
-
|
|
3910
|
+
|
|
3911
|
+
// src/providers/resend/broadcast.ts
|
|
3912
|
+
var import_resend3 = require("resend");
|
|
3913
|
+
init_types();
|
|
3914
|
+
var ResendBroadcastProvider = class extends BaseBroadcastProvider {
|
|
3915
|
+
constructor(config) {
|
|
3916
|
+
super(config);
|
|
3917
|
+
this.name = "resend";
|
|
3918
|
+
this.client = new import_resend3.Resend(config.apiKey);
|
|
3919
|
+
this.audienceIds = config.audienceIds || {};
|
|
3920
|
+
this.isDevelopment = process.env.NODE_ENV !== "production";
|
|
3921
|
+
if (!config.apiKey) {
|
|
3922
|
+
throw new BroadcastProviderError(
|
|
3923
|
+
"Resend API key is required",
|
|
3924
|
+
"CONFIGURATION_ERROR" /* CONFIGURATION_ERROR */,
|
|
3925
|
+
this.name
|
|
3926
|
+
);
|
|
3927
|
+
}
|
|
3928
|
+
}
|
|
3929
|
+
// Broadcast Management Methods
|
|
3930
|
+
async list(_options) {
|
|
3931
|
+
throw new BroadcastProviderError(
|
|
3932
|
+
"Listing broadcasts is not currently supported by Resend API. This feature may be available in the dashboard only.",
|
|
3933
|
+
"NOT_SUPPORTED" /* NOT_SUPPORTED */,
|
|
3934
|
+
this.name
|
|
3935
|
+
);
|
|
3936
|
+
}
|
|
3937
|
+
async get(_id) {
|
|
3938
|
+
throw new BroadcastProviderError(
|
|
3939
|
+
"Getting individual broadcasts is not currently supported by Resend API. This feature may be available in the dashboard only.",
|
|
3940
|
+
"NOT_SUPPORTED" /* NOT_SUPPORTED */,
|
|
3941
|
+
this.name
|
|
3942
|
+
);
|
|
3943
|
+
}
|
|
3944
|
+
async create(data) {
|
|
3945
|
+
try {
|
|
3946
|
+
this.validateRequiredFields(data, ["name", "subject", "content"]);
|
|
3947
|
+
const locale = "en";
|
|
3948
|
+
const audienceConfig = this.audienceIds?.[locale];
|
|
3949
|
+
const audienceId = this.isDevelopment ? audienceConfig?.development || audienceConfig?.production : audienceConfig?.production || audienceConfig?.development;
|
|
3950
|
+
if (!audienceId && data.audienceIds?.length) {
|
|
3951
|
+
}
|
|
3952
|
+
const resendClient = this.client;
|
|
3953
|
+
if (resendClient.broadcasts?.create) {
|
|
3954
|
+
const broadcast = await resendClient.broadcasts.create({
|
|
3955
|
+
name: data.name,
|
|
3956
|
+
subject: data.subject,
|
|
3957
|
+
from: `${this.config.fromName || "Newsletter"} <${this.config.fromEmail || "noreply@example.com"}>`,
|
|
3958
|
+
reply_to: data.replyTo,
|
|
3959
|
+
audience_id: audienceId || data.audienceIds?.[0],
|
|
3960
|
+
content: {
|
|
3961
|
+
html: data.content
|
|
3962
|
+
// TODO: Handle plain text version
|
|
3963
|
+
}
|
|
3964
|
+
});
|
|
3965
|
+
return this.transformResendToBroadcast(broadcast);
|
|
3966
|
+
}
|
|
3967
|
+
throw new BroadcastProviderError(
|
|
3968
|
+
"Creating broadcasts via API is not currently supported. Please check if Resend has released their Broadcasts API.",
|
|
3969
|
+
"NOT_SUPPORTED" /* NOT_SUPPORTED */,
|
|
3970
|
+
this.name
|
|
3971
|
+
);
|
|
3972
|
+
} catch (error) {
|
|
3973
|
+
if (error instanceof BroadcastProviderError) throw error;
|
|
3974
|
+
throw new BroadcastProviderError(
|
|
3975
|
+
`Failed to create broadcast: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
3976
|
+
"PROVIDER_ERROR" /* PROVIDER_ERROR */,
|
|
3977
|
+
this.name,
|
|
3978
|
+
error
|
|
3979
|
+
);
|
|
3980
|
+
}
|
|
3981
|
+
}
|
|
3982
|
+
async update(_id, _data) {
|
|
3983
|
+
throw new BroadcastProviderError(
|
|
3984
|
+
"Updating broadcasts is not currently supported by Resend API. Note: Resend broadcasts can only be edited where they were created.",
|
|
3985
|
+
"NOT_SUPPORTED" /* NOT_SUPPORTED */,
|
|
3986
|
+
this.name
|
|
3987
|
+
);
|
|
3988
|
+
}
|
|
3989
|
+
async delete(_id) {
|
|
3990
|
+
throw new BroadcastProviderError(
|
|
3991
|
+
"Deleting broadcasts is not currently supported by Resend API.",
|
|
3992
|
+
"NOT_SUPPORTED" /* NOT_SUPPORTED */,
|
|
3993
|
+
this.name
|
|
3994
|
+
);
|
|
3995
|
+
}
|
|
3996
|
+
async send(id, options) {
|
|
3997
|
+
try {
|
|
3998
|
+
const resendClient = this.client;
|
|
3999
|
+
if (resendClient.broadcasts?.send) {
|
|
4000
|
+
await resendClient.broadcasts.send(id, {
|
|
4001
|
+
audience_id: options?.audienceIds?.[0]
|
|
4002
|
+
// TODO: Handle test mode if supported
|
|
4003
|
+
});
|
|
4004
|
+
return {
|
|
4005
|
+
id,
|
|
4006
|
+
name: "Unknown",
|
|
4007
|
+
subject: "Unknown",
|
|
4008
|
+
content: "",
|
|
4009
|
+
status: "sending" /* SENDING */,
|
|
4010
|
+
trackOpens: true,
|
|
4011
|
+
trackClicks: true,
|
|
4012
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
4013
|
+
updatedAt: /* @__PURE__ */ new Date(),
|
|
4014
|
+
providerType: "resend"
|
|
4015
|
+
};
|
|
4016
|
+
}
|
|
4017
|
+
throw new BroadcastProviderError(
|
|
4018
|
+
"Sending broadcasts via API is not currently supported. Please check if Resend has released their Broadcasts API.",
|
|
4019
|
+
"NOT_SUPPORTED" /* NOT_SUPPORTED */,
|
|
4020
|
+
this.name
|
|
4021
|
+
);
|
|
4022
|
+
} catch (error) {
|
|
4023
|
+
if (error instanceof BroadcastProviderError) throw error;
|
|
4024
|
+
throw new BroadcastProviderError(
|
|
4025
|
+
`Failed to send broadcast: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
4026
|
+
"PROVIDER_ERROR" /* PROVIDER_ERROR */,
|
|
4027
|
+
this.name,
|
|
4028
|
+
error
|
|
4029
|
+
);
|
|
4030
|
+
}
|
|
4031
|
+
}
|
|
4032
|
+
async schedule(_id, _scheduledAt) {
|
|
4033
|
+
throw new BroadcastProviderError(
|
|
4034
|
+
"Scheduling broadcasts is not supported by Resend",
|
|
4035
|
+
"NOT_SUPPORTED" /* NOT_SUPPORTED */,
|
|
4036
|
+
this.name
|
|
4037
|
+
);
|
|
4038
|
+
}
|
|
4039
|
+
async getAnalytics(_id) {
|
|
4040
|
+
throw new BroadcastProviderError(
|
|
4041
|
+
"Getting broadcast analytics via API is not currently supported. Analytics may be available in the Resend dashboard.",
|
|
4042
|
+
"NOT_SUPPORTED" /* NOT_SUPPORTED */,
|
|
4043
|
+
this.name
|
|
4044
|
+
);
|
|
4045
|
+
}
|
|
4046
|
+
getCapabilities() {
|
|
4047
|
+
return {
|
|
4048
|
+
supportsScheduling: false,
|
|
4049
|
+
// Not documented
|
|
4050
|
+
supportsSegmentation: true,
|
|
4051
|
+
// Via Audiences
|
|
4052
|
+
supportsAnalytics: true,
|
|
4053
|
+
// Available in dashboard, API unclear
|
|
4054
|
+
supportsABTesting: false,
|
|
4055
|
+
supportsTemplates: false,
|
|
4056
|
+
// Not clear from docs
|
|
4057
|
+
supportsPersonalization: true,
|
|
4058
|
+
// Via merge tags
|
|
4059
|
+
supportsMultipleChannels: false,
|
|
4060
|
+
supportsChannelSegmentation: false,
|
|
4061
|
+
editableStatuses: [],
|
|
4062
|
+
// Unclear which statuses can be edited
|
|
4063
|
+
supportedContentTypes: ["html"]
|
|
4064
|
+
// React components via SDK
|
|
4065
|
+
};
|
|
4066
|
+
}
|
|
4067
|
+
async validateConfiguration() {
|
|
4068
|
+
try {
|
|
4069
|
+
const resendClient = this.client;
|
|
4070
|
+
if (resendClient.audiences?.list) {
|
|
4071
|
+
await resendClient.audiences.list({ limit: 1 });
|
|
4072
|
+
return true;
|
|
4073
|
+
}
|
|
4074
|
+
await this.client.emails.send({
|
|
4075
|
+
from: "onboarding@resend.dev",
|
|
4076
|
+
to: "delivered@resend.dev",
|
|
4077
|
+
subject: "Configuration Test",
|
|
4078
|
+
html: "<p>Testing configuration</p>"
|
|
4079
|
+
});
|
|
4080
|
+
return true;
|
|
4081
|
+
} catch {
|
|
4082
|
+
return false;
|
|
4083
|
+
}
|
|
4084
|
+
}
|
|
4085
|
+
/**
|
|
4086
|
+
* Transform Resend broadcast to our Broadcast type
|
|
4087
|
+
* NOTE: This is speculative based on what the API might return
|
|
4088
|
+
*/
|
|
4089
|
+
transformResendToBroadcast(broadcast) {
|
|
4090
|
+
return {
|
|
4091
|
+
id: broadcast.id,
|
|
4092
|
+
name: broadcast.name || "Untitled",
|
|
4093
|
+
subject: broadcast.subject,
|
|
4094
|
+
preheader: broadcast.preheader,
|
|
4095
|
+
content: broadcast.content?.html || broadcast.html || "",
|
|
4096
|
+
status: this.mapResendStatus(broadcast.status),
|
|
4097
|
+
trackOpens: true,
|
|
4098
|
+
// Resend tracks by default
|
|
4099
|
+
trackClicks: true,
|
|
4100
|
+
// Resend tracks by default
|
|
4101
|
+
replyTo: broadcast.reply_to,
|
|
4102
|
+
recipientCount: broadcast.recipient_count,
|
|
4103
|
+
sentAt: broadcast.sent_at ? new Date(broadcast.sent_at) : void 0,
|
|
4104
|
+
scheduledAt: broadcast.scheduled_at ? new Date(broadcast.scheduled_at) : void 0,
|
|
4105
|
+
createdAt: new Date(broadcast.created_at || Date.now()),
|
|
4106
|
+
updatedAt: new Date(broadcast.updated_at || Date.now()),
|
|
4107
|
+
providerData: { broadcast },
|
|
4108
|
+
providerId: broadcast.id,
|
|
4109
|
+
providerType: "resend"
|
|
4110
|
+
};
|
|
4111
|
+
}
|
|
4112
|
+
mapResendStatus(status) {
|
|
4113
|
+
if (!status) return "draft" /* DRAFT */;
|
|
4114
|
+
const statusMap = {
|
|
4115
|
+
"draft": "draft" /* DRAFT */,
|
|
4116
|
+
"scheduled": "scheduled" /* SCHEDULED */,
|
|
4117
|
+
"sending": "sending" /* SENDING */,
|
|
4118
|
+
"sent": "sent" /* SENT */,
|
|
4119
|
+
"failed": "failed" /* FAILED */
|
|
4120
|
+
};
|
|
4121
|
+
return statusMap[status.toLowerCase()] || "draft" /* DRAFT */;
|
|
4122
|
+
}
|
|
4123
|
+
};
|
|
4669
4124
|
|
|
4670
4125
|
// src/utilities/session.ts
|
|
4671
4126
|
var import_jsonwebtoken2 = __toESM(require("jsonwebtoken"), 1);
|
|
@@ -4765,9 +4220,8 @@ var newsletterPlugin = (pluginConfig) => (incomingConfig) => {
|
|
|
4765
4220
|
const settingsGlobal = createNewsletterSettingsGlobal(config);
|
|
4766
4221
|
let collections = [...incomingConfig.collections || [], subscribersCollection];
|
|
4767
4222
|
if (config.features?.newsletterManagement?.enabled) {
|
|
4768
|
-
const channelsCollection = createChannelsCollection(config);
|
|
4769
4223
|
const broadcastsCollection = createBroadcastsCollection(config);
|
|
4770
|
-
collections.push(
|
|
4224
|
+
collections.push(broadcastsCollection);
|
|
4771
4225
|
}
|
|
4772
4226
|
if (config.features?.newsletterScheduling?.enabled) {
|
|
4773
4227
|
const targetCollections = config.features.newsletterScheduling.collections || "articles";
|
|
@@ -4848,10 +4302,10 @@ var newsletterPlugin = (pluginConfig) => (incomingConfig) => {
|
|
|
4848
4302
|
} : config.providers.resend,
|
|
4849
4303
|
broadcast: settings.provider === "broadcast" ? {
|
|
4850
4304
|
apiUrl: settings.broadcastSettings?.apiUrl || config.providers.broadcast?.apiUrl || "",
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4305
|
+
token: settings.broadcastSettings?.token || config.providers.broadcast?.token || "",
|
|
4306
|
+
fromAddress: settings.fromAddress || config.providers.broadcast?.fromAddress,
|
|
4307
|
+
fromName: settings.fromName || config.providers.broadcast?.fromName,
|
|
4308
|
+
replyTo: settings.replyTo || config.providers.broadcast?.replyTo
|
|
4855
4309
|
} : config.providers.broadcast
|
|
4856
4310
|
};
|
|
4857
4311
|
} else {
|