109 lines
4.8 KiB
TypeScript
109 lines
4.8 KiB
TypeScript
"use server";
|
|
import { revalidatePath } from "next/cache";
|
|
import { rawDb, uniqueSlug } from "@/lib/db/client";
|
|
|
|
const EXCLUSIVE_GROUPS: string[][] = [["favorite", "vip"]];
|
|
|
|
function getExclusivePeers(categoryId: number): number[] {
|
|
const cat = rawDb.prepare(`SELECT slug FROM actress_categories WHERE id = ?`).get(categoryId) as { slug: string } | undefined;
|
|
if (!cat) return [];
|
|
const group = EXCLUSIVE_GROUPS.find((g) => g.includes(cat.slug));
|
|
if (!group) return [];
|
|
const peers = group.filter((s) => s !== cat.slug);
|
|
if (peers.length === 0) return [];
|
|
const placeholders = peers.map(() => "?").join(",");
|
|
const rows = rawDb.prepare(`SELECT id FROM actress_categories WHERE slug IN (${placeholders})`).all(...peers) as Array<{ id: number }>;
|
|
return rows.map((r) => r.id);
|
|
}
|
|
|
|
export async function toggleActressCategory(actressId: number, categoryId: number) {
|
|
const exists = rawDb.prepare(`
|
|
SELECT 1 FROM actress_categories_map WHERE actress_id = ? AND category_id = ?
|
|
`).get(actressId, categoryId);
|
|
if (exists) {
|
|
rawDb.prepare(`DELETE FROM actress_categories_map WHERE actress_id = ? AND category_id = ?`).run(actressId, categoryId);
|
|
} else {
|
|
// Adding: also remove any peer category in an exclusive group.
|
|
const peers = getExclusivePeers(categoryId);
|
|
const tx = rawDb.transaction(() => {
|
|
if (peers.length > 0) {
|
|
const placeholders = peers.map(() => "?").join(",");
|
|
rawDb.prepare(`
|
|
DELETE FROM actress_categories_map
|
|
WHERE actress_id = ? AND category_id IN (${placeholders})
|
|
`).run(actressId, ...peers);
|
|
}
|
|
rawDb.prepare(`INSERT INTO actress_categories_map (actress_id, category_id) VALUES (?, ?)`).run(actressId, categoryId);
|
|
});
|
|
tx();
|
|
}
|
|
const a = rawDb.prepare(`SELECT slug FROM actresses WHERE id = ?`).get(actressId) as { slug: string } | undefined;
|
|
revalidatePath("/actress");
|
|
if (a) revalidatePath(`/actress/${a.slug}`);
|
|
return { added: !exists };
|
|
}
|
|
|
|
export async function setActressCategories(actressId: number, categoryIds: number[]) {
|
|
const tx = rawDb.transaction(() => {
|
|
rawDb.prepare(`DELETE FROM actress_categories_map WHERE actress_id = ?`).run(actressId);
|
|
const ins = rawDb.prepare(`INSERT INTO actress_categories_map (actress_id, category_id) VALUES (?, ?)`);
|
|
for (const id of categoryIds) ins.run(actressId, id);
|
|
});
|
|
tx();
|
|
const a = rawDb.prepare(`SELECT slug FROM actresses WHERE id = ?`).get(actressId) as { slug: string } | undefined;
|
|
revalidatePath("/actress");
|
|
if (a) revalidatePath(`/actress/${a.slug}`);
|
|
}
|
|
|
|
export async function createActressCategory(input: { name: string; color?: string | null; icon?: string | null; priority?: number }) {
|
|
const trimmed = input.name.trim();
|
|
if (!trimmed) return null;
|
|
const existing = rawDb.prepare(`SELECT id, slug FROM actress_categories WHERE name = ?`).get(trimmed) as { id: number; slug: string } | undefined;
|
|
if (existing) return existing;
|
|
const slug = uniqueSlug(rawDb, "actress_categories", trimmed);
|
|
const row = rawDb.prepare(`
|
|
INSERT INTO actress_categories (name, slug, color, icon, priority, builtin)
|
|
VALUES (?, ?, ?, ?, ?, 0) RETURNING id
|
|
`).get(trimmed, slug, input.color ?? null, input.icon ?? null, input.priority ?? 50) as { id: number };
|
|
revalidatePath("/actress");
|
|
return { id: row.id, slug };
|
|
}
|
|
|
|
export async function bulkAddCategory(actressIds: number[], categoryId: number) {
|
|
if (actressIds.length === 0) return;
|
|
const peers = getExclusivePeers(categoryId);
|
|
const ins = rawDb.prepare(`INSERT OR IGNORE INTO actress_categories_map (actress_id, category_id) VALUES (?, ?)`);
|
|
const tx = rawDb.transaction(() => {
|
|
if (peers.length > 0) {
|
|
const peerPh = peers.map(() => "?").join(",");
|
|
const idPh = actressIds.map(() => "?").join(",");
|
|
rawDb.prepare(`
|
|
DELETE FROM actress_categories_map
|
|
WHERE actress_id IN (${idPh}) AND category_id IN (${peerPh})
|
|
`).run(...actressIds, ...peers);
|
|
}
|
|
for (const id of actressIds) ins.run(id, categoryId);
|
|
});
|
|
tx();
|
|
revalidatePath("/actress");
|
|
}
|
|
|
|
export async function bulkRemoveCategory(actressIds: number[], categoryId: number) {
|
|
if (actressIds.length === 0) return;
|
|
const placeholders = actressIds.map(() => "?").join(",");
|
|
rawDb.prepare(`
|
|
DELETE FROM actress_categories_map
|
|
WHERE category_id = ? AND actress_id IN (${placeholders})
|
|
`).run(categoryId, ...actressIds);
|
|
revalidatePath("/actress");
|
|
}
|
|
|
|
export async function deleteActressCategory(categoryId: number) {
|
|
const row = rawDb.prepare(`SELECT builtin FROM actress_categories WHERE id = ?`).get(categoryId) as { builtin: number } | undefined;
|
|
if (!row) return { ok: false, reason: "not found" };
|
|
if (row.builtin) return { ok: false, reason: "built-in category cannot be deleted" };
|
|
rawDb.prepare(`DELETE FROM actress_categories WHERE id = ?`).run(categoryId);
|
|
revalidatePath("/actress");
|
|
return { ok: true };
|
|
}
|