acdc25c797
Split out of the single Ampelos repository. Next.js app, Drizzle schema and migrations, brand art, planning notes. What left: scripts/, which was the agent's job library misfiled under web/ and imported nothing from src/; and deploy/truenas, whose broadcast posts to the scan listener on :3427 -- an agent script -- so it belongs beside the thing it talks to. This repository keeps the schema. The agent speaks raw SQL against the same tables and holds no copy of it, so a rename here can break it silently where it used to be one commit. The README says so, and the agent carries a snapshot to check against. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
55 lines
1.8 KiB
TypeScript
55 lines
1.8 KiB
TypeScript
import { auth } from "@/auth";
|
|
import { db } from "@/db/client";
|
|
import { classics, watchingNowItems } from "@/db/schema";
|
|
import { and, inArray, isNull } from "drizzle-orm";
|
|
import { revalidatePath } from "next/cache";
|
|
|
|
export const dynamic = "force-dynamic";
|
|
|
|
type RequestBody = {
|
|
action?: "add" | "remove";
|
|
mediaItemIds?: string[];
|
|
};
|
|
|
|
async function removeActiveWatchNow(mediaItemIds: string[]) {
|
|
if (!mediaItemIds.length) return;
|
|
|
|
await db
|
|
.update(watchingNowItems)
|
|
.set({ removedAt: new Date() })
|
|
.where(and(inArray(watchingNowItems.mediaItemId, mediaItemIds), isNull(watchingNowItems.removedAt)));
|
|
}
|
|
|
|
export async function POST(request: Request) {
|
|
const session = await auth();
|
|
if (!session) return Response.json({ error: "Unauthorized" }, { status: 401 });
|
|
if (!session.user.isAdmin) return Response.json({ error: "Forbidden" }, { status: 403 });
|
|
|
|
const body = (await request.json()) as RequestBody;
|
|
const mediaItemIds = Array.isArray(body.mediaItemIds)
|
|
? body.mediaItemIds.filter((id): id is string => typeof id === "string" && id.length > 0)
|
|
: [];
|
|
|
|
if (!body.action || !mediaItemIds.length) {
|
|
return Response.json({ error: "Missing action or mediaItemIds" }, { status: 400 });
|
|
}
|
|
|
|
if (body.action === "add") {
|
|
await Promise.all(
|
|
mediaItemIds.map((mediaItemId) =>
|
|
db
|
|
.insert(classics)
|
|
.values({ mediaItemId, note: "Bulk marked as Timeless Classic", addedBy: session.user.id })
|
|
.onConflictDoNothing({ target: classics.mediaItemId }),
|
|
),
|
|
);
|
|
await removeActiveWatchNow(mediaItemIds);
|
|
} else {
|
|
await db.delete(classics).where(inArray(classics.mediaItemId, mediaItemIds));
|
|
}
|
|
|
|
revalidatePath("/admin/inventory");
|
|
revalidatePath("/");
|
|
return Response.json({ ok: true });
|
|
}
|