"use server"; import { revalidatePath } from "next/cache"; import path from "node:path"; import fs from "node:fs/promises"; import { rawDb } from "@/lib/db/client"; import type { PortraitSlotKey } from "@/lib/db/queries"; import { safeJoin } from "@/lib/safePath"; const PORTRAIT_ROOT = path.join(process.cwd(), "data", "portraits"); const SLOT_COLS: Record = { "1": { path: "portrait_path", zoom: "portrait_zoom", ox: "portrait_offset_x", oy: "portrait_offset_y" }, "2": { path: "portrait2_path", zoom: "portrait2_zoom", ox: "portrait2_offset_x", oy: "portrait2_offset_y" }, "3": { path: "portrait3_path", zoom: "portrait3_zoom", ox: "portrait3_offset_x", oy: "portrait3_offset_y" }, "4": { path: "portrait4_path", zoom: "portrait4_zoom", ox: "portrait4_offset_x", oy: "portrait4_offset_y" }, "h": { path: "portraith_path", zoom: "portraith_zoom", ox: "portraith_offset_x", oy: "portraith_offset_y" }, }; export async function setActressPortraitTransform( actressId: number, slot: PortraitSlotKey, transform: { zoom: number; offsetX: number; offsetY: number }, ) { const c = SLOT_COLS[slot]; if (!c) return; const zoom = Math.max(0.5, Math.min(5, transform.zoom)); rawDb.prepare(` UPDATE actresses SET ${c.zoom} = ?, ${c.ox} = ?, ${c.oy} = ? WHERE id = ? `).run(zoom, transform.offsetX, transform.offsetY, actressId); const a = rawDb.prepare(`SELECT slug FROM actresses WHERE id = ?`).get(actressId) as { slug: string } | undefined; revalidatePath("/actress"); if (a) revalidatePath(`/actress/${a.slug}`); } type PortraitTuple = [string | null, number, number, number]; export async function reorderActressPortraitSlots( actressId: number, src: PortraitSlotKey, dest: PortraitSlotKey, ) { if (src === dest) return; if (src === "h" || dest === "h") return; const order: PortraitSlotKey[] = ["1", "2", "3", "4"]; if (!order.includes(src) || !order.includes(dest)) return; const cols = order.map((s) => SLOT_COLS[s]); const selectFrag = cols .map((c, i) => `${c.path} AS p${i}, ${c.zoom} AS z${i}, ${c.ox} AS x${i}, ${c.oy} AS y${i}`) .join(", "); const row = rawDb.prepare(`SELECT slug, ${selectFrag} FROM actresses WHERE id = ?`).get(actressId) as | (Record & { slug: string }) | undefined; if (!row) return; const current: PortraitTuple[] = order.map((_, i) => [ (row[`p${i}`] as string | null) ?? null, Number(row[`z${i}`] ?? 1), Number(row[`x${i}`] ?? 0), Number(row[`y${i}`] ?? 0), ]); const srcIdx = order.indexOf(src); const destIdx = order.indexOf(dest); const next = [...current]; const [moved] = next.splice(srcIdx, 1); next.splice(destIdx, 0, moved); const setFrag = cols.map((c) => `${c.path} = ?, ${c.zoom} = ?, ${c.ox} = ?, ${c.oy} = ?`).join(", "); const params = next.flatMap((t) => [t[0], t[1], t[2], t[3]]); rawDb.prepare(`UPDATE actresses SET ${setFrag} WHERE id = ?`).run(...params, actressId); revalidatePath("/actress"); revalidatePath(`/actress/${row.slug}`); } export async function clearActressPortrait(actressId: number, slot: PortraitSlotKey) { const c = SLOT_COLS[slot]; if (!c) return; const row = rawDb.prepare(`SELECT slug, ${c.path} AS p FROM actresses WHERE id = ?`).get(actressId) as | { slug: string; p: string | null } | undefined; if (!row) return; if (row.p) { const abs = safeJoin(PORTRAIT_ROOT, row.p); if (abs) await fs.rm(abs, { force: true }).catch(() => {}); } rawDb.prepare(` UPDATE actresses SET ${c.path} = NULL, ${c.zoom} = 1, ${c.ox} = 0, ${c.oy} = 0 WHERE id = ? `).run(actressId); revalidatePath("/actress"); revalidatePath(`/actress/${row.slug}`); }