import { NextResponse } from "next/server"; import { getSupabaseAdmin } from "@/lib/admin"; /** * GET /api/competitor-analysis?websiteId=xxx * * Returns your website's latest scores alongside competitor scores. */ export async function GET(request: Request) { try { const supabase = getSupabaseAdmin(); const url = new URL(request.url); const websiteId = url.searchParams.get("websiteId"); if (!websiteId) { return NextResponse.json({ error: "websiteId required" }, { status: 400 }); } // Get your website's latest scan results const { data: website } = await supabase .from("websites") .select("id, name, base_url") .eq("id", websiteId) .single(); if (!website) { return NextResponse.json({ error: "Website not found" }, { status: 404 }); } // Get latest scan results for your site const { data: yourScans } = await supabase .from("scan_results") .select("category, score, scans(website_id, created_at)") .eq("scans.website_id", websiteId) .order("created_at", { ascending: false, referencedTable: "scans" }) .limit(4); const yourScores: Record = {}; for (const scan of yourScans || []) { const category = String(scan.category || ""); const score = Number(scan.score); if (category && !isNaN(score) && !yourScores[category]) { yourScores[category] = score; } } // Get competitor entries for this website const { data: competitors } = await supabase .from("competitor_metrics") .select("*") .eq("website_id", websiteId); return NextResponse.json({ yourSite: { id: website.id, name: website.name, url: website.base_url, scores: { performance: yourScores.performance ?? null, seo: yourScores.seo ?? null, accessibility: yourScores.accessibility ?? null, bestPractices: yourScores.best_practices ?? yourScores.bestPractices ?? null, }, }, competitors: (competitors || []).map((c) => ({ id: c.id, name: c.name || c.url, url: c.url, scores: { performance: c.performance_score, seo: c.seo_score, accessibility: c.accessibility_score, bestPractices: c.best_practices_score, }, lastScanned: c.last_scanned_at, })), }); } catch (error) { console.error("Competitor analysis error:", error); return NextResponse.json( { error: "Failed to fetch competitor data" }, { status: 500 } ); } } /** * POST /api/competitor-analysis * * Add a competitor and scan it with Lighthouse. * Body: { websiteId, competitorUrl, competitorName } */ export async function POST(request: Request) { try { const supabase = getSupabaseAdmin(); const { websiteId, competitorUrl, competitorName } = await request.json(); if (!websiteId || !competitorUrl) { return NextResponse.json( { error: "websiteId and competitorUrl required" }, { status: 400 } ); } // Validate URL try { new URL(competitorUrl); } catch { return NextResponse.json({ error: "Invalid URL" }, { status: 400 }); } // Run a lightweight fetch-based check (no full Lighthouse to save resources) const start = Date.now(); let statusCode = null; let responseTime = 0; try { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 15000); const res = await fetch(competitorUrl, { method: "GET", signal: controller.signal, headers: { "User-Agent": "WebsiteMonitor/1.0 (Competitor Analysis)" }, }); clearTimeout(timeout); statusCode = res.status; responseTime = Date.now() - start; } catch { responseTime = Date.now() - start; } // Insert competitor record const { data: competitor, error } = await supabase .from("competitor_metrics") .upsert( { website_id: websiteId, url: competitorUrl, name: competitorName || new URL(competitorUrl).hostname, status_code: statusCode, response_time: responseTime, last_scanned_at: new Date().toISOString(), }, { onConflict: "website_id,url" } ) .select() .single(); if (error) throw error; return NextResponse.json({ success: true, competitor, message: `Competitor added: ${competitorUrl} (${responseTime}ms)`, }); } catch (error) { console.error("Competitor add error:", error); return NextResponse.json( { error: error instanceof Error ? error.message : "Failed to add competitor" }, { status: 500 } ); } }