payload-plugin-newsletter 0.13.3 → 0.14.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 +22 -0
- package/dist/index.cjs +138 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +138 -14
- package/dist/index.js.map +1 -1
- package/dist/types.d.cts +10 -0
- package/dist/types.d.ts +10 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
## [0.14.0] - 2025-07-22
|
|
2
|
+
|
|
3
|
+
### Added
|
|
4
|
+
- New authentication configuration options for flexible subscriber management
|
|
5
|
+
- `auth.allowUnsubscribedSignin`: Allow unsubscribed users to sign in
|
|
6
|
+
- `auth.allowResubscribe`: Allow unsubscribed users to resubscribe
|
|
7
|
+
- Improved subscribe endpoint behavior:
|
|
8
|
+
- Already subscribed users now receive a sign-in link instead of an error
|
|
9
|
+
- Unsubscribed users can resubscribe (when enabled)
|
|
10
|
+
- Enhanced response formats with status indicators:
|
|
11
|
+
- `requiresSubscribe`: Indicates user needs to subscribe first
|
|
12
|
+
- `requiresResubscribe`: Indicates user needs to resubscribe
|
|
13
|
+
- `wasResubscribed`: Indicates user was successfully resubscribed
|
|
14
|
+
- `alreadySubscribed`: Indicates user is already subscribed
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- Sign-in endpoint now returns more detailed error information for better UX
|
|
18
|
+
- Subscribe endpoint handles existing subscribers more gracefully
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- Unsubscribed users can now manage their preferences when `allowUnsubscribedSignin` is enabled
|
|
22
|
+
|
|
1
23
|
## [0.13.3] - 2025-07-21
|
|
2
24
|
|
|
3
25
|
### Added
|
package/dist/index.cjs
CHANGED
|
@@ -1278,6 +1278,44 @@ var createNewsletterSettingsGlobal = (pluginConfig) => {
|
|
|
1278
1278
|
}
|
|
1279
1279
|
]
|
|
1280
1280
|
},
|
|
1281
|
+
{
|
|
1282
|
+
label: "Brand Settings",
|
|
1283
|
+
fields: [
|
|
1284
|
+
{
|
|
1285
|
+
name: "brandSettings",
|
|
1286
|
+
type: "group",
|
|
1287
|
+
label: "Brand Settings",
|
|
1288
|
+
fields: [
|
|
1289
|
+
{
|
|
1290
|
+
name: "siteName",
|
|
1291
|
+
type: "text",
|
|
1292
|
+
label: "Site Name",
|
|
1293
|
+
required: true,
|
|
1294
|
+
defaultValue: "Newsletter",
|
|
1295
|
+
admin: {
|
|
1296
|
+
description: "Your website or newsletter name"
|
|
1297
|
+
}
|
|
1298
|
+
},
|
|
1299
|
+
{
|
|
1300
|
+
name: "siteUrl",
|
|
1301
|
+
type: "text",
|
|
1302
|
+
label: "Site URL",
|
|
1303
|
+
admin: {
|
|
1304
|
+
description: "Your website URL (optional)"
|
|
1305
|
+
}
|
|
1306
|
+
},
|
|
1307
|
+
{
|
|
1308
|
+
name: "logoUrl",
|
|
1309
|
+
type: "text",
|
|
1310
|
+
label: "Logo URL",
|
|
1311
|
+
admin: {
|
|
1312
|
+
description: "URL to your logo image (optional)"
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
]
|
|
1316
|
+
}
|
|
1317
|
+
]
|
|
1318
|
+
},
|
|
1281
1319
|
{
|
|
1282
1320
|
label: "Email Templates",
|
|
1283
1321
|
fields: [
|
|
@@ -2021,20 +2059,93 @@ var createSubscribeEndpoint = (config) => {
|
|
|
2021
2059
|
if (existing.docs.length > 0) {
|
|
2022
2060
|
const subscriber2 = existing.docs[0];
|
|
2023
2061
|
if (subscriber2.subscriptionStatus === "unsubscribed") {
|
|
2062
|
+
const allowResubscribe = config.auth?.allowResubscribe ?? false;
|
|
2063
|
+
if (!allowResubscribe) {
|
|
2064
|
+
return Response.json({
|
|
2065
|
+
success: false,
|
|
2066
|
+
error: "This email has been unsubscribed. Please contact support to resubscribe."
|
|
2067
|
+
}, { status: 400 });
|
|
2068
|
+
}
|
|
2069
|
+
const updated = await req.payload.update({
|
|
2070
|
+
collection: config.subscribersSlug || "subscribers",
|
|
2071
|
+
id: subscriber2.id,
|
|
2072
|
+
data: {
|
|
2073
|
+
subscriptionStatus: "active",
|
|
2074
|
+
resubscribedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2075
|
+
// Preserve preferences but update metadata
|
|
2076
|
+
signupMetadata: {
|
|
2077
|
+
...metadata,
|
|
2078
|
+
source: source || "resubscribe",
|
|
2079
|
+
resubscribedFrom: subscriber2.signupMetadata?.source
|
|
2080
|
+
}
|
|
2081
|
+
},
|
|
2082
|
+
overrideAccess: true
|
|
2083
|
+
});
|
|
2084
|
+
if (config.hooks?.afterSubscribe) {
|
|
2085
|
+
await config.hooks.afterSubscribe({
|
|
2086
|
+
doc: updated,
|
|
2087
|
+
req
|
|
2088
|
+
});
|
|
2089
|
+
}
|
|
2090
|
+
const emailService = req.payload.newsletterEmailService;
|
|
2091
|
+
if (emailService) {
|
|
2092
|
+
const settings2 = await req.payload.findGlobal({
|
|
2093
|
+
slug: config.settingsSlug || "newsletter-settings"
|
|
2094
|
+
});
|
|
2095
|
+
const html = await renderEmail("welcome", {
|
|
2096
|
+
name: updated.name || "",
|
|
2097
|
+
email: updated.email,
|
|
2098
|
+
siteName: settings2?.brandSettings?.siteName || "Newsletter",
|
|
2099
|
+
siteUrl: req.payload.config.serverURL || ""
|
|
2100
|
+
});
|
|
2101
|
+
await emailService.send({
|
|
2102
|
+
to: updated.email,
|
|
2103
|
+
subject: `Welcome back to ${settings2?.brandSettings?.siteName || "our newsletter"}!`,
|
|
2104
|
+
html
|
|
2105
|
+
});
|
|
2106
|
+
}
|
|
2024
2107
|
return Response.json({
|
|
2025
|
-
success:
|
|
2026
|
-
|
|
2027
|
-
|
|
2108
|
+
success: true,
|
|
2109
|
+
message: "Welcome back! You have been resubscribed.",
|
|
2110
|
+
subscriber: {
|
|
2111
|
+
id: updated.id,
|
|
2112
|
+
email: updated.email,
|
|
2113
|
+
subscriptionStatus: updated.subscriptionStatus
|
|
2114
|
+
},
|
|
2115
|
+
wasResubscribed: true
|
|
2116
|
+
});
|
|
2028
2117
|
}
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2118
|
+
if (subscriber2.subscriptionStatus === "active") {
|
|
2119
|
+
const token = generateMagicLinkToken(
|
|
2120
|
+
String(subscriber2.id),
|
|
2121
|
+
subscriber2.email,
|
|
2122
|
+
config
|
|
2123
|
+
);
|
|
2124
|
+
const serverURL = req.payload.config.serverURL || process.env.PAYLOAD_PUBLIC_SERVER_URL || "";
|
|
2125
|
+
const magicLinkURL = generateMagicLinkURL(token, serverURL, config);
|
|
2126
|
+
const emailService = req.payload.newsletterEmailService;
|
|
2127
|
+
if (emailService) {
|
|
2128
|
+
const settings2 = await req.payload.findGlobal({
|
|
2129
|
+
slug: config.settingsSlug || "newsletter-settings"
|
|
2130
|
+
});
|
|
2131
|
+
const html = await renderEmail("signin", {
|
|
2132
|
+
magicLink: magicLinkURL,
|
|
2133
|
+
email: subscriber2.email,
|
|
2134
|
+
siteName: settings2?.brandSettings?.siteName || "Newsletter",
|
|
2135
|
+
expiresIn: config.auth?.tokenExpiration || "7d"
|
|
2136
|
+
});
|
|
2137
|
+
await emailService.send({
|
|
2138
|
+
to: subscriber2.email,
|
|
2139
|
+
subject: `Sign in to ${settings2?.brandSettings?.siteName || "your account"}`,
|
|
2140
|
+
html
|
|
2141
|
+
});
|
|
2036
2142
|
}
|
|
2037
|
-
|
|
2143
|
+
return Response.json({
|
|
2144
|
+
success: true,
|
|
2145
|
+
message: "You are already subscribed! Check your email for a sign-in link.",
|
|
2146
|
+
alreadySubscribed: true
|
|
2147
|
+
});
|
|
2148
|
+
}
|
|
2038
2149
|
}
|
|
2039
2150
|
const ipAddress = req.ip || req.connection?.remoteAddress;
|
|
2040
2151
|
const maxPerIP = settings?.subscriptionSettings?.maxSubscribersPerIP || 10;
|
|
@@ -2575,8 +2686,7 @@ var createSigninEndpoint = (config) => {
|
|
|
2575
2686
|
const result = await req.payload.find({
|
|
2576
2687
|
collection: config.subscribersSlug || "subscribers",
|
|
2577
2688
|
where: {
|
|
2578
|
-
email: { equals: email.toLowerCase() }
|
|
2579
|
-
subscriptionStatus: { equals: "active" }
|
|
2689
|
+
email: { equals: email.toLowerCase() }
|
|
2580
2690
|
},
|
|
2581
2691
|
limit: 1,
|
|
2582
2692
|
overrideAccess: true
|
|
@@ -2585,10 +2695,24 @@ var createSigninEndpoint = (config) => {
|
|
|
2585
2695
|
if (result.docs.length === 0) {
|
|
2586
2696
|
return Response.json({
|
|
2587
2697
|
success: false,
|
|
2588
|
-
error: "Email not found. Please subscribe first."
|
|
2698
|
+
error: "Email not found. Please subscribe first.",
|
|
2699
|
+
requiresSubscribe: true
|
|
2589
2700
|
}, { status: 404 });
|
|
2590
2701
|
}
|
|
2591
2702
|
const subscriber = result.docs[0];
|
|
2703
|
+
const allowUnsubscribed = config.auth?.allowUnsubscribedSignin ?? false;
|
|
2704
|
+
if (subscriber.subscriptionStatus === "unsubscribed" && !allowUnsubscribed) {
|
|
2705
|
+
return Response.json({
|
|
2706
|
+
success: false,
|
|
2707
|
+
error: "Your subscription is inactive. Please resubscribe to sign in.",
|
|
2708
|
+
subscriber: {
|
|
2709
|
+
id: subscriber.id,
|
|
2710
|
+
email: subscriber.email,
|
|
2711
|
+
subscriptionStatus: subscriber.subscriptionStatus
|
|
2712
|
+
},
|
|
2713
|
+
requiresResubscribe: true
|
|
2714
|
+
}, { status: 403 });
|
|
2715
|
+
}
|
|
2592
2716
|
const token = generateMagicLinkToken(
|
|
2593
2717
|
String(subscriber.id),
|
|
2594
2718
|
subscriber.email,
|