app/lib/editor/preview-link-action.server.ts
79 lines and 2 functions in DrDustinEdwards/dustinedwards-info, at commit 1590ba4 on 2026-10-24. This file on GitHub.
Functions
| Function | Lines | Complexity | Covered | CRAP | Mutation |
|---|---|---|---|---|---|
| previewLinkAction | 14 to 83 | 7 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
| problem | 32 to 38 | 1 | No data: no coverage report in this map | No data: no coverage report in this map | No data: no mutants in this code |
Imports
It imports app/lib/content/slug.mjs, app/lib/editor/frontmatter.ts, app/lib/editor/github.server.ts, app/lib/error-message.mjs, app/lib/preview-links.server.ts, app/lib/preview-token.mjs. Imported by app/routes/admin.posts.$slug.edit.tsx.
Source
import { postPath } from "~/lib/content/slug.mjs";
import { parsePost } from "~/lib/editor/frontmatter";
import { readFile } from "~/lib/editor/github.server";
import { errorMessage } from "~/lib/error-message.mjs";
import { createPreviewLink, revokePreviewLink } from "~/lib/preview-links.server";
import { previewUrl } from "~/lib/preview-token.mjs";
/**
* The editor's preview-link sub-router: mint a link for a draft, or revoke one.
*
* `createdBy` is a thunk because the admin session is read only in the route's action (check:policy
* holds it there), and only when a link is minted: a revoke never reads it.
*/
export async function previewLinkAction(
env: Env,
{
intent,
slug,
origin,
form,
createdBy,
headAfterProblem,
}: {
intent: "preview-link" | "revoke-preview-link";
slug: string;
origin: string;
form: FormData;
createdBy: () => string;
headAfterProblem: () => Promise<string>;
},
) {
const problem = async (message: string) => ({
kind: "problem" as const,
/* These forms carry no body, so the editor keeps the loaded fields rather than an empty post. */
fields: undefined,
problem: { message, conflict: false, field: undefined, line: undefined },
headSha: await headAfterProblem(),
});
if (intent === "preview-link") {
/*
* Server side, not just the UI: minting a capability must not trust the absence of a button.
* The committed file is the authority, because the form is the author's unsaved draft.
*/
const committed = await readFile(env, postPath(slug));
if (!committed) return problem(`No post file exists for "${slug}".`);
if (parsePost(committed.content).draft !== true) {
return problem(
"Preview links are only for drafts. This post is already public, so " +
"its URL is the link.",
);
}
try {
const link = await createPreviewLink(env, {
slug,
createdBy: createdBy(),
});
return {
kind: "preview-link" as const,
url: previewUrl(origin, link.token),
expiresAt: link.expiresAt,
};
} catch (error) {
return problem(
`The preview link could not be created: ${errorMessage(error)}`,
);
}
}
/* Revoke is not draft-gated: removing access must never be blocked by a stale page. */
try {
await revokePreviewLink(env, {
slug,
token: String(form.get("token") ?? ""),
});
return { kind: "preview-link-revoked" as const };
} catch (error) {
return problem(
`The preview link could not be revoked: ${errorMessage(error)}`,
);
}
}