diff --git a/apps/web/app/api/onboarding/research/route.ts b/apps/web/app/api/onboarding/research/route.ts index 67bf4654..8fc39b23 100644 --- a/apps/web/app/api/onboarding/research/route.ts +++ b/apps/web/app/api/onboarding/research/route.ts @@ -7,16 +7,28 @@ interface ResearchRequest { email?: string } -function extractHandle(url: string): string { - const cleaned = url - .toLowerCase() - .replace("https://x.com/", "") - .replace("https://twitter.com/", "") - .replace("http://x.com/", "") - .replace("http://twitter.com/", "") - .replace("@", "") +function extractHandle(input: string): string { + const trimmed = input.trim() + if (!trimmed) return "" - return (cleaned.split("/")[0] ?? cleaned).split("?")[0] ?? cleaned + let handle = trimmed.replace(/^@+/, "") + const lower = handle.toLowerCase() + + if (lower.includes("x.com") || lower.includes("twitter.com")) { + try { + const parsed = new URL( + handle.startsWith("http://") || handle.startsWith("https://") + ? handle + : `https://${handle}`, + ) + handle = parsed.pathname.split("/").filter(Boolean)[0] ?? "" + } catch { + handle = + handle.match(/(?:x\.com|twitter\.com)\/([^/\s?#]+)/i)?.[1] ?? "" + } + } + + return handle.replace(/^@+/, "").split(/[/?#]/)[0]?.toLowerCase() ?? "" } function finalPrompt(handle: string, userContext: string) { @@ -47,6 +59,13 @@ export async function POST(req: Request) { const handle = extractHandle(xUrl) + if (!/^[A-Za-z0-9_]{1,15}$/.test(handle)) { + return Response.json( + { error: "Could not parse a valid X/Twitter handle from the input" }, + { status: 400 }, + ) + } + const contextParts: string[] = [] if (name) contextParts.push(`Name: ${name}`) if (email) contextParts.push(`Email: ${email}`)