106 lines
3.2 KiB
TypeScript
106 lines
3.2 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server";
|
|
import { getZine, readFileAsBuffer, getPageImagePath, getPrintLayoutPath } from "@/lib/storage";
|
|
|
|
interface RouteContext {
|
|
params: Promise<{ id: string }>;
|
|
}
|
|
|
|
// GET /api/zine/[id] - Get zine metadata
|
|
// GET /api/zine/[id]?image=p1 - Get page image
|
|
// GET /api/zine/[id]?print=true - Get print layout
|
|
export async function GET(request: NextRequest, context: RouteContext) {
|
|
try {
|
|
const { id } = await context.params;
|
|
const url = new URL(request.url);
|
|
const imageParam = url.searchParams.get("image");
|
|
const printParam = url.searchParams.get("print");
|
|
|
|
// Serve page image
|
|
if (imageParam) {
|
|
const pageMatch = imageParam.match(/^p(\d)$/);
|
|
if (!pageMatch) {
|
|
return NextResponse.json(
|
|
{ error: "Invalid image parameter. Use p1-p8." },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
const pageNumber = parseInt(pageMatch[1], 10);
|
|
if (pageNumber < 1 || pageNumber > 8) {
|
|
return NextResponse.json(
|
|
{ error: "Page number must be between 1 and 8" },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
const imagePath = await getPageImagePath(id, pageNumber);
|
|
if (!imagePath) {
|
|
return NextResponse.json(
|
|
{ error: "Page image not found" },
|
|
{ status: 404 }
|
|
);
|
|
}
|
|
|
|
const imageBuffer = await readFileAsBuffer(imagePath);
|
|
return new NextResponse(new Uint8Array(imageBuffer), {
|
|
headers: {
|
|
"Content-Type": "image/png",
|
|
"Cache-Control": "public, max-age=31536000, immutable",
|
|
},
|
|
});
|
|
}
|
|
|
|
// Serve print layout
|
|
if (printParam === "true") {
|
|
const printPath = await getPrintLayoutPath(id);
|
|
if (!printPath) {
|
|
return NextResponse.json(
|
|
{ error: "Print layout not found. Generate it first." },
|
|
{ status: 404 }
|
|
);
|
|
}
|
|
|
|
const printBuffer = await readFileAsBuffer(printPath);
|
|
const downloadParam = url.searchParams.get("download");
|
|
|
|
const headers: Record<string, string> = {
|
|
"Content-Type": "image/png",
|
|
"Cache-Control": "public, max-age=31536000, immutable",
|
|
};
|
|
|
|
// Only add Content-Disposition for explicit downloads
|
|
if (downloadParam === "true") {
|
|
headers["Content-Disposition"] = `attachment; filename="${id}_print.png"`;
|
|
}
|
|
|
|
return new NextResponse(new Uint8Array(printBuffer), { headers });
|
|
}
|
|
|
|
// Return zine metadata
|
|
const zine = await getZine(id);
|
|
if (!zine) {
|
|
return NextResponse.json(
|
|
{ error: "Zine not found" },
|
|
{ status: 404 }
|
|
);
|
|
}
|
|
|
|
// Build response with image URLs
|
|
const baseUrl = process.env.NEXT_PUBLIC_APP_URL || "";
|
|
const response = {
|
|
...zine,
|
|
pageUrls: Array.from({ length: 8 }, (_, i) => `${baseUrl}/api/zine/${id}?image=p${i + 1}`),
|
|
printLayoutUrl: zine.printLayout ? `${baseUrl}/api/zine/${id}?print=true` : null,
|
|
shareUrl: `${baseUrl}/zine/z/${id}`,
|
|
};
|
|
|
|
return NextResponse.json(response);
|
|
} catch (error) {
|
|
console.error("Get zine error:", error);
|
|
return NextResponse.json(
|
|
{ error: error instanceof Error ? error.message : "Failed to get zine" },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|