Files
avbeat-com/src/app/api/auth/lookup-email/route.ts
Local Administrator 25abc55da1 feat(avbeat): point forum + auth at av schema, swap newsletter chips to AV verticals
Schema wiring — AV Beat was inheriting a 'bb' default schema from the
original fork. Fixed three call sites so the AV Beat app reads/writes
the av schema unless an explicit NEXT_PUBLIC_SUPABASE_SCHEMA override
is set:
  - src/lib/supabase/client.tsx:8 default 'bb' -> 'av'
  - src/lib/supabase/server.tsx:11 default 'bb' -> 'av'
  - src/app/api/auth/lookup-email/route.ts :31 + :42 hardcoded
    .schema('bb') -> .schema('av') so signup lookup hits the
    AV-side subscriber tables, not BB's.

Newsletter signup — homepage chips were the BB taxonomy ('Broadcast',
'Post Production', 'Animation', 'AI & Automation', 'Live Production',
'NAB Show'). Swapped for pro-AV verticals that align with the new
forum categories: Conference Rooms & UC, Digital Signage, Live Events
& Staging, Displays & LED, Audio, AV over IP, InfoComm & ISE.
Default selected flipped from 'Broadcast' to 'Conference Rooms & UC'
so the form doesn't pre-select a non-existent value.
2026-06-03 14:39:12 +00:00

52 lines
1.8 KiB
TypeScript

/**
* GET /api/auth/lookup-email?email=foo@example.com
*
* Returns { known: true|false }. "Known" means the email is already on our
* subscriber list (bb.email_subscribers) OR has an auth.users entry. Used
* by the register page so existing newsletter readers get a magic-link
* login flow instead of being asked to create a new account.
*
* Privacy: returns only known/not-known, never any details — does not
* leak account existence beyond a binary signal (necessary for the UX).
* Rate-limited at the proxy layer.
*/
import { NextRequest, NextResponse } from "next/server";
import { createClient as createService } from "@supabase/supabase-js";
export async function GET(req: NextRequest) {
const email = (new URL(req.url).searchParams.get("email") || "").trim().toLowerCase();
if (!email || !email.includes("@")) {
return NextResponse.json({ known: false }, { status: 400 });
}
const url = process.env.NEXT_PUBLIC_SUPABASE_URL || "";
const key = process.env.SUPABASE_SERVICE_ROLE_KEY || "";
if (!url || !key) {
return NextResponse.json({ known: false });
}
const svc = createService(url, key, { auth: { persistSession: false } });
// Layer 1 — the rich subscriber registry (95k+ rows)
const { data: sub } = await svc
.schema("av")
.from("email_subscribers")
.select("id, status")
.eq("email_domain", email.split("@")[1])
.ilike("email", email)
.limit(1)
.maybeSingle();
if (sub) return NextResponse.json({ known: true, source: "subscriber" });
// Layer 2 — the lean property-specific list
const { data: nl } = await svc
.schema("av")
.from("newsletter_subscribers")
.select("id")
.ilike("email", email)
.limit(1)
.maybeSingle();
if (nl) return NextResponse.json({ known: true, source: "newsletter" });
return NextResponse.json({ known: false });
}