summaryrefslogtreecommitdiff
path: root/src/app/(main)/(home)/actions.ts
diff options
context:
space:
mode:
authorBertrand Yuan <bert.yuan@outlook.com>2025-12-16 00:12:49 +0800
committerBertrand Yuan <bert.yuan@outlook.com>2025-12-16 00:12:49 +0800
commit02ae938c238c9d18448d17a8ec92c0edd8c17463 (patch)
treedcd6a30505adb52522b20af2c0ac27f713403f10 /src/app/(main)/(home)/actions.ts
parent48b07bc308a35734a6a7a305c8fdccbfa47de7d8 (diff)
feat(back-end): introduce payload
Payload is the next.js Headless CMS and App Framework, I would like to pick it up and modify it as it is MIT licensed. Many features in Payload is not applicable for our project. So, I modify it so that it is light and clear.
Diffstat (limited to 'src/app/(main)/(home)/actions.ts')
-rw-r--r--src/app/(main)/(home)/actions.ts78
1 files changed, 78 insertions, 0 deletions
diff --git a/src/app/(main)/(home)/actions.ts b/src/app/(main)/(home)/actions.ts
new file mode 100644
index 0000000..fdb16ca
--- /dev/null
+++ b/src/app/(main)/(home)/actions.ts
@@ -0,0 +1,78 @@
+'use server';
+
+import { getContact, sendWelcomeEmail, updateContact } from '@/lib/resend';
+import { ActionError, actionClient } from '@/lib/safe-action';
+import { getSortedByDatePosts } from '@/lib/source';
+import { NewsletterSchema } from '@/lib/validators';
+import { getSession } from '@/server/auth';
+import { Resend } from 'resend';
+
+const resend = new Resend(process.env.RESEND_API_KEY as string);
+const audienceId = process.env.RESEND_AUDIENCE_ID as string;
+
+const splitName = (name = '') => {
+ const [firstName, ...lastName] = name.split(' ').filter(Boolean);
+ return {
+ firstName: firstName,
+ lastName: lastName.join(' '),
+ };
+};
+
+export const subscribeUser = actionClient
+ .schema(NewsletterSchema)
+ .action(async ({ parsedInput: { email } }) => {
+ const session = await getSession();
+ const fullName = session?.user.name || '';
+ const { firstName, lastName } = fullName
+ ? splitName(fullName)
+ : { firstName: '', lastName: '' };
+
+ try {
+ const contact = await getContact({ email, audienceId });
+
+ if (contact) {
+ await updateContact({
+ email,
+ firstName,
+ lastName,
+ audienceId,
+ unsubscribed: false,
+ });
+
+ return {
+ success: true,
+ message: 'You are already subscribed to our newsletter!',
+ };
+ }
+
+ const { data, error } = await resend.contacts.create({
+ email,
+ audienceId,
+ firstName,
+ lastName,
+ unsubscribed: false,
+ });
+
+ if (!data || error) {
+ throw new Error(
+ `Failed to create contact: ${error?.message || 'Unknown error'}`,
+ );
+ }
+
+ const posts = getSortedByDatePosts();
+ await sendWelcomeEmail({
+ posts,
+ to: email,
+ firstName: firstName || 'there',
+ });
+
+ return {
+ success: true,
+ message: 'You are now subscribed to our newsletter!',
+ };
+ } catch (error) {
+ console.error('Failed to subscribe:', error);
+ if (error instanceof ActionError) throw error;
+ throw new ActionError('Oops, something went wrong while subscribing.');
+ }
+ });