Files
EpicNext-Cms/src/lib/article-request.ts
T
Simo 52f6d1491f
CI / check (push) Successful in 3m12s
CI / deploy (push) Successful in 1m13s
CI / publish-container (push) Successful in 42s
fix(news): persist publication requests and retry cache delivery
2026-09-13 18:33:45 +02:00

46 lines
1.2 KiB
TypeScript

export interface ArticleRequest {
signature: string;
key: string;
}
/** Retain only a fingerprint and request ID, never editorial content. */
export async function articleRequest(
form: FormData,
scope: string,
previous?: ArticleRequest,
storage?: Pick<Storage, "getItem" | "setItem">,
): Promise<ArticleRequest> {
const entries = [...form.entries()]
.filter(([key]) => key !== "requestKey")
.sort(([a], [b]) => a.localeCompare(b));
const digest = await crypto.subtle.digest(
"SHA-256",
new TextEncoder().encode(JSON.stringify([scope, entries])),
);
const signature = Array.from(new Uint8Array(digest), (byte) =>
byte.toString(16).padStart(2, "0"),
).join("");
const storageKey = `cms:news:request:${scope}`;
let saved = previous;
if (!saved) {
try {
saved = JSON.parse(storage?.getItem(storageKey) ?? "null") ?? undefined;
} catch {
/* Storage may be disabled. */
}
}
if (
saved?.signature === signature &&
/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(
saved.key,
)
)
return saved;
const next = { signature, key: crypto.randomUUID() };
try {
storage?.setItem(storageKey, JSON.stringify(next));
} catch {
/* The editor retains the key in memory. */
}
return next;
}