SPB Git forge

spb/fetcha

Public
11commits 1branches 0releases
1.5 MBsize
maindefault branch
16 days agolast push
TypeScript 97.5% SQL 1.4% Python 0.8%
4.9 KB · 93 lines tsx
Raw Blame History
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: &quot;sess_…&quot;</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