diff --git a/server/api/me/media/orphans-delete.post.ts b/server/api/me/media/orphans-delete.post.ts new file mode 100644 index 0000000..3cb426e --- /dev/null +++ b/server/api/me/media/orphans-delete.post.ts @@ -0,0 +1,35 @@ +import { deleteMediaAssetsForUser } from "#server/service/media"; + +const MAX_BATCH = 50; + +export default defineWrappedResponseHandler(async (event) => { + const user = await event.context.auth.requireUser(); + const body = await readBody<{ ids?: unknown }>(event); + + if (body === null || typeof body !== "object" || !("ids" in body)) { + throw createError({ statusCode: 400, statusMessage: "请求体须包含 ids 数组" }); + } + + const { ids } = body; + if (!Array.isArray(ids)) { + throw createError({ statusCode: 400, statusMessage: "ids 必须为数组" }); + } + if (ids.length > MAX_BATCH) { + throw createError({ statusCode: 400, statusMessage: `单次最多删除 ${MAX_BATCH} 条` }); + } + + const numericIds: number[] = []; + for (const x of ids) { + if (typeof x !== "number" || !Number.isInteger(x) || x < 1) { + throw createError({ statusCode: 400, statusMessage: "ids 须均为正整数" }); + } + numericIds.push(x); + } + + if (numericIds.length === 0) { + return R.success({ deleted: 0 }); + } + + const deleted = await deleteMediaAssetsForUser(user.id, numericIds); + return R.success({ deleted }); +}); diff --git a/server/api/me/media/orphans.get.ts b/server/api/me/media/orphans.get.ts new file mode 100644 index 0000000..5d172a3 --- /dev/null +++ b/server/api/me/media/orphans.get.ts @@ -0,0 +1,38 @@ +import { listOrphanCandidatesForUser } from "#server/service/media"; + +type OrphanFilter = "all" | "deletable" | "cooling"; + +function parseFilter(raw: string | undefined): OrphanFilter { + const v = raw === undefined || raw === "" ? "all" : raw; + if (v === "all" || v === "deletable" || v === "cooling") { + return v; + } + throw createError({ statusCode: 400, statusMessage: "filter 须为 all、deletable 或 cooling" }); +} + +function parsePositiveInt(raw: string | undefined, fallback: number, label: string): number { + if (raw === undefined || raw === "") { + return fallback; + } + const n = Number(raw); + if (!Number.isInteger(n) || n < 1) { + throw createError({ statusCode: 400, statusMessage: `${label} 须为正整数` }); + } + return n; +} + +export default defineWrappedResponseHandler(async (event) => { + const user = await event.context.auth.requireUser(); + const q = getQuery(event); + const filter = parseFilter(typeof q.filter === "string" ? q.filter : undefined); + const page = parsePositiveInt(typeof q.page === "string" ? q.page : undefined, 1, "page"); + const pageSizeRaw = parsePositiveInt( + typeof q.pageSize === "string" ? q.pageSize : undefined, + 20, + "pageSize", + ); + const pageSize = Math.min(pageSizeRaw, 100); + + const { items, total } = await listOrphanCandidatesForUser(user.id, filter, page, pageSize); + return R.success({ items, total }); +});