35 lines
1.6 KiB
TypeScript
35 lines
1.6 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server";
|
|
import { eq } from "drizzle-orm";
|
|
import { db } from "@/lib/db";
|
|
import { imageArchiveTask } from "@/lib/db/schema";
|
|
|
|
// The SHA-256 URL is a capability, like the provider's signed image URL. It must be
|
|
// readable by image nodes and reference-image providers without a session cookie.
|
|
export async function GET(request: NextRequest, { params }: { params: Promise<{ key: string }> }) {
|
|
const { key } = await params;
|
|
if (!/^[a-f0-9]{64}$/.test(key)) return new NextResponse(null, { status: 404 });
|
|
const configured =
|
|
process.env.NEXT_PUBLIC_APP_URL || process.env.AUTH_URL || process.env.NEXTAUTH_URL;
|
|
const origin = configured
|
|
? new URL(configured).origin
|
|
: `http://localhost:${process.env.PORT || "3000"}`;
|
|
const sourceUrl = `${origin}/api/generated-image-preview/${key}`;
|
|
const [task] = await db
|
|
.select({ targetUrl: imageArchiveTask.targetUrl, metadata: imageArchiveTask.metadata })
|
|
.from(imageArchiveTask)
|
|
.where(eq(imageArchiveTask.sourceUrl, sourceUrl))
|
|
.limit(1);
|
|
if (task?.targetUrl) return NextResponse.redirect(task.targetUrl, 307);
|
|
const inline = (task?.metadata as { inlineSource?: string } | null)?.inlineSource;
|
|
const match = inline?.match(/^data:(image\/[a-z0-9.+-]+);base64,([a-z0-9+/=]+)$/i);
|
|
if (!match) return new NextResponse(null, { status: 404 });
|
|
return new NextResponse(Buffer.from(match[2], "base64"), {
|
|
headers: {
|
|
"Content-Type": match[1],
|
|
"Cache-Control": "private, no-store",
|
|
"X-Content-Type-Options": "nosniff",
|
|
"Content-Security-Policy": "default-src 'none'; sandbox",
|
|
},
|
|
});
|
|
}
|