refactor: improve news and email handling, style, folder structure (#16)

This commit is contained in:
Riccardo Senica
2024-06-04 18:04:54 +08:00
committed by GitHub
parent bc5e0cc195
commit acc10bf5fd
62 changed files with 1737 additions and 1553 deletions

View File

@@ -1,8 +1,7 @@
import { NextResponse } from 'next/server';
export function ApiResponse(status: number, message: string) {
const response = new NextResponse(message, { status });
response.headers.set('Access-Control-Allow-Origin', process.env.HOME_URL!);
export function ApiResponse(status: number, message: unknown) {
const stringMessage = JSON.stringify(message);
return response;
return new NextResponse(stringMessage, { status });
}

13
utils/sayings.ts Normal file
View File

@@ -0,0 +1,13 @@
export const sayings = [
'hot off the press',
'straight from the oven',
"straight from the horse's mouth",
'brand spanking new',
'fresh as a daisy',
'straight out of the box',
'straight off the assembly line',
'hot out of the kitchen',
'just minted',
'freshly brewed',
'just off the production line'
];

View File

@@ -1,43 +1,61 @@
import { Resend } from 'resend';
type EmailTemplate = {
interface EmailTemplate {
subject: string;
template: JSX.Element;
};
}
export async function sender(
to: string[],
recipients: string[],
{ subject, template }: EmailTemplate
) {
if (recipients.length === 0) {
console.info(`${subject} email skipped for having zero recipients`);
return true;
}
const resend = new Resend(process.env.RESEND_KEY);
try {
const { error } = await resend.batch.send(
to.map(t => {
return {
from: process.env.RESEND_FROM!,
to: t,
subject,
react: template,
headers: {
'List-Unsubscribe': `<${process.env.HOME_URL}/unsubscribe>`
}
};
})
);
let response;
if (recipients.length == 1) {
response = await resend.emails.send({
from: process.env.RESEND_FROM!,
to: recipients[0],
subject,
react: template,
headers: {
'List-Unsubscribe': `<${process.env.HOME_URL}/unsubscribe>`
}
});
} else {
response = await resend.batch.send(
recipients.map(recipient => {
return {
from: process.env.RESEND_FROM!,
to: recipient,
subject,
react: template,
headers: {
'List-Unsubscribe': `<${process.env.HOME_URL}/unsubscribe>`
}
};
})
);
}
const { error } = response;
if (error) {
console.log(error);
console.error(error);
return false;
}
} catch (error) {
console.log(error);
console.info(`${subject} email sent to ${recipients.length} recipients`);
return true;
} catch (error) {
console.error(error);
return false;
}
console.log('Email sent', subject, to.length);
return true;
}

6
utils/statusCodes.ts Normal file
View File

@@ -0,0 +1,6 @@
export const STATUS_OK = 200;
export const STATUS_BAD_REQUEST = 400;
export const BAD_REQUEST = 'Bad request';
export const STATUS_INTERNAL_SERVER_ERROR = 500;
export const INTERNAL_SERVER_ERROR = 'Internal server error';
export const STATUS_UNAUTHORIZED = 401;

View File

@@ -1,3 +1,5 @@
export const topNews = 'https://hacker-news.firebaseio.com/v0/topstories.json';
export const singleNews = (id: number) =>
`https://hacker-news.firebaseio.com/v0/item/${id}.json`;
export function singleNews(id: number) {
return `https://hacker-news.firebaseio.com/v0/item/${id}.json`;
}

View File

@@ -5,20 +5,24 @@ export const ResponseSchema = z.object({
message: z.string()
});
export type ResponseType = z.infer<typeof ResponseSchema>;
export const SubscribeFormSchema = z.object({
email: z.string().email(),
name: z.string().optional()
email: z.string().email()
});
export type SubscribeFormType = z.infer<typeof SubscribeFormSchema>;
export const ConfirmationSchema = z.object({
code: z.string()
});
export const UnsubscribeFormSchema = z.object({
email: z.string().email(),
name: z.string().optional()
email: z.string().email()
});
export type UnsubscribeFormType = z.infer<typeof UnsubscribeFormSchema>;
export const NewsDatabaseSchema = z.object({
id: z.number(),
title: z.string(),
@@ -30,6 +34,8 @@ export const NewsDatabaseSchema = z.object({
score: z.number()
});
export type NewsDatabaseType = z.infer<typeof NewsDatabaseSchema>;
export const NewsSchema = z.object({
id: z.number(),
title: z.string(),
@@ -42,10 +48,12 @@ export const NewsSchema = z.object({
createdAt: z.date()
});
export type NewsType = z.infer<typeof NewsSchema>;
export const NewsTileSchema = z.object({
id: z.number(),
title: z.string(),
by: z.string()
});
export type NewsTile = z.infer<typeof NewsTileSchema>;
export type NewsTileType = z.infer<typeof NewsTileSchema>;