import { NextResponse } from "next/server"; import { applyChannelFormPatch, validateChannelFormPatch, } from "../../../channels/components/channelConfigToForm"; import { createChannelFromForm } from "../../../channels/actions"; import { OpsInputError, opsFail, ops, optBool, readSiteMemberships, } from "../_lib"; export const dynamic = "force-dynamic"; // POST { // slug?: string, // else derived from fields.name, as the form does // fields: { name, handling, url?, platform?, sourceKind?, postFetcher?, // socialHandle?, … }, // the New channel form's field names // sites?: [{ siteId, groupId? | newGroupName? }], // absent = on no site // fetchPlaylist?: boolean, // the form's "Fetch playlist now" // fetchPostsNow?: boolean, // the form's "Fetch posts now" // prioritizeDownload?: boolean, // the form's "Add to top of auto-queue" // } // // The New channel form, over HTTP. `fields` takes exactly the keys // /api/ops/channel-config's `patch` takes (channelConfigToForm.ts), built into // the FormData the form would post, and the same parser refuses what the form // refuses: no name, a handling that is not "youtube" or "transcribe", a social // URL with no derivable handle, a bad filter regex. // // The three start-something options are OFF unless asked for. The form ticks // "Fetch playlist now" by default because a person creating a channel usually // wants it; a script creating one says so. Any job they start comes back in // `jobIds`, so `--wait` can follow it. export async function POST(request: Request) { return ops( request, ["slug", "fields", "sites", "fetchPlaylist", "fetchPostsNow", "prioritizeDownload"], async (body) => { const fields = body.fields; if (typeof fields !== "object" || fields === null || Array.isArray(fields)) { throw new OpsInputError('"fields" must be a JSON object'); } try { validateChannelFormPatch(fields as Record); } catch (e) { throw new OpsInputError((e as Error).message); } const fd = new FormData(); applyChannelFormPatch(fd, fields as Record); if (body.slug !== undefined) { if (typeof body.slug !== "string") { throw new OpsInputError('"slug" must be a string'); } fd.set("slug", body.slug.trim()); } const sites = readSiteMemberships(body); if (sites) fd.set("siteMembershipsJson", JSON.stringify(sites)); for (const flag of ["fetchPlaylist", "fetchPostsNow", "prioritizeDownload"]) { if (optBool(body, flag)) fd.set(flag, "on"); } const result = await createChannelFromForm(fd); if (!result.ok) return opsFail(result.error); return NextResponse.json({ ok: true, slug: result.slug, jobIds: result.jobIds, ...(result.jobIds.length === 1 ? { jobId: result.jobIds[0] } : {}), }); }, ); }