payload-plugin-newsletter 0.16.5 → 0.16.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +45 -0
- package/dist/collections.cjs +184 -134
- package/dist/collections.cjs.map +1 -1
- package/dist/collections.js +184 -134
- package/dist/collections.js.map +1 -1
- package/dist/index.cjs +184 -134
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +184 -134
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -240,29 +240,46 @@ var init_broadcast2 = __esm({
|
|
|
240
240
|
async create(data) {
|
|
241
241
|
try {
|
|
242
242
|
this.validateRequiredFields(data, ["name", "subject", "content"]);
|
|
243
|
+
const requestBody = {
|
|
244
|
+
broadcast: {
|
|
245
|
+
name: data.name,
|
|
246
|
+
subject: data.subject,
|
|
247
|
+
preheader: data.preheader,
|
|
248
|
+
body: data.content,
|
|
249
|
+
html_body: true,
|
|
250
|
+
track_opens: data.trackOpens ?? true,
|
|
251
|
+
track_clicks: data.trackClicks ?? true,
|
|
252
|
+
reply_to: data.replyTo,
|
|
253
|
+
segment_ids: data.audienceIds
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
console.log("[BroadcastApiProvider] Creating broadcast:", {
|
|
257
|
+
url: `${this.apiUrl}/api/v1/broadcasts`,
|
|
258
|
+
method: "POST",
|
|
259
|
+
hasToken: !!this.token,
|
|
260
|
+
tokenLength: this.token?.length,
|
|
261
|
+
body: JSON.stringify(requestBody, null, 2)
|
|
262
|
+
});
|
|
243
263
|
const response = await fetch(`${this.apiUrl}/api/v1/broadcasts`, {
|
|
244
264
|
method: "POST",
|
|
245
265
|
headers: {
|
|
246
266
|
"Authorization": `Bearer ${this.token}`,
|
|
247
267
|
"Content-Type": "application/json"
|
|
248
268
|
},
|
|
249
|
-
body: JSON.stringify(
|
|
250
|
-
broadcast: {
|
|
251
|
-
name: data.name,
|
|
252
|
-
subject: data.subject,
|
|
253
|
-
preheader: data.preheader,
|
|
254
|
-
body: data.content,
|
|
255
|
-
html_body: true,
|
|
256
|
-
track_opens: data.trackOpens ?? true,
|
|
257
|
-
track_clicks: data.trackClicks ?? true,
|
|
258
|
-
reply_to: data.replyTo,
|
|
259
|
-
segment_ids: data.audienceIds
|
|
260
|
-
}
|
|
261
|
-
})
|
|
269
|
+
body: JSON.stringify(requestBody)
|
|
262
270
|
});
|
|
271
|
+
console.log("[BroadcastApiProvider] Response status:", response.status);
|
|
272
|
+
console.log("[BroadcastApiProvider] Response headers:", Object.fromEntries(response.headers.entries()));
|
|
263
273
|
if (!response.ok) {
|
|
264
|
-
const
|
|
265
|
-
|
|
274
|
+
const errorText = await response.text();
|
|
275
|
+
console.error("[BroadcastApiProvider] Error response body:", errorText);
|
|
276
|
+
let errorDetails;
|
|
277
|
+
try {
|
|
278
|
+
errorDetails = JSON.parse(errorText);
|
|
279
|
+
console.error("[BroadcastApiProvider] Parsed error:", errorDetails);
|
|
280
|
+
} catch {
|
|
281
|
+
}
|
|
282
|
+
throw new Error(`Broadcast API error: ${response.status} - ${errorText}`);
|
|
266
283
|
}
|
|
267
284
|
const result = await response.json();
|
|
268
285
|
return this.get(result.id.toString());
|
|
@@ -4372,58 +4389,162 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
4372
4389
|
}
|
|
4373
4390
|
],
|
|
4374
4391
|
hooks: {
|
|
4375
|
-
// Sync with provider on create
|
|
4392
|
+
// Sync with provider on create and update
|
|
4376
4393
|
afterChange: [
|
|
4377
|
-
async ({ doc, operation, req }) => {
|
|
4378
|
-
if (!hasProviders
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4394
|
+
async ({ doc, operation, req, previousDoc }) => {
|
|
4395
|
+
if (!hasProviders) return doc;
|
|
4396
|
+
if (operation === "create") {
|
|
4397
|
+
try {
|
|
4398
|
+
const providerConfig = await getBroadcastConfig(req, pluginConfig);
|
|
4399
|
+
if (!providerConfig || !providerConfig.token) {
|
|
4400
|
+
req.payload.logger.error("Broadcast provider not configured in Newsletter Settings or environment variables");
|
|
4401
|
+
return doc;
|
|
4402
|
+
}
|
|
4403
|
+
const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
|
|
4404
|
+
const provider = new BroadcastApiProvider2(providerConfig);
|
|
4405
|
+
req.payload.logger.info("Converting content to HTML...");
|
|
4406
|
+
const htmlContent = await convertToEmailSafeHtml(doc.contentSection?.content);
|
|
4407
|
+
const createData = {
|
|
4408
|
+
name: doc.subject,
|
|
4409
|
+
// Use subject as name since we removed the name field
|
|
4410
|
+
subject: doc.subject,
|
|
4411
|
+
preheader: doc.contentSection?.preheader,
|
|
4412
|
+
content: htmlContent,
|
|
4413
|
+
trackOpens: doc.settings?.trackOpens,
|
|
4414
|
+
trackClicks: doc.settings?.trackClicks,
|
|
4415
|
+
replyTo: doc.settings?.replyTo || providerConfig.replyTo,
|
|
4416
|
+
audienceIds: doc.audienceIds?.map((a) => a.audienceId)
|
|
4417
|
+
};
|
|
4418
|
+
req.payload.logger.info("Creating broadcast with data:", {
|
|
4419
|
+
name: createData.name,
|
|
4420
|
+
subject: createData.subject,
|
|
4421
|
+
preheader: createData.preheader || "NONE",
|
|
4422
|
+
contentLength: htmlContent ? htmlContent.length : 0,
|
|
4423
|
+
contentPreview: htmlContent ? htmlContent.substring(0, 100) + "..." : "EMPTY",
|
|
4424
|
+
trackOpens: createData.trackOpens,
|
|
4425
|
+
trackClicks: createData.trackClicks,
|
|
4426
|
+
replyTo: createData.replyTo,
|
|
4427
|
+
audienceIds: createData.audienceIds || [],
|
|
4428
|
+
apiUrl: providerConfig.apiUrl,
|
|
4429
|
+
hasToken: !!providerConfig.token
|
|
4430
|
+
});
|
|
4431
|
+
const providerBroadcast = await provider.create(createData);
|
|
4432
|
+
await req.payload.update({
|
|
4433
|
+
collection: "broadcasts",
|
|
4434
|
+
id: doc.id,
|
|
4435
|
+
data: {
|
|
4436
|
+
providerId: providerBroadcast.id,
|
|
4437
|
+
providerData: providerBroadcast.providerData
|
|
4438
|
+
},
|
|
4439
|
+
req
|
|
4440
|
+
});
|
|
4441
|
+
return {
|
|
4442
|
+
...doc,
|
|
4403
4443
|
providerId: providerBroadcast.id,
|
|
4404
4444
|
providerData: providerBroadcast.providerData
|
|
4405
|
-
}
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4445
|
+
};
|
|
4446
|
+
} catch (error) {
|
|
4447
|
+
req.payload.logger.error("Raw error from broadcast provider:");
|
|
4448
|
+
req.payload.logger.error(error);
|
|
4449
|
+
if (error instanceof Error) {
|
|
4450
|
+
req.payload.logger.error("Error is instance of Error:", {
|
|
4451
|
+
message: error.message,
|
|
4452
|
+
stack: error.stack,
|
|
4453
|
+
name: error.name,
|
|
4454
|
+
// If it's a BroadcastProviderError, it might have additional details
|
|
4455
|
+
...error.details,
|
|
4456
|
+
// Check if it's a fetch response error
|
|
4457
|
+
...error.response,
|
|
4458
|
+
...error.data,
|
|
4459
|
+
...error.status,
|
|
4460
|
+
...error.statusText
|
|
4461
|
+
});
|
|
4462
|
+
} else if (typeof error === "string") {
|
|
4463
|
+
req.payload.logger.error("Error is a string:", error);
|
|
4464
|
+
} else if (error && typeof error === "object") {
|
|
4465
|
+
req.payload.logger.error("Error is an object:", JSON.stringify(error, null, 2));
|
|
4466
|
+
} else {
|
|
4467
|
+
req.payload.logger.error("Unknown error type:", typeof error);
|
|
4468
|
+
}
|
|
4469
|
+
req.payload.logger.error("Failed broadcast document:", {
|
|
4470
|
+
id: doc.id,
|
|
4471
|
+
subject: doc.subject,
|
|
4472
|
+
hasContent: !!doc.contentSection?.content,
|
|
4473
|
+
contentType: doc.contentSection?.content ? typeof doc.contentSection.content : "none"
|
|
4421
4474
|
});
|
|
4422
|
-
|
|
4423
|
-
req.payload.logger.error("Failed to create broadcast in provider:", error);
|
|
4475
|
+
return doc;
|
|
4424
4476
|
}
|
|
4425
|
-
return doc;
|
|
4426
4477
|
}
|
|
4478
|
+
if (operation === "update" && doc.providerId) {
|
|
4479
|
+
req.payload.logger.info("Broadcast afterChange update hook triggered", {
|
|
4480
|
+
operation,
|
|
4481
|
+
hasProviderId: !!doc.providerId,
|
|
4482
|
+
sendStatus: doc.sendStatus,
|
|
4483
|
+
publishStatus: doc._status
|
|
4484
|
+
});
|
|
4485
|
+
try {
|
|
4486
|
+
const providerConfig = await getBroadcastConfig(req, pluginConfig);
|
|
4487
|
+
if (!providerConfig || !providerConfig.token) {
|
|
4488
|
+
req.payload.logger.error("Broadcast provider not configured in Newsletter Settings or environment variables");
|
|
4489
|
+
return doc;
|
|
4490
|
+
}
|
|
4491
|
+
const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
|
|
4492
|
+
const provider = new BroadcastApiProvider2(providerConfig);
|
|
4493
|
+
const capabilities = provider.getCapabilities();
|
|
4494
|
+
const sendStatus = doc.sendStatus || "draft" /* DRAFT */;
|
|
4495
|
+
if (!capabilities.editableStatuses.includes(sendStatus)) {
|
|
4496
|
+
req.payload.logger.info(`Skipping sync for broadcast in status: ${sendStatus}`);
|
|
4497
|
+
return doc;
|
|
4498
|
+
}
|
|
4499
|
+
const contentChanged = doc.subject !== previousDoc?.subject || doc.contentSection?.preheader !== previousDoc?.contentSection?.preheader || JSON.stringify(doc.contentSection?.content) !== JSON.stringify(previousDoc?.contentSection?.content) || doc.settings?.trackOpens !== previousDoc?.settings?.trackOpens || doc.settings?.trackClicks !== previousDoc?.settings?.trackClicks || doc.settings?.replyTo !== previousDoc?.settings?.replyTo || JSON.stringify(doc.audienceIds) !== JSON.stringify(previousDoc?.audienceIds);
|
|
4500
|
+
if (contentChanged) {
|
|
4501
|
+
const updates = {};
|
|
4502
|
+
if (doc.subject !== previousDoc?.subject) {
|
|
4503
|
+
updates.name = doc.subject;
|
|
4504
|
+
updates.subject = doc.subject;
|
|
4505
|
+
}
|
|
4506
|
+
if (doc.contentSection?.preheader !== previousDoc?.contentSection?.preheader) {
|
|
4507
|
+
updates.preheader = doc.contentSection?.preheader;
|
|
4508
|
+
}
|
|
4509
|
+
if (JSON.stringify(doc.contentSection?.content) !== JSON.stringify(previousDoc?.contentSection?.content)) {
|
|
4510
|
+
updates.content = await convertToEmailSafeHtml(doc.contentSection?.content);
|
|
4511
|
+
}
|
|
4512
|
+
if (doc.settings?.trackOpens !== previousDoc?.settings?.trackOpens) {
|
|
4513
|
+
updates.trackOpens = doc.settings.trackOpens;
|
|
4514
|
+
}
|
|
4515
|
+
if (doc.settings?.trackClicks !== previousDoc?.settings?.trackClicks) {
|
|
4516
|
+
updates.trackClicks = doc.settings.trackClicks;
|
|
4517
|
+
}
|
|
4518
|
+
if (doc.settings?.replyTo !== previousDoc?.settings?.replyTo) {
|
|
4519
|
+
updates.replyTo = doc.settings.replyTo || providerConfig.replyTo;
|
|
4520
|
+
}
|
|
4521
|
+
if (JSON.stringify(doc.audienceIds) !== JSON.stringify(previousDoc?.audienceIds)) {
|
|
4522
|
+
updates.audienceIds = doc.audienceIds?.map((a) => a.audienceId);
|
|
4523
|
+
}
|
|
4524
|
+
req.payload.logger.info("Syncing broadcast updates to provider", {
|
|
4525
|
+
providerId: doc.providerId,
|
|
4526
|
+
updates
|
|
4527
|
+
});
|
|
4528
|
+
await provider.update(doc.providerId, updates);
|
|
4529
|
+
req.payload.logger.info(`Broadcast ${doc.id} synced to provider successfully`);
|
|
4530
|
+
} else {
|
|
4531
|
+
req.payload.logger.info("No content changes to sync to provider");
|
|
4532
|
+
}
|
|
4533
|
+
} catch (error) {
|
|
4534
|
+
if (error instanceof Error) {
|
|
4535
|
+
req.payload.logger.error("Failed to sync broadcast update to provider:", {
|
|
4536
|
+
message: error.message,
|
|
4537
|
+
stack: error.stack,
|
|
4538
|
+
name: error.name,
|
|
4539
|
+
// If it's a BroadcastProviderError, it might have additional details
|
|
4540
|
+
...error.details
|
|
4541
|
+
});
|
|
4542
|
+
} else {
|
|
4543
|
+
req.payload.logger.error("Failed to sync broadcast update to provider:", error);
|
|
4544
|
+
}
|
|
4545
|
+
}
|
|
4546
|
+
}
|
|
4547
|
+
return doc;
|
|
4427
4548
|
},
|
|
4428
4549
|
// Hook to send when published
|
|
4429
4550
|
async ({ doc, operation, previousDoc, req }) => {
|
|
@@ -4481,79 +4602,8 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
4481
4602
|
return doc;
|
|
4482
4603
|
}
|
|
4483
4604
|
],
|
|
4484
|
-
//
|
|
4485
|
-
beforeChange: [
|
|
4486
|
-
async ({ data, originalDoc, operation, req }) => {
|
|
4487
|
-
if (!hasProviders || !originalDoc?.providerId || operation !== "update") return data;
|
|
4488
|
-
req.payload.logger.info("Broadcast beforeChange update hook triggered", {
|
|
4489
|
-
operation,
|
|
4490
|
-
hasProviderId: !!originalDoc?.providerId,
|
|
4491
|
-
originalSendStatus: originalDoc?.sendStatus,
|
|
4492
|
-
originalPublishStatus: originalDoc?._status,
|
|
4493
|
-
newSendStatus: data?.sendStatus,
|
|
4494
|
-
newPublishStatus: data?._status
|
|
4495
|
-
});
|
|
4496
|
-
try {
|
|
4497
|
-
const providerConfig = await getBroadcastConfig(req, pluginConfig);
|
|
4498
|
-
if (!providerConfig || !providerConfig.token) {
|
|
4499
|
-
req.payload.logger.error("Broadcast provider not configured in Newsletter Settings or environment variables");
|
|
4500
|
-
return data;
|
|
4501
|
-
}
|
|
4502
|
-
const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
|
|
4503
|
-
const provider = new BroadcastApiProvider2(providerConfig);
|
|
4504
|
-
const capabilities = provider.getCapabilities();
|
|
4505
|
-
const sendStatus = originalDoc.sendStatus || "draft" /* DRAFT */;
|
|
4506
|
-
if (!capabilities.editableStatuses.includes(sendStatus)) {
|
|
4507
|
-
req.payload.logger.info(`Skipping sync for broadcast in status: ${sendStatus}`);
|
|
4508
|
-
return data;
|
|
4509
|
-
}
|
|
4510
|
-
const updates = {};
|
|
4511
|
-
if (data.subject !== originalDoc.subject) {
|
|
4512
|
-
updates.name = data.subject;
|
|
4513
|
-
updates.subject = data.subject;
|
|
4514
|
-
}
|
|
4515
|
-
if (data.contentSection?.preheader !== originalDoc.contentSection?.preheader) updates.preheader = data.contentSection?.preheader;
|
|
4516
|
-
if (data.contentSection?.content !== originalDoc.contentSection?.content) {
|
|
4517
|
-
updates.content = await convertToEmailSafeHtml(data.contentSection?.content);
|
|
4518
|
-
}
|
|
4519
|
-
if (data.settings?.trackOpens !== originalDoc.settings?.trackOpens) {
|
|
4520
|
-
updates.trackOpens = data.settings.trackOpens;
|
|
4521
|
-
}
|
|
4522
|
-
if (data.settings?.trackClicks !== originalDoc.settings?.trackClicks) {
|
|
4523
|
-
updates.trackClicks = data.settings.trackClicks;
|
|
4524
|
-
}
|
|
4525
|
-
if (data.settings?.replyTo !== originalDoc.settings?.replyTo) {
|
|
4526
|
-
updates.replyTo = data.settings.replyTo || providerConfig.replyTo;
|
|
4527
|
-
}
|
|
4528
|
-
if (JSON.stringify(data.audienceIds) !== JSON.stringify(originalDoc.audienceIds)) {
|
|
4529
|
-
updates.audienceIds = data.audienceIds?.map((a) => a.audienceId);
|
|
4530
|
-
}
|
|
4531
|
-
if (Object.keys(updates).length > 0) {
|
|
4532
|
-
req.payload.logger.info("Syncing broadcast updates to provider", {
|
|
4533
|
-
providerId: originalDoc.providerId,
|
|
4534
|
-
updates
|
|
4535
|
-
});
|
|
4536
|
-
await provider.update(originalDoc.providerId, updates);
|
|
4537
|
-
req.payload.logger.info("Successfully synced broadcast updates to provider");
|
|
4538
|
-
} else {
|
|
4539
|
-
req.payload.logger.info("No broadcast updates to sync to provider");
|
|
4540
|
-
}
|
|
4541
|
-
} catch (error) {
|
|
4542
|
-
if (error instanceof Error) {
|
|
4543
|
-
req.payload.logger.error("Failed to update broadcast in provider:", {
|
|
4544
|
-
message: error.message,
|
|
4545
|
-
stack: error.stack,
|
|
4546
|
-
name: error.name,
|
|
4547
|
-
// If it's a BroadcastProviderError, it might have additional details
|
|
4548
|
-
...error.details
|
|
4549
|
-
});
|
|
4550
|
-
} else {
|
|
4551
|
-
req.payload.logger.error("Failed to update broadcast in provider:", error);
|
|
4552
|
-
}
|
|
4553
|
-
}
|
|
4554
|
-
return data;
|
|
4555
|
-
}
|
|
4556
|
-
],
|
|
4605
|
+
// beforeChange hooks can be added here if needed
|
|
4606
|
+
beforeChange: [],
|
|
4557
4607
|
// Handle deletion
|
|
4558
4608
|
afterDelete: [
|
|
4559
4609
|
async ({ doc, req }) => {
|