"use server"; import { revalidatePath } from "next/cache"; import { z } from "zod"; import { COUNTRIES } from "@fetcha/core"; import { getWorkspace } from "@/lib/session"; import { internalApi, InternalApiError } from "@/lib/api"; import type { ActionResult } from "./projects"; /** Session as returned by the Fetcha API (`POST /v1/sessions`). Provider details are never included. */ export interface DashboardSession { id: string; label: string | null; status: string; network: string; country: string | null; region: string | null; city: string | null; request_count: number; last_used_at: string | null; expires_at: string; created_at: string; } const createSchema = z.object({ country: z .string() .trim() .length(2) .toUpperCase() .refine((c) => c in COUNTRIES, "Unsupported country") .optional(), region: z.string().trim().max(64).optional(), city: z.string().trim().max(128).optional(), // Only `auto` and `residential` are live in V1; the API rejects the others with NETWORK_UNAVAILABLE. network: z.enum(["auto", "residential", "datacenter", "isp", "mobile"]).default("auto"), ttl: z.coerce.number().int().min(60).max(1800).default(600), label: z.string().trim().max(128).optional(), }); export type CreateSessionInput = z.input; function clean>(o: T): Partial { return Object.fromEntries(Object.entries(o).filter(([, v]) => v !== undefined && v !== "")) as Partial; } export async function createDashboardSession(input: CreateSessionInput): Promise> { const ws = await getWorkspace(); const parsed = createSchema.safeParse(clean(input as Record)); if (!parsed.success) return { ok: false, error: parsed.error.issues[0]?.message ?? "Invalid input", field: String(parsed.error.issues[0]?.path[0] ?? "") }; try { const session = (await internalApi.createSession(ws.project.id, ws.user.id, clean(parsed.data))) as DashboardSession; revalidatePath("/dashboard/sessions"); revalidatePath("/dashboard"); return { ok: true, data: session }; } catch (e) { if (e instanceof InternalApiError) return { ok: false, error: e.code === "NETWORK_UNAVAILABLE" ? "That network class is not available yet. Use auto or residential." : e.message }; return { ok: false, error: "Could not create the session. Please try again." }; } } export async function closeDashboardSession(id: string): Promise { const ws = await getWorkspace(); if (!/^sess_[A-Za-z0-9]{4,64}$/.test(id)) return { ok: false, error: "Invalid session id" }; try { await internalApi.closeSession(ws.project.id, ws.user.id, id); revalidatePath("/dashboard/sessions"); revalidatePath("/dashboard"); return { ok: true }; } catch (e) { if (e instanceof InternalApiError) { if (e.code === "SESSION_NOT_FOUND") return { ok: false, error: "Session not found in this project." }; return { ok: false, error: e.message }; } return { ok: false, error: "Could not close the session. Please try again." }; } }