Spaces:
Sleeping
Sleeping
| import { execFile } from "node:child_process"; | |
| import { promisify } from "node:util"; | |
| import { mkdirSync } from "node:fs"; | |
| import path from "node:path"; | |
| import crypto from "node:crypto"; | |
| import type { NextRequest } from "next/server"; | |
| import { NextResponse } from "next/server"; | |
| const execFileAsync = promisify(execFile); | |
| const SESSION_COOKIE = "cardcli_demo_session"; | |
| const DEMO_HOME_ROOT = process.env.CARDCLI_DEMO_HOME_ROOT ?? "/tmp/cardcli-demo"; | |
| const SHARED_DEMO_SESSION = (process.env.CARDCLI_DEMO_SHARED_SESSION ?? "true") === "true"; | |
| function cardCliBinPath() { | |
| return ( | |
| process.env.CARDCLI_BIN ?? | |
| path.resolve(process.cwd(), "..", "target", "release", process.platform === "win32" ? "card-cli.exe" : "card-cli") | |
| ); | |
| } | |
| function sessionIdFromRequest(request: NextRequest) { | |
| if (SHARED_DEMO_SESSION) { | |
| return "shared-demo"; | |
| } | |
| return request.cookies.get(SESSION_COOKIE)?.value ?? crypto.randomUUID(); | |
| } | |
| function cardCliHome(sessionId: string) { | |
| const dir = path.join(DEMO_HOME_ROOT, "sessions", sessionId); | |
| mkdirSync(dir, { recursive: true }); | |
| return dir; | |
| } | |
| function cardCliEnv(sessionId: string): NodeJS.ProcessEnv { | |
| return { | |
| ...process.env, | |
| CARDCLI_HOME: cardCliHome(sessionId), | |
| CARDCLI_SOLANA_PRIVATE_KEY_BASE58: | |
| process.env.CARDCLI_DEMO_SOLANA_PRIVATE_KEY ?? process.env.CARDCLI_SOLANA_PRIVATE_KEY_BASE58 ?? "", | |
| CARDCLI_FEE_PROGRAM_ID: | |
| process.env.CARDCLI_DEMO_FEE_PROGRAM_ID ?? process.env.CARDCLI_FEE_PROGRAM_ID ?? "EpB6hUZUf1vvvTVAYvEN57pjUWfYswaAuKGGQDHP5iH", | |
| CARDCLI_ONCHAIN_FEE_COLLECTION_ENABLED: | |
| process.env.CARDCLI_ONCHAIN_FEE_COLLECTION_ENABLED ?? "true", | |
| CARDCLI_FEE_FIXED_CENTS: process.env.CARDCLI_FEE_FIXED_CENTS ?? "10", | |
| CARDCLI_FEE_VARIABLE_BPS: process.env.CARDCLI_FEE_VARIABLE_BPS ?? "20", | |
| CARDCLI_RPC_URL: process.env.CARDCLI_RPC_URL ?? "https://api.devnet.solana.com", | |
| CARDCLI_COINBASE_BASE_URL: process.env.CARDCLI_COINBASE_BASE_URL ?? "https://api.coinbase.com", | |
| }; | |
| } | |
| export async function runCardCli( | |
| request: NextRequest, | |
| args: string[], | |
| ): Promise<{ sessionId: string; payload: unknown; stderr?: string }> { | |
| const sessionId = sessionIdFromRequest(request); | |
| const env = cardCliEnv(sessionId); | |
| const bin = cardCliBinPath(); | |
| try { | |
| const { stdout, stderr } = await execFileAsync(bin, [...args, "--format", "json"], { | |
| env, | |
| maxBuffer: 10 * 1024 * 1024, | |
| }); | |
| return { | |
| sessionId, | |
| payload: JSON.parse(stdout), | |
| stderr, | |
| }; | |
| } catch (error) { | |
| const err = error as { stdout?: string; stderr?: string; message?: string }; | |
| const raw = err.stderr || err.stdout || JSON.stringify({ ok: false, error: { message: err.message } }); | |
| try { | |
| return { | |
| sessionId, | |
| payload: JSON.parse(raw), | |
| stderr: err.stderr, | |
| }; | |
| } catch { | |
| return { | |
| sessionId, | |
| payload: { | |
| ok: false, | |
| error: { | |
| code: "WEB_EXEC_ERROR", | |
| message: err.message ?? "Failed to run card-cli", | |
| raw, | |
| }, | |
| }, | |
| stderr: err.stderr, | |
| }; | |
| } | |
| } | |
| } | |
| export function jsonWithSession(sessionId: string, payload: unknown, init?: ResponseInit) { | |
| const response = NextResponse.json(payload, init); | |
| if (!SHARED_DEMO_SESSION) { | |
| response.cookies.set(SESSION_COOKIE, sessionId, { | |
| httpOnly: true, | |
| sameSite: "lax", | |
| path: "/", | |
| maxAge: 60 * 60 * 24, | |
| }); | |
| } | |
| return response; | |
| } | |