TypeScript 97.5%
SQL 1.4%
Python 0.8%
1import Link from "next/link";2import { Waypoints } from "lucide-react";3import { getWorkspace } from "@/lib/session";4import { API_PUBLIC_URL } from "@/lib/utils";5import { listProjectSessions, type Scope } from "@/lib/queries/dashboard";6import { PageHeader } from "@/components/ui/page-header";7import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";8import { EmptyState } from "@/components/ui/empty-state";9import { Stat, StatGrid } from "@/components/ui/stat";10import { CodeBlock } from "@/components/ui/code-block";11import { formatNumber } from "@/lib/format";12import { SessionsTable } from "@/components/dashboard/sessions/sessions-table";13import { CreateSessionDialog } from "@/components/dashboard/sessions/create-session-dialog";1415export const dynamic = "force-dynamic";1617const API_SNIPPET = `# 1. Create a session (60–1800 s)18curl -X POST ${API_PUBLIC_URL}/v1/sessions \\19 -H "Authorization: Bearer fch_live_YOUR_KEY" \\20 -H "Content-Type: application/json" \\21 -d '{ "country": "CA", "ttl": 600, "label": "checkout" }'22# → { "id": "sess_…", "status": "active", … }2324# 2. Reuse it on every request that must share the same IP and cookies25curl -X POST ${API_PUBLIC_URL}/v1/fetch \\26 -H "Authorization: Bearer fch_live_YOUR_KEY" \\27 -H "Content-Type: application/json" \\28 -d '{ "url": "https://example.com/cart", "session": "sess_…" }'`;2930export default async function SessionsPage() {31 const ws = await getWorkspace();32 const scope: Scope = { organizationId: ws.organization.id, projectId: ws.project.id };33 const rows = await listProjectSessions(scope);34 const active = rows.filter((r) => r.status === "active");35 const totalRequests = rows.reduce((a, r) => a + r.requestCount, 0);3637 return (38 <div className="flex flex-col gap-5">39 <PageHeader40 title="Sessions"41 description={`Sticky sessions for ${ws.project.name}. A session keeps the same exit IP and cookie jar across requests until it expires or is closed.`}42 actions={<CreateSessionDialog apiBase={API_PUBLIC_URL} defaultCountry={ws.project.defaultCountry} />}43 />4445 <StatGrid cols={3}>46 <Stat label="Active sessions" value={formatNumber(active.length)} hint="expire automatically" />47 <Stat label="Sessions created" value={formatNumber(rows.length)} hint="in this project" />48 <Stat label="Requests via sessions" value={formatNumber(totalRequests)} hint="all time" />49 </StatGrid>5051 {rows.length ? (52 <Card className="overflow-hidden">53 <SessionsTable rows={rows} />54 </Card>55 ) : (56 <EmptyState57 icon={Waypoints}58 title="No sessions yet"59 description="Create one here to try it, or from your code with POST /v1/sessions. Sessions are optional: stateless requests get a fresh IP every time."60 action={<CreateSessionDialog apiBase={API_PUBLIC_URL} defaultCountry={ws.project.defaultCountry} />}61 />62 )}6364 <div className="grid gap-4 lg:grid-cols-2">65 <Card>66 <CardHeader>67 <CardTitle>How sessions work</CardTitle>68 <CardDescription>When to pin an IP, and when not to.</CardDescription>69 </CardHeader>70 <CardContent className="space-y-3 text-[13px] leading-relaxed text-fg-muted">71 <p>72 <strong className="text-fg">Same IP, same cookies.</strong> Every request that passes <code className="font-mono">session: "sess_…"</code> is routed through the same exit IP in the requested country, and cookies set by the target are replayed automatically. Use it for logins, carts, multi-step forms and paginated listings that break when the visitor changes.73 </p>74 <p>75 <strong className="text-fg">Short-lived by design.</strong> TTL is 60 to 1800 seconds and does not extend on use. When a session expires or the IP is rotated away by the network, requests fail with <code className="font-mono">SESSION_EXPIRED</code>; create a new one and retry.76 </p>77 <p>78 <strong className="text-fg">Network class.</strong> Only <code className="font-mono">auto</code> and <code className="font-mono">residential</code> are available for sessions today. Datacenter, ISP and mobile are coming soon.79 </p>80 <p>81 <strong className="text-fg">Billing.</strong> Sessions are free; you pay for the requests and bandwidth that go through them. Closing a session early frees the sticky slot immediately.82 </p>83 <p className="text-fg-subtle">84 Requests made through a session are listed in <Link href="/dashboard/requests" className="underline-offset-4 hover:underline">Requests</Link> with the session id on the detail page.85 </p>86 </CardContent>87 </Card>88 <CodeBlock code={API_SNIPPET} lang="bash" title="Sessions from the API" className="self-start" />89 </div>90 </div>91 );92}93