import { NextRequest, NextResponse } from "next/server"; import { prisma } from "@/lib/db"; const ENRICHMENT_PROMPT = `Du analysierst eine Unternehmenswebsite für ein Solarunternehmen und extrahierst strukturierte Informationen. Antworte ausschließlich mit JSON: { "companyType": "stadtwerke" | "industrie" | "sonstiges", "topics": ["photovoltaik"|"speicher"|"emobilitaet"|"waerme"|"netz"|"wasserstoff"], "salesScore": 1-5, "salesReason": "Ein Satz warum dieser Lead gut/schlecht passt", "offerPackage": "solar-basis" | "solar-pro" | "solar-speicher" | "komplett" } Bewertungskriterien salesScore: 5 = Stadtwerk oder Industriebetrieb mit explizitem Energiethema, perfekter Fit 4 = Energierelevante Branche, wahrscheinlich guter Fit 3 = Mittelständischer Betrieb, möglicher Fit 2 = Kleiner Betrieb oder unklare Relevanz 1 = Offensichtlich nicht relevant (Privatpersonen, Behörden ohne Energie-Relevanz) Angebotspaket-Logik: - solar-basis: Kleine Betriebe <50 Mitarbeiter, residentiell - solar-pro: Mittelstand, Gewerbe - solar-speicher: Energieintensive Betriebe, Stadtwerke - komplett: Stadtwerke, Großindustrie, wenn topics viele Themen enthält Website-Text: [TEXT]`; async function fetchWebsiteText(domain: string): Promise { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 3000); try { const url = domain.startsWith("http") ? domain : `https://${domain}`; const res = await fetch(url, { signal: controller.signal, headers: { "User-Agent": "Mozilla/5.0 (compatible; LeadBot/1.0)" }, }); clearTimeout(timeout); const html = await res.text(); // Strip tags, extract text const text = html .replace(//gi, "") .replace(//gi, "") .replace(/<[^>]+>/g, " ") .replace(/\s+/g, " ") .trim() .slice(0, 2000); return text; } catch { clearTimeout(timeout); return ""; } } interface EnrichmentResult { companyType: string; topics: string[]; salesScore: number; salesReason: string; offerPackage: string; } async function enrichWithAI(text: string, apiKey: string): Promise { if (!text.trim()) return null; const prompt = ENRICHMENT_PROMPT.replace("[TEXT]", text); const res = await fetch("https://openrouter.ai/api/v1/chat/completions", { method: "POST", headers: { "Authorization": `Bearer ${apiKey}`, "Content-Type": "application/json", "HTTP-Referer": "https://mein-solar.com", "X-Title": "MeinSolar Leads", }, body: JSON.stringify({ model: "openai/gpt-4o-mini", temperature: 0.2, max_tokens: 256, messages: [ { role: "user", content: prompt }, ], }), }); if (!res.ok) return null; const data = await res.json() as { choices: Array<{ message: { content: string } }> }; const raw = data.choices[0]?.message?.content?.trim() ?? ""; try { const parsed = JSON.parse(raw) as EnrichmentResult; return parsed; } catch { const match = raw.match(/\{[\s\S]*\}/); if (!match) return null; try { return JSON.parse(match[0]) as EnrichmentResult; } catch { return null; } } } export async function POST(req: NextRequest) { try { const { jobId } = await req.json() as { jobId: string }; if (!jobId) { return NextResponse.json({ error: "jobId fehlt" }, { status: 400 }); } const apiKey = process.env.OPENROUTER_API_KEY; if (!apiKey) { return NextResponse.json({ error: "OpenRouter API Key nicht konfiguriert" }, { status: 500 }); } // Load all LeadResults for this job const results = await prisma.leadResult.findMany({ where: { jobId }, }); // Find corresponding leads with domains const domainsToEnrich = results .filter(r => r.domain) .map(r => r.domain as string); if (domainsToEnrich.length === 0) { return NextResponse.json({ enriched: 0, skipped: 0 }); } // Find leads matching these domains const leads = await prisma.lead.findMany({ where: { domain: { in: domainsToEnrich }, salesScore: null, // only enrich if not already enriched }, }); let enriched = 0; let skipped = 0; for (const lead of leads) { if (!lead.domain) { skipped++; continue; } const text = await fetchWebsiteText(lead.domain); if (!text) { skipped++; continue; } const result = await enrichWithAI(text, apiKey); if (!result) { skipped++; continue; } await prisma.lead.update({ where: { id: lead.id }, data: { companyType: result.companyType || null, topics: result.topics ? JSON.stringify(result.topics) : null, salesScore: typeof result.salesScore === "number" ? result.salesScore : null, salesReason: result.salesReason || null, offerPackage: result.offerPackage || null, }, }); enriched++; } return NextResponse.json({ enriched, skipped, total: leads.length }); } catch (err) { console.error("POST /api/enrich-leads error:", err); return NextResponse.json({ error: "Enrichment failed" }, { status: 500 }); } }