Refactor email templates to use plain text format and update sender addresses

- Simplified the magic link email format to plain text for better compatibility.
- Updated the welcome email to use plain text and changed the sender address to match the domain.
- Enhanced event registration email format to plain text, removing HTML styling for a cleaner approach.
This commit is contained in:
Jennie Robinson Faber 2026-03-05 18:40:37 +00:00
parent 8143631364
commit c3c8b6bcd4
5 changed files with 132 additions and 626 deletions

View file

@ -0,0 +1,35 @@
import Member from '../../../../models/member.js'
import { connectDB } from '../../../../utils/mongoose.js'
import { validateBody } from '../../../../utils/validateBody.js'
import { adminRoleUpdateSchema } from '../../../../utils/schemas.js'
export default defineEventHandler(async (event) => {
const admin = await requireAdmin(event)
await connectDB()
const { role } = await validateBody(event, adminRoleUpdateSchema)
const memberId = getRouterParam(event, 'id')
// Prevent self-demotion
if (admin._id.toString() === memberId && role !== 'admin') {
throw createError({
statusCode: 400,
statusMessage: 'You cannot remove your own admin role.'
})
}
const member = await Member.findByIdAndUpdate(
memberId,
{ role },
{ new: true }
)
if (!member) {
throw createError({
statusCode: 404,
statusMessage: 'Member not found.'
})
}
return { success: true, member }
})

View file

@ -9,7 +9,6 @@ import { emailSchema } from "../../utils/schemas.js";
const resend = new Resend(process.env.RESEND_API_KEY);
export default defineEventHandler(async (event) => {
// Connect to database
await connectDB();
const { email } = await validateBody(event, emailSchema);
@ -19,14 +18,12 @@ export default defineEventHandler(async (event) => {
const member = await Member.findOne({ email });
if (!member) {
// Return same response shape to prevent enumeration
return {
success: true,
message: GENERIC_MESSAGE,
};
}
// Generate magic link token (use runtime config for consistency with verify/requireAuth)
const config = useRuntimeConfig(event);
const token = jwt.sign(
{ memberId: member._id },
@ -34,33 +31,22 @@ export default defineEventHandler(async (event) => {
{ expiresIn: "15m" },
);
// Get the base URL for the magic link
const headers = getHeaders(event);
const baseUrl =
process.env.BASE_URL ||
`${headers.host?.includes("localhost") ? "http" : "https"}://${headers.host}`;
// Send magic link via Resend
try {
await resend.emails.send({
from: "Ghost Guild <ghostguild@babyghosts.org>",
to: email,
subject: "Your Ghost Guild login link",
html: `
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<h2 style="color: #2563eb;">Welcome back to Ghost Guild!</h2>
<p>Click the button below to sign in to your account:</p>
<div style="text-align: center; margin: 30px 0;">
<a href="${baseUrl}/api/auth/verify?token=${token}"
style="background-color: #2563eb; color: white; padding: 12px 24px; text-decoration: none; border-radius: 6px; display: inline-block;">
Sign In to Ghost Guild
</a>
</div>
<p style="color: #666; font-size: 14px;">
This link will expire in 15 minutes for security. If you didn't request this login link, you can safely ignore this email.
</p>
</div>
`,
text: `Hi,
Sign in to Ghost Guild:
${baseUrl}/api/auth/verify?token=${token}
This link expires in 15 minutes. If you didn't request it, ignore this email.`,
});
return {