66 lines
3.3 KiB
TypeScript
66 lines
3.3 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server";
|
|
import { and, eq, inArray, sql } from "drizzle-orm";
|
|
import { auth } from "@/app/(auth)/auth";
|
|
import { db } from "@/lib/db";
|
|
import { canvasProject, imageArchiveTask, generatedImage, canvasImageTask } from "@/lib/db/schema";
|
|
import { imageArchiveSourceKey } from "@/lib/storage/image-archive-db";
|
|
import { isGeneratedImageSource } from "@/lib/storage/image-archive-urls";
|
|
import { scheduleImageArchiveWorker } from "@/lib/storage/image-archive-worker";
|
|
|
|
export async function POST(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
|
const session = await auth();
|
|
if (!session?.user?.id) return NextResponse.json({ error: "未登录" }, { status: 401 });
|
|
const { id } = await params;
|
|
const [project] = await db
|
|
.select({ id: canvasProject.id })
|
|
.from(canvasProject)
|
|
.where(and(eq(canvasProject.id, id), eq(canvasProject.userId, session.user.id)))
|
|
.limit(1);
|
|
if (!project) return NextResponse.json({ error: "画布不存在" }, { status: 404 });
|
|
const body = await request.json();
|
|
if (
|
|
!Array.isArray(body.urls) ||
|
|
body.urls.length > 100 ||
|
|
body.urls.some((url: unknown) => typeof url !== "string" || !isGeneratedImageSource(url))
|
|
) {
|
|
return NextResponse.json({ error: "图片地址格式错误" }, { status: 400 });
|
|
}
|
|
const urls: string[] = [...new Set<string>(body.urls)];
|
|
if (!urls.length) return NextResponse.json({ replacements: {}, pending: [] });
|
|
const source = imageArchiveTask.sourceUrl;
|
|
const rows = await db
|
|
.select({
|
|
sourceUrl: imageArchiveTask.sourceUrl,
|
|
targetUrl: imageArchiveTask.targetUrl,
|
|
status: imageArchiveTask.status,
|
|
})
|
|
.from(imageArchiveTask)
|
|
.where(
|
|
and(
|
|
inArray(imageArchiveTask.sourceUrl, urls.map(imageArchiveSourceKey)),
|
|
sql`(
|
|
exists (select 1 from ${generatedImage} where ${generatedImage.userId} = ${session.user.id}
|
|
and (${generatedImage.originalUrl} = ${source} or ${generatedImage.thumbnailUrl} = ${source}
|
|
or ${generatedImage.originalUrl} = ${imageArchiveTask.targetUrl} or ${generatedImage.thumbnailUrl} = ${imageArchiveTask.targetUrl}))
|
|
or exists (select 1 from ${canvasImageTask} where ${canvasImageTask.userId} = ${session.user.id}
|
|
and (position(${source} in ${canvasImageTask.result}::text) > 0 or position(${imageArchiveTask.targetUrl} in ${canvasImageTask.result}::text) > 0))
|
|
or exists (select 1 from ${canvasProject} where ${canvasProject.id} = ${id} and ${canvasProject.userId} = ${session.user.id}
|
|
and (position(${source} in ${canvasProject.canvasData}::text) > 0 or position(${imageArchiveTask.targetUrl} in ${canvasProject.canvasData}::text) > 0))
|
|
)`,
|
|
),
|
|
);
|
|
const byKey = new Map(rows.map((row) => [row.sourceUrl, row]));
|
|
const replacements: Record<string, string> = {};
|
|
const pending: string[] = [];
|
|
for (const url of urls) {
|
|
const row = byKey.get(imageArchiveSourceKey(url));
|
|
if (row?.targetUrl) replacements[url] = row.targetUrl;
|
|
else if (row && row.status !== "abandoned") pending.push(url);
|
|
}
|
|
if (rows.some((row) => row.status === "pending")) scheduleImageArchiveWorker(true);
|
|
return NextResponse.json(
|
|
{ replacements, pending },
|
|
{ headers: { "Cache-Control": "private, no-store" } },
|
|
);
|
|
}
|