Merge S7 (a1): newsletter — THE VYNDR REPORT
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> # Conflicts: # BUILD-STATE.md # CLAUDE.md
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
const BACKEND_URL = process.env.BACKEND_URL || 'http://localhost:3000';
|
||||
|
||||
/**
|
||||
* Newsletter subscribe proxy (Session S7, a1) — S25 rule: the browser hits
|
||||
* the Next origin, never Express directly. Forwards to the backend, which
|
||||
* forwards to the self-hosted Listmonk (double opt-in).
|
||||
*
|
||||
* Failure posture: any upstream problem degrades to the calm
|
||||
* { ok: false, reason: 'not configured' } shape — the capture component
|
||||
* renders "Signups open soon", never an error wall.
|
||||
*/
|
||||
export async function POST(req: NextRequest) {
|
||||
let body: unknown = {};
|
||||
try {
|
||||
body = await req.json();
|
||||
} catch {
|
||||
return NextResponse.json({ ok: false, error: 'Enter a valid email.' }, { status: 400 });
|
||||
}
|
||||
try {
|
||||
const upstream = await fetch(`${BACKEND_URL}/api/newsletter/subscribe`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
||||
body: JSON.stringify(body),
|
||||
cache: 'no-store',
|
||||
});
|
||||
const data = await upstream.json().catch(() => ({ ok: false, reason: 'not configured' }));
|
||||
return NextResponse.json(data, { status: upstream.status === 400 ? 400 : 200 });
|
||||
} catch {
|
||||
return NextResponse.json({ ok: false, reason: 'not configured' }, { status: 200 });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user