payload-plugin-newsletter 0.17.1 → 0.17.3
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 +25 -0
- package/dist/collections.cjs +432 -62
- package/dist/collections.cjs.map +1 -1
- package/dist/collections.js +432 -62
- package/dist/collections.js.map +1 -1
- package/dist/index.cjs +1017 -1019
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1057 -1059
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,28 @@
|
|
|
1
|
+
## [0.17.3] - 2025-07-30
|
|
2
|
+
|
|
3
|
+
### Fixed
|
|
4
|
+
- **Duplicate Collection Slug in Endpoint Paths** - Fixed broadcast endpoints having duplicate collection slugs
|
|
5
|
+
- Removed collection slug from endpoint paths as Payload automatically prepends it
|
|
6
|
+
- Changed `/${collectionSlug}/preview` to `/preview`
|
|
7
|
+
- Changed `/${collectionSlug}/:id/send` to `/:id/send`
|
|
8
|
+
- Changed `/${collectionSlug}/:id/schedule` to `/:id/schedule`
|
|
9
|
+
- Changed `/${collectionSlug}/:id/test` to `/:id/test`
|
|
10
|
+
- Fixes 404 errors when accessing endpoints from `/api/broadcasts/preview` instead of `/api/broadcasts/broadcasts/preview`
|
|
11
|
+
- Client code can now properly access endpoints at their expected paths
|
|
12
|
+
|
|
13
|
+
## [0.17.2] - 2025-07-29
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- **Broadcast Endpoints Registration** - Fixed endpoints not being accessible in Payload v3
|
|
17
|
+
- Moved broadcast endpoints from global endpoints to collection endpoints
|
|
18
|
+
- Endpoints are now properly registered on the broadcasts collection
|
|
19
|
+
- Fixes 404 errors for all broadcast endpoints (/preview, /test, /send, /schedule)
|
|
20
|
+
- Aligns with Payload v3 architecture where collection endpoints should be defined on the collection
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
- `createBroadcastManagementEndpoints` now returns empty array (kept for backward compatibility)
|
|
24
|
+
- Broadcast endpoints are defined directly in the broadcasts collection configuration
|
|
25
|
+
|
|
1
26
|
## [0.17.1] - 2025-07-29
|
|
2
27
|
|
|
3
28
|
### Fixed
|
package/dist/collections.cjs
CHANGED
|
@@ -31,9 +31,19 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
31
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
32
|
|
|
33
33
|
// src/types/newsletter.ts
|
|
34
|
+
var NewsletterProviderError;
|
|
34
35
|
var init_newsletter = __esm({
|
|
35
36
|
"src/types/newsletter.ts"() {
|
|
36
37
|
"use strict";
|
|
38
|
+
NewsletterProviderError = class extends Error {
|
|
39
|
+
constructor(message, code, provider, details) {
|
|
40
|
+
super(message);
|
|
41
|
+
this.code = code;
|
|
42
|
+
this.provider = provider;
|
|
43
|
+
this.details = details;
|
|
44
|
+
this.name = "NewsletterProviderError";
|
|
45
|
+
}
|
|
46
|
+
};
|
|
37
47
|
}
|
|
38
48
|
});
|
|
39
49
|
|
|
@@ -1241,12 +1251,427 @@ async function getBroadcastConfig(req, pluginConfig) {
|
|
|
1241
1251
|
}
|
|
1242
1252
|
}
|
|
1243
1253
|
|
|
1254
|
+
// src/endpoints/broadcasts/send.ts
|
|
1255
|
+
init_types();
|
|
1256
|
+
|
|
1257
|
+
// src/utils/access.ts
|
|
1258
|
+
var isAdmin = (user, config) => {
|
|
1259
|
+
if (!user || user.collection !== "users") {
|
|
1260
|
+
return false;
|
|
1261
|
+
}
|
|
1262
|
+
if (config?.access?.isAdmin) {
|
|
1263
|
+
return config.access.isAdmin(user);
|
|
1264
|
+
}
|
|
1265
|
+
if (user.roles?.includes("admin")) {
|
|
1266
|
+
return true;
|
|
1267
|
+
}
|
|
1268
|
+
if (user.isAdmin === true) {
|
|
1269
|
+
return true;
|
|
1270
|
+
}
|
|
1271
|
+
if (user.role === "admin") {
|
|
1272
|
+
return true;
|
|
1273
|
+
}
|
|
1274
|
+
if (user.admin === true) {
|
|
1275
|
+
return true;
|
|
1276
|
+
}
|
|
1277
|
+
return false;
|
|
1278
|
+
};
|
|
1279
|
+
var adminOnly = (config) => ({ req }) => {
|
|
1280
|
+
const user = req.user;
|
|
1281
|
+
return isAdmin(user, config);
|
|
1282
|
+
};
|
|
1283
|
+
var adminOrSelf = (config) => ({ req, id }) => {
|
|
1284
|
+
const user = req.user;
|
|
1285
|
+
if (!user) {
|
|
1286
|
+
if (!id) {
|
|
1287
|
+
return {
|
|
1288
|
+
id: {
|
|
1289
|
+
equals: "unauthorized-no-access"
|
|
1290
|
+
}
|
|
1291
|
+
};
|
|
1292
|
+
}
|
|
1293
|
+
return false;
|
|
1294
|
+
}
|
|
1295
|
+
if (isAdmin(user, config)) {
|
|
1296
|
+
return true;
|
|
1297
|
+
}
|
|
1298
|
+
if (user.collection === "subscribers") {
|
|
1299
|
+
if (!id) {
|
|
1300
|
+
return {
|
|
1301
|
+
id: {
|
|
1302
|
+
equals: user.id
|
|
1303
|
+
}
|
|
1304
|
+
};
|
|
1305
|
+
}
|
|
1306
|
+
return id === user.id;
|
|
1307
|
+
}
|
|
1308
|
+
if (!id) {
|
|
1309
|
+
return {
|
|
1310
|
+
id: {
|
|
1311
|
+
equals: "unauthorized-no-access"
|
|
1312
|
+
}
|
|
1313
|
+
};
|
|
1314
|
+
}
|
|
1315
|
+
return false;
|
|
1316
|
+
};
|
|
1317
|
+
|
|
1318
|
+
// src/utils/auth.ts
|
|
1319
|
+
async function getAuthenticatedUser(req) {
|
|
1320
|
+
try {
|
|
1321
|
+
const me = await req.payload.find({
|
|
1322
|
+
collection: "users",
|
|
1323
|
+
where: {
|
|
1324
|
+
id: {
|
|
1325
|
+
equals: "me"
|
|
1326
|
+
// Special value in Payload to get current user
|
|
1327
|
+
}
|
|
1328
|
+
},
|
|
1329
|
+
limit: 1,
|
|
1330
|
+
depth: 0
|
|
1331
|
+
});
|
|
1332
|
+
return me.docs[0] || null;
|
|
1333
|
+
} catch {
|
|
1334
|
+
return null;
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
async function requireAdmin(req, config) {
|
|
1338
|
+
const user = await getAuthenticatedUser(req);
|
|
1339
|
+
if (!user) {
|
|
1340
|
+
return {
|
|
1341
|
+
authorized: false,
|
|
1342
|
+
error: "Authentication required"
|
|
1343
|
+
};
|
|
1344
|
+
}
|
|
1345
|
+
if (!isAdmin(user, config)) {
|
|
1346
|
+
return {
|
|
1347
|
+
authorized: false,
|
|
1348
|
+
error: "Admin access required"
|
|
1349
|
+
};
|
|
1350
|
+
}
|
|
1351
|
+
return {
|
|
1352
|
+
authorized: true,
|
|
1353
|
+
user
|
|
1354
|
+
};
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
// src/endpoints/broadcasts/send.ts
|
|
1358
|
+
var createSendBroadcastEndpoint = (config, collectionSlug) => {
|
|
1359
|
+
return {
|
|
1360
|
+
path: "/:id/send",
|
|
1361
|
+
method: "post",
|
|
1362
|
+
handler: async (req) => {
|
|
1363
|
+
try {
|
|
1364
|
+
const auth = await requireAdmin(req, config);
|
|
1365
|
+
if (!auth.authorized) {
|
|
1366
|
+
return Response.json({
|
|
1367
|
+
success: false,
|
|
1368
|
+
error: auth.error
|
|
1369
|
+
}, { status: 401 });
|
|
1370
|
+
}
|
|
1371
|
+
if (!config.features?.newsletterManagement?.enabled) {
|
|
1372
|
+
return Response.json({
|
|
1373
|
+
success: false,
|
|
1374
|
+
error: "Broadcast management is not enabled"
|
|
1375
|
+
}, { status: 400 });
|
|
1376
|
+
}
|
|
1377
|
+
const url = new URL(req.url || "", `http://localhost`);
|
|
1378
|
+
const pathParts = url.pathname.split("/");
|
|
1379
|
+
const id = pathParts[pathParts.length - 2];
|
|
1380
|
+
if (!id) {
|
|
1381
|
+
return Response.json({
|
|
1382
|
+
success: false,
|
|
1383
|
+
error: "Broadcast ID is required"
|
|
1384
|
+
}, { status: 400 });
|
|
1385
|
+
}
|
|
1386
|
+
const data = await (req.json?.() || Promise.resolve({}));
|
|
1387
|
+
const broadcastDoc = await req.payload.findByID({
|
|
1388
|
+
collection: collectionSlug,
|
|
1389
|
+
id,
|
|
1390
|
+
user: auth.user
|
|
1391
|
+
});
|
|
1392
|
+
if (!broadcastDoc || !broadcastDoc.providerId) {
|
|
1393
|
+
return Response.json({
|
|
1394
|
+
success: false,
|
|
1395
|
+
error: "Broadcast not found or not synced with provider"
|
|
1396
|
+
}, { status: 404 });
|
|
1397
|
+
}
|
|
1398
|
+
const providerConfig = await getBroadcastConfig(req, config);
|
|
1399
|
+
if (!providerConfig || !providerConfig.token) {
|
|
1400
|
+
return Response.json({
|
|
1401
|
+
success: false,
|
|
1402
|
+
error: "Broadcast provider not configured in Newsletter Settings or environment variables"
|
|
1403
|
+
}, { status: 500 });
|
|
1404
|
+
}
|
|
1405
|
+
const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
|
|
1406
|
+
const provider = new BroadcastApiProvider2(providerConfig);
|
|
1407
|
+
const broadcast = await provider.send(broadcastDoc.providerId, data);
|
|
1408
|
+
await req.payload.update({
|
|
1409
|
+
collection: collectionSlug,
|
|
1410
|
+
id,
|
|
1411
|
+
data: {
|
|
1412
|
+
sendStatus: "sending" /* SENDING */,
|
|
1413
|
+
sentAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1414
|
+
},
|
|
1415
|
+
user: auth.user
|
|
1416
|
+
});
|
|
1417
|
+
return Response.json({
|
|
1418
|
+
success: true,
|
|
1419
|
+
message: "Broadcast sent successfully",
|
|
1420
|
+
broadcast
|
|
1421
|
+
});
|
|
1422
|
+
} catch (error) {
|
|
1423
|
+
console.error("Failed to send broadcast:", error);
|
|
1424
|
+
if (error instanceof NewsletterProviderError) {
|
|
1425
|
+
return Response.json({
|
|
1426
|
+
success: false,
|
|
1427
|
+
error: error.message,
|
|
1428
|
+
code: error.code
|
|
1429
|
+
}, { status: error.code === "NOT_SUPPORTED" ? 501 : 500 });
|
|
1430
|
+
}
|
|
1431
|
+
return Response.json({
|
|
1432
|
+
success: false,
|
|
1433
|
+
error: "Failed to send broadcast"
|
|
1434
|
+
}, { status: 500 });
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
};
|
|
1438
|
+
};
|
|
1439
|
+
|
|
1440
|
+
// src/endpoints/broadcasts/schedule.ts
|
|
1441
|
+
init_types();
|
|
1442
|
+
var createScheduleBroadcastEndpoint = (config, collectionSlug) => {
|
|
1443
|
+
return {
|
|
1444
|
+
path: "/:id/schedule",
|
|
1445
|
+
method: "post",
|
|
1446
|
+
handler: async (req) => {
|
|
1447
|
+
try {
|
|
1448
|
+
const auth = await requireAdmin(req, config);
|
|
1449
|
+
if (!auth.authorized) {
|
|
1450
|
+
return Response.json({
|
|
1451
|
+
success: false,
|
|
1452
|
+
error: auth.error
|
|
1453
|
+
}, { status: 401 });
|
|
1454
|
+
}
|
|
1455
|
+
if (!config.features?.newsletterManagement?.enabled) {
|
|
1456
|
+
return Response.json({
|
|
1457
|
+
success: false,
|
|
1458
|
+
error: "Broadcast management is not enabled"
|
|
1459
|
+
}, { status: 400 });
|
|
1460
|
+
}
|
|
1461
|
+
const url = new URL(req.url || "", `http://localhost`);
|
|
1462
|
+
const pathParts = url.pathname.split("/");
|
|
1463
|
+
const id = pathParts[pathParts.length - 2];
|
|
1464
|
+
if (!id) {
|
|
1465
|
+
return Response.json({
|
|
1466
|
+
success: false,
|
|
1467
|
+
error: "Broadcast ID is required"
|
|
1468
|
+
}, { status: 400 });
|
|
1469
|
+
}
|
|
1470
|
+
const data = await (req.json?.() || Promise.resolve({}));
|
|
1471
|
+
const { scheduledAt } = data;
|
|
1472
|
+
if (!scheduledAt) {
|
|
1473
|
+
return Response.json({
|
|
1474
|
+
success: false,
|
|
1475
|
+
error: "scheduledAt is required"
|
|
1476
|
+
}, { status: 400 });
|
|
1477
|
+
}
|
|
1478
|
+
const scheduledDate = new Date(scheduledAt);
|
|
1479
|
+
if (isNaN(scheduledDate.getTime())) {
|
|
1480
|
+
return Response.json({
|
|
1481
|
+
success: false,
|
|
1482
|
+
error: "Invalid scheduledAt date"
|
|
1483
|
+
}, { status: 400 });
|
|
1484
|
+
}
|
|
1485
|
+
if (scheduledDate <= /* @__PURE__ */ new Date()) {
|
|
1486
|
+
return Response.json({
|
|
1487
|
+
success: false,
|
|
1488
|
+
error: "scheduledAt must be in the future"
|
|
1489
|
+
}, { status: 400 });
|
|
1490
|
+
}
|
|
1491
|
+
const broadcastDoc = await req.payload.findByID({
|
|
1492
|
+
collection: collectionSlug,
|
|
1493
|
+
id,
|
|
1494
|
+
user: auth.user
|
|
1495
|
+
});
|
|
1496
|
+
if (!broadcastDoc || !broadcastDoc.providerId) {
|
|
1497
|
+
return Response.json({
|
|
1498
|
+
success: false,
|
|
1499
|
+
error: "Broadcast not found or not synced with provider"
|
|
1500
|
+
}, { status: 404 });
|
|
1501
|
+
}
|
|
1502
|
+
const providerConfig = config.providers?.broadcast;
|
|
1503
|
+
if (!providerConfig) {
|
|
1504
|
+
return Response.json({
|
|
1505
|
+
success: false,
|
|
1506
|
+
error: "Broadcast provider not configured"
|
|
1507
|
+
}, { status: 500 });
|
|
1508
|
+
}
|
|
1509
|
+
const { BroadcastApiProvider: BroadcastApiProvider2 } = await Promise.resolve().then(() => (init_broadcast2(), broadcast_exports));
|
|
1510
|
+
const provider = new BroadcastApiProvider2(providerConfig);
|
|
1511
|
+
const broadcast = await provider.schedule(broadcastDoc.providerId, scheduledDate);
|
|
1512
|
+
await req.payload.update({
|
|
1513
|
+
collection: collectionSlug,
|
|
1514
|
+
id,
|
|
1515
|
+
data: {
|
|
1516
|
+
sendStatus: "scheduled" /* SCHEDULED */,
|
|
1517
|
+
scheduledAt: scheduledDate.toISOString()
|
|
1518
|
+
},
|
|
1519
|
+
user: auth.user
|
|
1520
|
+
});
|
|
1521
|
+
return Response.json({
|
|
1522
|
+
success: true,
|
|
1523
|
+
message: `Broadcast scheduled for ${scheduledDate.toISOString()}`,
|
|
1524
|
+
broadcast
|
|
1525
|
+
});
|
|
1526
|
+
} catch (error) {
|
|
1527
|
+
console.error("Failed to schedule broadcast:", error);
|
|
1528
|
+
if (error instanceof NewsletterProviderError) {
|
|
1529
|
+
return Response.json({
|
|
1530
|
+
success: false,
|
|
1531
|
+
error: error.message,
|
|
1532
|
+
code: error.code
|
|
1533
|
+
}, { status: error.code === "NOT_SUPPORTED" ? 501 : 500 });
|
|
1534
|
+
}
|
|
1535
|
+
return Response.json({
|
|
1536
|
+
success: false,
|
|
1537
|
+
error: "Failed to schedule broadcast"
|
|
1538
|
+
}, { status: 500 });
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
};
|
|
1542
|
+
};
|
|
1543
|
+
|
|
1544
|
+
// src/endpoints/broadcasts/test.ts
|
|
1545
|
+
var createTestBroadcastEndpoint = (config, collectionSlug) => {
|
|
1546
|
+
return {
|
|
1547
|
+
path: "/:id/test",
|
|
1548
|
+
method: "post",
|
|
1549
|
+
handler: async (req) => {
|
|
1550
|
+
try {
|
|
1551
|
+
const auth = await requireAdmin(req, config);
|
|
1552
|
+
if (!auth.authorized) {
|
|
1553
|
+
return Response.json({
|
|
1554
|
+
success: false,
|
|
1555
|
+
error: auth.error
|
|
1556
|
+
}, { status: 401 });
|
|
1557
|
+
}
|
|
1558
|
+
const url = new URL(req.url || "", `http://localhost`);
|
|
1559
|
+
const pathParts = url.pathname.split("/");
|
|
1560
|
+
const id = pathParts[pathParts.length - 2];
|
|
1561
|
+
if (!id) {
|
|
1562
|
+
return Response.json({
|
|
1563
|
+
success: false,
|
|
1564
|
+
error: "Broadcast ID is required"
|
|
1565
|
+
}, { status: 400 });
|
|
1566
|
+
}
|
|
1567
|
+
const data = await (req.json?.() || Promise.resolve({}));
|
|
1568
|
+
const testEmail = data.email || auth.user.email;
|
|
1569
|
+
if (!testEmail) {
|
|
1570
|
+
return Response.json({
|
|
1571
|
+
success: false,
|
|
1572
|
+
error: "No email address available for test send"
|
|
1573
|
+
}, { status: 400 });
|
|
1574
|
+
}
|
|
1575
|
+
const broadcast = await req.payload.findByID({
|
|
1576
|
+
collection: collectionSlug,
|
|
1577
|
+
id,
|
|
1578
|
+
user: auth.user
|
|
1579
|
+
});
|
|
1580
|
+
if (!broadcast) {
|
|
1581
|
+
return Response.json({
|
|
1582
|
+
success: false,
|
|
1583
|
+
error: "Broadcast not found"
|
|
1584
|
+
}, { status: 404 });
|
|
1585
|
+
}
|
|
1586
|
+
const htmlContent = await convertToEmailSafeHtml(broadcast.content, {
|
|
1587
|
+
wrapInTemplate: true,
|
|
1588
|
+
preheader: broadcast.preheader,
|
|
1589
|
+
customBlockConverter: config.customizations?.broadcasts?.customBlockConverter
|
|
1590
|
+
});
|
|
1591
|
+
const emailService = req.payload.newsletterEmailService;
|
|
1592
|
+
if (!emailService) {
|
|
1593
|
+
return Response.json({
|
|
1594
|
+
success: false,
|
|
1595
|
+
error: "Email service is not configured"
|
|
1596
|
+
}, { status: 500 });
|
|
1597
|
+
}
|
|
1598
|
+
const providerConfig = config.providers.default === "resend" ? config.providers.resend : config.providers.broadcast;
|
|
1599
|
+
const fromEmail = providerConfig?.fromAddress || providerConfig?.fromEmail || "noreply@example.com";
|
|
1600
|
+
const fromName = providerConfig?.fromName || "Newsletter";
|
|
1601
|
+
const replyTo = broadcast.settings?.replyTo || providerConfig?.replyTo;
|
|
1602
|
+
await emailService.send({
|
|
1603
|
+
to: testEmail,
|
|
1604
|
+
from: fromEmail,
|
|
1605
|
+
fromName,
|
|
1606
|
+
replyTo,
|
|
1607
|
+
subject: `[TEST] ${broadcast.subject}`,
|
|
1608
|
+
html: htmlContent,
|
|
1609
|
+
trackOpens: false,
|
|
1610
|
+
trackClicks: false
|
|
1611
|
+
});
|
|
1612
|
+
return Response.json({
|
|
1613
|
+
success: true,
|
|
1614
|
+
message: `Test email sent to ${testEmail}`
|
|
1615
|
+
});
|
|
1616
|
+
} catch (error) {
|
|
1617
|
+
console.error("Failed to send test broadcast:", error);
|
|
1618
|
+
return Response.json({
|
|
1619
|
+
success: false,
|
|
1620
|
+
error: "Failed to send test email"
|
|
1621
|
+
}, { status: 500 });
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
};
|
|
1625
|
+
};
|
|
1626
|
+
|
|
1627
|
+
// src/endpoints/broadcasts/preview.ts
|
|
1628
|
+
var createBroadcastPreviewEndpoint = (config, collectionSlug) => {
|
|
1629
|
+
return {
|
|
1630
|
+
path: "/preview",
|
|
1631
|
+
method: "post",
|
|
1632
|
+
handler: async (req) => {
|
|
1633
|
+
try {
|
|
1634
|
+
const data = await (req.json?.() || Promise.resolve({}));
|
|
1635
|
+
const { content, preheader, subject } = data;
|
|
1636
|
+
if (!content) {
|
|
1637
|
+
return Response.json({
|
|
1638
|
+
success: false,
|
|
1639
|
+
error: "Content is required for preview"
|
|
1640
|
+
}, { status: 400 });
|
|
1641
|
+
}
|
|
1642
|
+
const mediaUrl = req.payload.config.serverURL ? `${req.payload.config.serverURL}/api/media` : "/api/media";
|
|
1643
|
+
const htmlContent = await convertToEmailSafeHtml(content, {
|
|
1644
|
+
wrapInTemplate: true,
|
|
1645
|
+
preheader,
|
|
1646
|
+
mediaUrl,
|
|
1647
|
+
customBlockConverter: config.customizations?.broadcasts?.customBlockConverter
|
|
1648
|
+
});
|
|
1649
|
+
return Response.json({
|
|
1650
|
+
success: true,
|
|
1651
|
+
preview: {
|
|
1652
|
+
subject: subject || "Preview",
|
|
1653
|
+
preheader: preheader || "",
|
|
1654
|
+
html: htmlContent
|
|
1655
|
+
}
|
|
1656
|
+
});
|
|
1657
|
+
} catch (error) {
|
|
1658
|
+
console.error("Failed to generate email preview:", error);
|
|
1659
|
+
return Response.json({
|
|
1660
|
+
success: false,
|
|
1661
|
+
error: "Failed to generate email preview"
|
|
1662
|
+
}, { status: 500 });
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
};
|
|
1666
|
+
};
|
|
1667
|
+
|
|
1244
1668
|
// src/collections/Broadcasts.ts
|
|
1245
1669
|
var createBroadcastsCollection = (pluginConfig) => {
|
|
1246
1670
|
const hasProviders = !!(pluginConfig.providers?.broadcast || pluginConfig.providers?.resend);
|
|
1247
1671
|
const customizations = pluginConfig.customizations?.broadcasts;
|
|
1672
|
+
const collectionSlug = "broadcasts";
|
|
1248
1673
|
return {
|
|
1249
|
-
slug:
|
|
1674
|
+
slug: collectionSlug,
|
|
1250
1675
|
access: {
|
|
1251
1676
|
read: () => true,
|
|
1252
1677
|
// Public read access
|
|
@@ -1275,6 +1700,12 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
1275
1700
|
description: "Individual email campaigns sent to subscribers",
|
|
1276
1701
|
defaultColumns: ["subject", "_status", "sendStatus", "sentAt", "recipientCount"]
|
|
1277
1702
|
},
|
|
1703
|
+
endpoints: [
|
|
1704
|
+
createSendBroadcastEndpoint(pluginConfig, collectionSlug),
|
|
1705
|
+
createScheduleBroadcastEndpoint(pluginConfig, collectionSlug),
|
|
1706
|
+
createTestBroadcastEndpoint(pluginConfig, collectionSlug),
|
|
1707
|
+
createBroadcastPreviewEndpoint(pluginConfig, collectionSlug)
|
|
1708
|
+
],
|
|
1278
1709
|
fields: [
|
|
1279
1710
|
{
|
|
1280
1711
|
name: "subject",
|
|
@@ -1820,67 +2251,6 @@ var createBroadcastsCollection = (pluginConfig) => {
|
|
|
1820
2251
|
};
|
|
1821
2252
|
};
|
|
1822
2253
|
|
|
1823
|
-
// src/utils/access.ts
|
|
1824
|
-
var isAdmin = (user, config) => {
|
|
1825
|
-
if (!user || user.collection !== "users") {
|
|
1826
|
-
return false;
|
|
1827
|
-
}
|
|
1828
|
-
if (config?.access?.isAdmin) {
|
|
1829
|
-
return config.access.isAdmin(user);
|
|
1830
|
-
}
|
|
1831
|
-
if (user.roles?.includes("admin")) {
|
|
1832
|
-
return true;
|
|
1833
|
-
}
|
|
1834
|
-
if (user.isAdmin === true) {
|
|
1835
|
-
return true;
|
|
1836
|
-
}
|
|
1837
|
-
if (user.role === "admin") {
|
|
1838
|
-
return true;
|
|
1839
|
-
}
|
|
1840
|
-
if (user.admin === true) {
|
|
1841
|
-
return true;
|
|
1842
|
-
}
|
|
1843
|
-
return false;
|
|
1844
|
-
};
|
|
1845
|
-
var adminOnly = (config) => ({ req }) => {
|
|
1846
|
-
const user = req.user;
|
|
1847
|
-
return isAdmin(user, config);
|
|
1848
|
-
};
|
|
1849
|
-
var adminOrSelf = (config) => ({ req, id }) => {
|
|
1850
|
-
const user = req.user;
|
|
1851
|
-
if (!user) {
|
|
1852
|
-
if (!id) {
|
|
1853
|
-
return {
|
|
1854
|
-
id: {
|
|
1855
|
-
equals: "unauthorized-no-access"
|
|
1856
|
-
}
|
|
1857
|
-
};
|
|
1858
|
-
}
|
|
1859
|
-
return false;
|
|
1860
|
-
}
|
|
1861
|
-
if (isAdmin(user, config)) {
|
|
1862
|
-
return true;
|
|
1863
|
-
}
|
|
1864
|
-
if (user.collection === "subscribers") {
|
|
1865
|
-
if (!id) {
|
|
1866
|
-
return {
|
|
1867
|
-
id: {
|
|
1868
|
-
equals: user.id
|
|
1869
|
-
}
|
|
1870
|
-
};
|
|
1871
|
-
}
|
|
1872
|
-
return id === user.id;
|
|
1873
|
-
}
|
|
1874
|
-
if (!id) {
|
|
1875
|
-
return {
|
|
1876
|
-
id: {
|
|
1877
|
-
equals: "unauthorized-no-access"
|
|
1878
|
-
}
|
|
1879
|
-
};
|
|
1880
|
-
}
|
|
1881
|
-
return false;
|
|
1882
|
-
};
|
|
1883
|
-
|
|
1884
2254
|
// src/emails/render.tsx
|
|
1885
2255
|
var import_render = require("@react-email/render");
|
|
1886
2256
|
|