refactor: optimize newsletter subscribe and check status logic

This commit is contained in:
javayhu 2025-04-29 22:22:26 +08:00
parent d94e777dde
commit f607bae96e
3 changed files with 24 additions and 53 deletions

View File

@ -112,7 +112,7 @@
"remark": "^15.0.1", "remark": "^15.0.1",
"remark-code-import": "^1.2.0", "remark-code-import": "^1.2.0",
"remark-gfm": "^4.0.1", "remark-gfm": "^4.0.1",
"resend": "^4.3.0", "resend": "^4.4.1",
"shiki": "^2.4.2", "shiki": "^2.4.2",
"sonner": "^2.0.0", "sonner": "^2.0.0",
"stripe": "^17.6.0", "stripe": "^17.6.0",

18
pnpm-lock.yaml generated
View File

@ -294,8 +294,8 @@ importers:
specifier: ^4.0.1 specifier: ^4.0.1
version: 4.0.1 version: 4.0.1
resend: resend:
specifier: ^4.3.0 specifier: ^4.4.1
version: 4.3.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) version: 4.4.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
shiki: shiki:
specifier: ^2.4.2 specifier: ^2.4.2
version: 2.4.2 version: 2.4.2
@ -3197,8 +3197,8 @@ packages:
'@types/node@20.17.23': '@types/node@20.17.23':
resolution: {integrity: sha512-8PCGZ1ZJbEZuYNTMqywO+Sj4vSKjSjT6Ua+6RFOYlEvIvKQABPtrNkoVSLSKDb4obYcMhspVKmsw8Cm10NFRUg==} resolution: {integrity: sha512-8PCGZ1ZJbEZuYNTMqywO+Sj4vSKjSjT6Ua+6RFOYlEvIvKQABPtrNkoVSLSKDb4obYcMhspVKmsw8Cm10NFRUg==}
'@types/node@20.17.24': '@types/node@20.17.32':
resolution: {integrity: sha512-d7fGCyB96w9BnWQrOsJtpyiSaBcAYYr75bnK6ZRjDbql2cGLj/3GsL5OYmLPNq76l7Gf2q4Rv9J2o6h5CrD9sA==} resolution: {integrity: sha512-zeMXFn8zQ+UkjK4ws0RiOC9EWByyW1CcVmLe+2rQocXRsGEDxUCwPEIVgpsGcLHS/P8JkT0oa3839BRABS0oPw==}
'@types/pg@8.11.11': '@types/pg@8.11.11':
resolution: {integrity: sha512-kGT1qKM8wJQ5qlawUrEkXgvMSXoV213KfMGXcwfDwUIfUHXqXYXOfS1nE1LINRJVVVx5wCm70XnFlMHaIcQAfw==} resolution: {integrity: sha512-kGT1qKM8wJQ5qlawUrEkXgvMSXoV213KfMGXcwfDwUIfUHXqXYXOfS1nE1LINRJVVVx5wCm70XnFlMHaIcQAfw==}
@ -5115,8 +5115,8 @@ packages:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
resend@4.3.0: resend@4.4.1:
resolution: {integrity: sha512-4OBHeusMVSl0vcba2J3AaGzdZ1SXAAhX/Wkcwobe16AHmlW9h3li8wG62Fhvlsc61e+wlQoxcwJZP6WrBTbghQ==} resolution: {integrity: sha512-FR22bzMW3VfoyZSBc8ScGo8ShrMWHmWB0G3FrispzWCnYSEEK5M7pyRvZtInKmM/09lsJETKc2q66mX+dXPSmg==}
engines: {node: '>=18'} engines: {node: '>=18'}
resolve-pkg-maps@1.0.0: resolve-pkg-maps@1.0.0:
@ -8567,7 +8567,7 @@ snapshots:
dependencies: dependencies:
undici-types: 6.19.8 undici-types: 6.19.8
'@types/node@20.17.24': '@types/node@20.17.32':
dependencies: dependencies:
undici-types: 6.19.8 undici-types: 6.19.8
optional: true optional: true
@ -8580,7 +8580,7 @@ snapshots:
'@types/pg@8.11.6': '@types/pg@8.11.6':
dependencies: dependencies:
'@types/node': 20.17.24 '@types/node': 20.17.32
pg-protocol: 1.9.5 pg-protocol: 1.9.5
pg-types: 4.0.2 pg-types: 4.0.2
optional: true optional: true
@ -10927,7 +10927,7 @@ snapshots:
require-directory@2.1.1: {} require-directory@2.1.1: {}
resend@4.3.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0): resend@4.4.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
dependencies: dependencies:
'@react-email/render': 1.0.6(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@react-email/render': 1.0.6(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
transitivePeerDependencies: transitivePeerDependencies:

View File

@ -43,25 +43,14 @@ export class ResendNewsletterProvider implements NewsletterProvider {
*/ */
async subscribe({ email }: SubscribeNewsletterParams): Promise<boolean> { async subscribe({ email }: SubscribeNewsletterParams): Promise<boolean> {
try { try {
// First, list all contacts to find the one with the matching email // Check if the contact exists
const listResult = await this.resend.contacts.list({ const getResult = await this.resend.contacts.get({
email,
audienceId: this.audienceId, audienceId: this.audienceId,
}); });
if (listResult.error) {
console.error('Error listing contacts:', listResult.error);
return false;
}
// console.log('subscribe list result:', listResult); // If contact doesn't exist, create a new one
// Check if the contact with the given email exists in the list if (getResult.error) {
let contact = null;
if (listResult.data?.data && Array.isArray(listResult.data.data)) {
contact = listResult.data.data.find((c) => c.email === email);
}
// console.log('subscribe params:', { email, contact });
// If the contact does not exist, create a new one
if (!contact) {
console.log('Creating new contact', email); console.log('Creating new contact', email);
const createResult = await this.resend.contacts.create({ const createResult = await this.resend.contacts.create({
email, email,
@ -69,7 +58,6 @@ export class ResendNewsletterProvider implements NewsletterProvider {
unsubscribed: false, unsubscribed: false,
}); });
// console.log('subscribe create result:', createResult);
if (createResult.error) { if (createResult.error) {
console.error('Error creating contact', createResult.error); console.error('Error creating contact', createResult.error);
return false; return false;
@ -78,20 +66,13 @@ export class ResendNewsletterProvider implements NewsletterProvider {
return true; return true;
} }
// If the contact already exists, update it // If the contact exists, update it
// NOTICE: we can not just create a new contact if this email already exists,
// because Resend will response 201, but user is not subscribed
// NOTICE: we can not update it with email if the contact not found, it will return 404,
// statusCode: 404, name: not_found, message: Contact not found
const updateResult = await this.resend.contacts.update({ const updateResult = await this.resend.contacts.update({
email, email,
audienceId: this.audienceId, audienceId: this.audienceId,
unsubscribed: false, unsubscribed: false,
}); });
// console.log('subscribe update result:', updateResult);
// NOTICE: we can not request too many times, because of the rate limit of Resend
// statusCode: 429, name: rate_limit_exceeded, message: Too many requests, you can only make 2 requests per second.
if (updateResult.error) { if (updateResult.error) {
console.error('Error updating contact', updateResult.error); console.error('Error updating contact', updateResult.error);
return false; return false;
@ -142,31 +123,21 @@ export class ResendNewsletterProvider implements NewsletterProvider {
email, email,
}: CheckSubscribeStatusParams): Promise<boolean> { }: CheckSubscribeStatusParams): Promise<boolean> {
try { try {
// TODO: use get method to check if the contact exists with email, const result = await this.resend.contacts.get({
// the new Resend API is not ready yet, so we use the old way to check email,
// First, list all contacts to find the one with the matching email
const listResult = await this.resend.contacts.list({
audienceId: this.audienceId, audienceId: this.audienceId,
}); });
if (listResult.error) { if (result.error) {
console.error('Error listing contacts:', listResult.error); console.error('Error getting contact:', result.error);
return false; return false;
} }
// console.log('check newsletter params:', { email, listResult }); const status = !result.data?.unsubscribed;
// Check if the contact with the given email exists in the list console.log('Check subscribe status:', { email, status });
if (listResult.data?.data && Array.isArray(listResult.data.data)) { return status;
return listResult.data.data.some(
(contact) => contact.email === email && contact.unsubscribed === false
);
}
// console.log('check newsletter status:', { email, subscribed: false });
return false;
} catch (error) { } catch (error) {
console.error('Error checking subscription status:', error); console.error('Error checking subscribe status:', error);
return false; return false;
} }
} }