← back to Trademarks Copyright
src/app/api/drops/unsubscribe/[token]/route.ts
40 lines
import { NextRequest, NextResponse } from "next/server";
import { query } from "@/lib/db";
async function unsubscribe(token: string) {
const { rowCount } = await query(
`UPDATE subscribers SET status = 'cancelled', cancelled_at = NOW() WHERE token = $1`,
[token]
) as unknown as { rowCount: number };
return rowCount > 0;
}
// POST is required by Gmail/Yahoo one-click unsubscribe (RFC 8058).
export async function POST(_req: NextRequest, { params }: { params: Promise<{ token: string }> }) {
const { token } = await params;
const ok = await unsubscribe(token);
if (!ok) return NextResponse.json({ error: "not found" }, { status: 404 });
return NextResponse.json({ ok: true });
}
// GET supports email-client clickthroughs that don't POST.
// Returns a confirmation page rather than JSON.
export async function GET(_req: NextRequest, { params }: { params: Promise<{ token: string }> }) {
const { token } = await params;
const ok = await unsubscribe(token);
const html = ok
? `<!doctype html><html><body style="font-family:Georgia,serif;max-width:500px;margin:80px auto;padding:20px;text-align:center;">
<h1>Unsubscribed.</h1>
<p>You won't receive any more drops. Sorry to see you go.</p>
<p style="font-size:13px;color:#888;">If this was an accident, <a href="/drops">sign up again</a>.</p>
</body></html>`
: `<!doctype html><html><body style="font-family:Georgia,serif;max-width:500px;margin:80px auto;padding:20px;text-align:center;">
<h1>Unsubscribe link invalid.</h1>
<p>This token wasn't recognized. If you still receive drops, reply directly and we'll remove you.</p>
</body></html>`;
return new NextResponse(html, {
status: ok ? 200 : 404,
headers: { "content-type": "text/html" },
});
}