"use server"; import { revalidatePath } from "next/cache"; import { getPaths } from "yt-dlp-transcript-common/lib/paths"; import { readJsonFile } from "yt-dlp-transcript-common/lib/jsonFile-server"; import { isReportId } from "yt-dlp-transcript-common/lib/report/schema"; import { isValidSiteId, patchSite } from "yt-dlp-transcript-common/lib/site"; import { siteReportFile } from "yt-dlp-transcript-common/publish/reportMedia"; import { publishRefusal, reportRow, withReportMoved, withReportPublished, withReportUnpublished, } from "./reportList"; // THE REPORTS TAB'S WRITES: site.json `reports`, the site's published list. // Each goes through patchSite, which reads the site inside the file lock and // applies the edit to the list as it is THEN — never a list the tab read // earlier — and writes the rest of the site back untouched. export type ReportsActionResult = { ok: true } | { ok: false; error: string }; type Edit = (list: readonly string[]) => string[]; async function editReports(siteId: string, reportId: string, edit: Edit): Promise { if (!isValidSiteId(siteId)) return { ok: false, error: `"${siteId}" is not a valid site id` }; if (!isReportId(reportId)) return { ok: false, error: `"${reportId}" is not a valid report id` }; let written; try { written = await patchSite(siteId, (current) => ({ reports: edit(current.reports ?? []) }), getPaths()); } catch (err) { return { ok: false, error: err instanceof Error ? err.message : String(err) }; } if (!written) return { ok: false, error: `No site "${siteId}"` }; // "layout": the Settings tab lists the reports too. revalidatePath("/sites/[siteId]", "layout"); return { ok: true }; } // Publish a draft: appended to the list. Refused unless its report.json parses // with no problems — read now, not when the tab rendered. export async function publishReportAction(siteId: string, reportId: string): Promise { if (!isValidSiteId(siteId)) return { ok: false, error: `"${siteId}" is not a valid site id` }; if (!isReportId(reportId)) return { ok: false, error: `"${reportId}" is not a valid report id` }; const read = await readJsonFile(siteReportFile(getPaths(), siteId, reportId)); const refusal = publishRefusal(reportRow(reportId, read, null)); if (refusal) return { ok: false, error: refusal }; return editReports(siteId, reportId, (list) => withReportPublished(list, reportId)); } // Unpublish: off the list, back to a draft. Its directory is not touched. export async function unpublishReportAction(siteId: string, reportId: string): Promise { return editReports(siteId, reportId, (list) => withReportUnpublished(list, reportId)); } // One place up (-1) or down (1) in the published order. export async function moveReportAction( siteId: string, reportId: string, dir: -1 | 1, ): Promise { if (dir !== -1 && dir !== 1) return { ok: false, error: "A report moves one place up or down." }; return editReports(siteId, reportId, (list) => withReportMoved(list, reportId, dir)); }