assistant-ui--assistant-ui
e30e75b5d4
Changesets / Create Version PR (push) Has been cancelled
Deploy Shadcn Registry / Deploy Production (push) Has been cancelled
Template Metrics / LOC + Bundle Size (push) Has been cancelled
Code Quality / Oxlint + Oxfmt (push) Has been cancelled
Code Quality / Template Sync (push) Has been cancelled
Code Quality / Build Changed Packages (push) Has been cancelled
Code Quality / Test Changed Packages (push) Has been cancelled
Deploy Expo Example / Deploy Production (push) Has been cancelled
Deploy Ink Example / Deploy Production (push) Has been cancelled
Python Tests / pytest (assistant-stream, 3.10) (push) Has been cancelled
Python Tests / pytest (assistant-stream, 3.12) (push) Has been cancelled
Python Tests / pytest (assistant-ui-sync-server-api, 3.10) (push) Has been cancelled
Python Tests / pytest (assistant-ui-sync-server-api, 3.12) (push) Has been cancelled
62 行
1.8 KiB
TypeScript
62 行
1.8 KiB
TypeScript
import { Readable } from "node:stream";
|
|
import { NextResponse } from "next/server";
|
|
import { checkRateLimit } from "@/lib/rate-limit";
|
|
import { isAiPlaygroundEnabled } from "@/lib/feature-flags";
|
|
import { exportWorkspaceArchive } from "../blaxel-sandbox";
|
|
|
|
export const maxDuration = 120;
|
|
|
|
export async function POST(request: Request) {
|
|
if (!isAiPlaygroundEnabled) {
|
|
return NextResponse.json({ error: "Not found." }, { status: 404 });
|
|
}
|
|
|
|
let cleanup: (() => Promise<void>) | undefined;
|
|
const runCleanup = () => {
|
|
const current = cleanup;
|
|
cleanup = undefined;
|
|
return current?.().catch(() => {});
|
|
};
|
|
|
|
try {
|
|
const rateLimitResponse = await checkRateLimit(request);
|
|
if (rateLimitResponse) return rateLimitResponse;
|
|
|
|
const body = (await request.json().catch(() => null)) as {
|
|
sessionId?: unknown;
|
|
} | null;
|
|
const sessionId = body?.sessionId;
|
|
if (typeof sessionId !== "string" || sessionId.length === 0) {
|
|
return NextResponse.json(
|
|
{ error: "Missing sessionId." },
|
|
{ status: 400 },
|
|
);
|
|
}
|
|
|
|
const archive = await exportWorkspaceArchive(sessionId);
|
|
cleanup = archive.cleanup;
|
|
const stream = Readable.toWeb(archive.stream) as ReadableStream;
|
|
archive.stream.on("close", () => {
|
|
void runCleanup();
|
|
});
|
|
archive.stream.on("error", () => {
|
|
void runCleanup();
|
|
});
|
|
|
|
return new Response(stream, {
|
|
headers: {
|
|
"Content-Type": archive.contentType,
|
|
"Content-Disposition": `attachment; filename="${archive.filename}"`,
|
|
"Cache-Control": "no-store",
|
|
},
|
|
});
|
|
} catch (error) {
|
|
console.error("Failed to download Xulux workspace archive", error);
|
|
await runCleanup();
|
|
return NextResponse.json(
|
|
{ error: "Failed to download workspace." },
|
|
{ status: 500 },
|
|
);
|
|
}
|
|
}
|