83 lines
2.2 KiB
JavaScript
83 lines
2.2 KiB
JavaScript
// server/api/auth/login.post.js
|
|
import jwt from "jsonwebtoken";
|
|
import { randomUUID } from "crypto";
|
|
import { Resend } from "resend";
|
|
import Member from "../../models/member.js";
|
|
import { connectDB } from "../../utils/mongoose.js";
|
|
import { validateBody } from "../../utils/validateBody.js";
|
|
import { emailSchema } from "../../utils/schemas.js";
|
|
|
|
const resend = new Resend(process.env.RESEND_API_KEY);
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
await connectDB();
|
|
|
|
const baseUrl = process.env.BASE_URL;
|
|
if (!baseUrl) {
|
|
throw createError({
|
|
statusCode: 500,
|
|
statusMessage: "BASE_URL environment variable is not set",
|
|
});
|
|
}
|
|
|
|
const { email } = await validateBody(event, emailSchema);
|
|
|
|
const GENERIC_MESSAGE = "If this email is registered, we've sent a login link.";
|
|
|
|
const member = await Member.findOne({ email });
|
|
|
|
if (!member) {
|
|
return {
|
|
success: true,
|
|
message: GENERIC_MESSAGE,
|
|
};
|
|
}
|
|
|
|
const config = useRuntimeConfig(event);
|
|
const jti = randomUUID();
|
|
|
|
const token = jwt.sign(
|
|
{ memberId: member._id, jti },
|
|
config.jwtSecret,
|
|
{ expiresIn: "15m" },
|
|
);
|
|
|
|
// Store jti so we can burn it on first use
|
|
await Member.findByIdAndUpdate(
|
|
member._id,
|
|
{ $set: { magicLinkJti: jti, magicLinkJtiUsed: false } },
|
|
{ runValidators: false }
|
|
);
|
|
|
|
// Token goes in the fragment — never sent to server, never logged
|
|
const magicLink = `${baseUrl}/verify#${token}`;
|
|
|
|
const emailSubject = "Your Ghost Guild login link";
|
|
const emailBody = `Hi,\n\nSign in to Ghost Guild:\n${magicLink}\n\nThis link expires in 15 minutes. If you didn't request it, ignore this email.`;
|
|
|
|
try {
|
|
await resend.emails.send({
|
|
from: "Ghost Guild <ghostguild@babyghosts.org>",
|
|
to: email,
|
|
subject: emailSubject,
|
|
text: emailBody,
|
|
});
|
|
|
|
logActivity(member._id, 'email_sent', {
|
|
emailType: 'magic_link',
|
|
subject: emailSubject,
|
|
body: emailBody
|
|
})
|
|
|
|
return {
|
|
success: true,
|
|
message: GENERIC_MESSAGE,
|
|
};
|
|
} catch (error) {
|
|
console.error("Failed to send email:", error);
|
|
throw createError({
|
|
statusCode: 500,
|
|
statusMessage: "Failed to send login email. Please try again.",
|
|
});
|
|
}
|
|
});
|