import { NextResponse } from "next/server"; import { getSupabaseAdmin } from "@/lib/admin"; import { TIER_LIMITS } from "@/services/tierLimits"; import { requireOrgMembership } from "@/lib/apiAuth"; /** * GET /api/billing/usage * * Returns current usage vs tier limits for an organization. * Requires authenticated user who is a member of the organization. * Query params: ?organizationId=xxx */ export async function GET(request: Request) { try { const url = new URL(request.url); const organizationId = url.searchParams.get("organizationId"); if (!organizationId) { return NextResponse.json({ error: "organizationId required" }, { status: 400 }); } // Verify caller belongs to this organization const auth = await requireOrgMembership(organizationId, request); if (auth instanceof NextResponse) return auth; const supabase = getSupabaseAdmin(); // Get organization with tier info const { data: org, error: orgError } = await supabase .from("organizations") .select("id, name, subscription_tier, subscription_status, created_at") .eq("id", organizationId) .single(); if (orgError || !org) { return NextResponse.json({ error: "Organization not found" }, { status: 404 }); } const tier = String(org.subscription_tier || "free"); const limits = TIER_LIMITS[tier] || TIER_LIMITS.free; // Get current usage (parallel queries) const startOfMonth = new Date(); startOfMonth.setDate(1); startOfMonth.setHours(0, 0, 0, 0); const [ { count: websiteCount }, { count: memberCount }, { count: scanCountThisMonth }, { count: scanCountTotal }, { count: alertCount }, ] = await Promise.all([ supabase.from("websites").select("*", { count: "exact", head: true }).eq("organization_id", organizationId), supabase.from("organization_members").select("*", { count: "exact", head: true }).eq("organization_id", organizationId), supabase .from("scans") .select("*", { count: "exact", head: true }) .gte("created_at", startOfMonth.toISOString()), supabase.from("scans").select("*", { count: "exact", head: true }), supabase.from("alerts").select("*", { count: "exact", head: true }).eq("status", "active"), ]); const usage = { websites: { used: websiteCount || 0, limit: limits.websites, percentage: limits.websites === -1 ? 0 : Math.round(((websiteCount || 0) / limits.websites) * 100) }, scansThisMonth: { used: scanCountThisMonth || 0, limit: limits.scansPerMonth, percentage: limits.scansPerMonth === -1 ? 0 : Math.round(((scanCountThisMonth || 0) / limits.scansPerMonth) * 100) }, teamMembers: { used: memberCount || 0, limit: limits.teamMembers, percentage: limits.teamMembers === -1 ? 0 : Math.round(((memberCount || 0) / limits.teamMembers) * 100) }, totalScans: scanCountTotal || 0, activeAlerts: alertCount || 0, }; return NextResponse.json({ organization: { id: org.id, name: org.name, tier, status: org.subscription_status || "active", createdAt: org.created_at, }, plan: limits, usage, features: { scheduledScans: limits.scheduledScans, alertNotifications: limits.alertNotifications, competitorAnalysis: limits.competitorAnalysis, apiAccess: limits.apiAccess, prioritySupport: limits.prioritySupport, }, }); } catch (error) { return NextResponse.json( { error: error instanceof Error ? error.message : "Unknown error" }, { status: 500 } ); } }