88 lines
2.4 KiB
TypeScript
88 lines
2.4 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server";
|
|
import { z } from "zod";
|
|
import { convexAuthNextjsToken, isAuthenticatedNextjs } from "@convex-dev/auth/nextjs/server";
|
|
import { fetchMutation, fetchQuery } from "convex/nextjs";
|
|
import { api } from "@/convex/_generated/api";
|
|
import { scrapeWebsite, analyzeFromText } from "@/lib/scraper";
|
|
import { repromptSection } from "@/lib/analysis-pipeline";
|
|
|
|
const bodySchema = z.object({
|
|
analysisId: z.string().min(1),
|
|
sectionKey: z.enum([
|
|
"profile",
|
|
"features",
|
|
"competitors",
|
|
"keywords",
|
|
"problems",
|
|
"personas",
|
|
"useCases",
|
|
"dorkQueries",
|
|
]),
|
|
prompt: z.string().optional(),
|
|
});
|
|
|
|
export async function POST(request: NextRequest) {
|
|
if (!(await isAuthenticatedNextjs())) {
|
|
const redirectUrl = new URL("/auth", request.url);
|
|
const referer = request.headers.get("referer");
|
|
const nextPath = referer ? new URL(referer).pathname + new URL(referer).search : "/";
|
|
redirectUrl.searchParams.set("next", nextPath);
|
|
return NextResponse.redirect(redirectUrl);
|
|
}
|
|
|
|
const body = await request.json();
|
|
const parsed = bodySchema.parse(body);
|
|
const token = await convexAuthNextjsToken();
|
|
|
|
const analysis = await fetchQuery(
|
|
api.analyses.getById,
|
|
{ analysisId: parsed.analysisId as any },
|
|
{ token }
|
|
);
|
|
|
|
if (!analysis) {
|
|
return NextResponse.json({ error: "Analysis not found." }, { status: 404 });
|
|
}
|
|
|
|
const dataSource = await fetchQuery(
|
|
api.dataSources.getById,
|
|
{ dataSourceId: analysis.dataSourceId as any },
|
|
{ token }
|
|
);
|
|
|
|
if (!dataSource) {
|
|
return NextResponse.json({ error: "Data source not found." }, { status: 404 });
|
|
}
|
|
|
|
const isManual = dataSource.url.startsWith("manual:") || dataSource.url === "manual-input";
|
|
const featureText = (analysis.features || []).map((f: any) => f.name).join("\n");
|
|
const content = isManual
|
|
? await analyzeFromText(
|
|
analysis.productName,
|
|
analysis.description || "",
|
|
featureText
|
|
)
|
|
: await scrapeWebsite(dataSource.url);
|
|
|
|
const items = await repromptSection(
|
|
parsed.sectionKey,
|
|
content,
|
|
analysis as any,
|
|
parsed.prompt
|
|
);
|
|
|
|
await fetchMutation(
|
|
api.analysisSections.replaceSection,
|
|
{
|
|
analysisId: parsed.analysisId as any,
|
|
sectionKey: parsed.sectionKey,
|
|
items,
|
|
lastPrompt: parsed.prompt,
|
|
source: "ai",
|
|
},
|
|
{ token }
|
|
);
|
|
|
|
return NextResponse.json({ success: true, items });
|
|
}
|