import Link from "next/link"; import { ArrowRight, Check, KeyRound, ShieldCheck } from "lucide-react"; import { PLAN_LIMITS, isUnlimited, normalizePlan } from "@fetcha/core"; import { getWorkspace } from "@/lib/session"; import { monthlyUsage } from "@/lib/queries/account"; import { formatBytes, formatNumber, formatUsd, formatDateOnly } from "@/lib/format"; import { PageHeader } from "@/components/ui/page-header"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Alert } from "@/components/ui/alert"; export const dynamic = "force-dynamic"; export const metadata = { title: "Plan & access · Fetcha" }; function seconds(ms: number) { return `${ms / 1000} s`; } export default async function BillingPage() { const ws = await getWorkspace(); const limits = PLAN_LIMITS[normalizePlan(ws.organization.plan)]; const unlimited = isUnlimited(limits); const now = new Date(); const monthStart = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1)); const nextMonth = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() + 1, 1)); const usage = await monthlyUsage(ws.organization.id); const included: Array<{ label: string; detail?: string }> = [ { label: unlimited ? "Unlimited requests" : `${formatNumber(limits.monthly_requests)} requests / month`, detail: "no monthly quota, no overage" }, { label: `${formatNumber(limits.concurrency)} concurrent requests`, detail: "per organization, all projects together" }, { label: `${seconds(limits.max_timeout_ms)} maximum timeout`, detail: `up to ${limits.max_retries} automatic retries per request` }, { label: "All network classes", detail: limits.networks.map((n) => n[0]!.toUpperCase() + n.slice(1)).join(", ") }, { label: "Managed browser rendering", detail: `${limits.browser_concurrency} concurrent renders, routed through the same networks` }, { label: `Crawl jobs up to ${formatNumber(limits.crawl_max_pages)} pages`, detail: `${limits.crawl_concurrent_jobs} jobs in parallel, sitemap discovery and Markdown output` }, { label: "Sticky sessions and geo targeting", detail: "country, region and city on every request" }, { label: `${limits.retention_days}-day request logs`, detail: "full metadata, attempts and timings" }, ]; return (
Spend is an internal cost estimate used only for your optional spending limits. Nothing is invoiced.
Detailed usage