| /** | |
| * POST /api/solve | |
| * | |
| * Returns a valid Cloudflare Turnstile token for geminigen.ai. | |
| * Protected by optional SOLVER_SECRET environment variable. | |
| * | |
| * Response: { token: string, cached: boolean, solvedInMs?: number } | |
| * Error: { error: string } | |
| */ | |
| import type { VercelRequest, VercelResponse } from "@vercel/node"; | |
| import { solveTurnstile, invalidateCache } from "../src/solver"; | |
| export default async function handler( | |
| req: VercelRequest, | |
| res: VercelResponse | |
| ) { | |
| // CORS preflight | |
| if (req.method === "OPTIONS") { | |
| return res.status(200).end(); | |
| } | |
| if (req.method !== "POST" && req.method !== "GET") { | |
| return res.status(405).json({ error: "Method not allowed" }); | |
| } | |
| // Optional secret check | |
| const secret = process.env.SOLVER_SECRET; | |
| if (secret) { | |
| const provided = | |
| req.headers["x-solver-secret"] || | |
| req.query.secret || | |
| req.body?.secret; | |
| if (provided !== secret) { | |
| return res.status(401).json({ error: "Unauthorized" }); | |
| } | |
| } | |
| // Force-invalidate cached token if requested | |
| if (req.query.force === "1" || req.body?.force) { | |
| invalidateCache(); | |
| } | |
| try { | |
| console.log("[solve] Starting Turnstile solve..."); | |
| const result = await solveTurnstile(); | |
| console.log( | |
| `[solve] Done — cached=${result.cached}, ms=${result.solvedInMs ?? 0}` | |
| ); | |
| return res.json(result); | |
| } catch (err: any) { | |
| console.error("[solve] Error:", err?.message); | |
| return res.status(500).json({ error: err?.message || "Solver failed" }); | |
| } | |
| } | |