fix: add Resend for password reset emails + install F1-F2 deps

- Wire Resend to /api/auth/reset-request with fallback for dev
- Install: sharp, recharts, next-pwa, resend, @react-pdf/renderer, @types/sharp

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Manohar Gupta 2026-05-17 16:42:45 +05:30
parent c459b4411a
commit c787caa821
3 changed files with 4902 additions and 14 deletions

View file

@ -11,6 +11,7 @@
"@auth/drizzle-adapter": "^1.11.2",
"@aws-sdk/client-s3": "^3.1045.0",
"@aws-sdk/s3-request-presigner": "^3.1045.0",
"@react-pdf/renderer": "^4.5.1",
"bcryptjs": "^3.0.3",
"chart.js": "^4.5.1",
"date-fns": "^4.1.0",
@ -19,6 +20,7 @@
"nanoid": "^5.1.11",
"next": "16.2.6",
"next-auth": "5.0.0-beta.31",
"next-pwa": "^5.6.0",
"nodemailer": "^7.0.13",
"openai": "^6.37.0",
"postgres": "^3.4.9",
@ -26,6 +28,9 @@
"react": "19.2.4",
"react-chartjs-2": "^5.3.1",
"react-dom": "19.2.4",
"recharts": "^3.8.1",
"resend": "^6.12.3",
"sharp": "^0.34.5",
"zod": "^4.4.3"
},
"devDependencies": {
@ -34,6 +39,7 @@
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"@types/sharp": "^0.32.0",
"drizzle-kit": "^0.31.10",
"tailwindcss": "^4",
"tsx": "^4.21.0",

4875
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

View file

@ -2,6 +2,11 @@ import { NextResponse } from "next/server";
import { sql } from "@/db";
import { rateLimit, getClientIp, getRateLimitKey } from "@/lib/rate-limit";
import { randomBytes } from "crypto";
import { Resend } from "resend";
const RESEND_API_KEY = process.env.RESEND_API_KEY;
const EMAIL_FROM = process.env.EMAIL_FROM || "Tia <noreply@tia.manohargupta.com>";
const RESET_URL = process.env.NEXT_PUBLIC_APP_URL || "https://tia.manohargupta.com";
export async function POST(request: Request) {
const ip = getClientIp(request);
@ -36,8 +41,34 @@ export async function POST(request: Request) {
[user.id, token, expiresAt.toISOString()]
);
// In production, send email with reset link
const resetLink = `${RESET_URL}/reset-password?token=${token}`;
// Send email via Resend if API key is configured
if (RESEND_API_KEY) {
try {
const resend = new Resend(RESEND_API_KEY);
await resend.emails.send({
from: EMAIL_FROM,
to: email,
subject: "Reset your Tia password",
html: `
<div style="font-family: system-ui, sans-serif; max-width: 500px; margin: 0 auto;">
<h2>Reset your Tia password</h2>
<p>Click the button below to reset your password. This link expires in 1 hour.</p>
<a href="${resetLink}" style="display: inline-block; background: #2563eb; color: white; padding: 12px 24px; text-decoration: none; border-radius: 6px; margin: 16px 0;">Reset Password</a>
<p style="color: #6b7280; font-size: 14px;">If you didn't request this, you can safely ignore this email.</p>
</div>
`,
});
console.log(`[RESET-EMAIL-SENT] user=${user.id} email=${email}`);
} catch (emailError) {
console.error("[RESET-EMAIL-FAILED]", emailError);
}
} else {
// Development fallback
console.log(`[RESET-TOKEN] user=${user.id} email=${email} token=reset_${token} expires=${expiresAt.toISOString()}`);
}
return NextResponse.json({ success: true, message: "If email exists, reset link sent" });
} catch (error) {
console.error("Reset request error:", error);