111 lines
2.9 KiB
TypeScript
111 lines
2.9 KiB
TypeScript
"use server";
|
|
import { and, desc, eq } from "drizzle-orm";
|
|
import { ArticleDrafts, ArticleRevisions } from "@/db/article-editor";
|
|
import { requirePermission } from "@/lib/admin/guard";
|
|
import {
|
|
type ArticleDraft,
|
|
articleKey,
|
|
readArticleDraft,
|
|
} from "@/lib/article-draft";
|
|
import { db, WebsiteArticles } from "@/lib/db";
|
|
import { PERMS } from "@/lib/permissions";
|
|
|
|
export async function loadArticleRecovery(key: string) {
|
|
const staff = await requirePermission(PERMS.NEWS_EDIT);
|
|
key = articleKey(key);
|
|
const [draft] = await db
|
|
.select()
|
|
.from(ArticleDrafts)
|
|
.where(
|
|
and(
|
|
eq(ArticleDrafts.userId, staff.id),
|
|
eq(ArticleDrafts.articleKey, key),
|
|
),
|
|
)
|
|
.limit(1);
|
|
const revisions =
|
|
key === "new"
|
|
? []
|
|
: await db
|
|
.select()
|
|
.from(ArticleRevisions)
|
|
.where(eq(ArticleRevisions.articleId, BigInt(key)))
|
|
.orderBy(desc(ArticleRevisions.id))
|
|
.limit(20);
|
|
return {
|
|
version: draft?.version ?? 0,
|
|
draft:
|
|
draft && draft.payload !== "{}"
|
|
? {
|
|
version: draft.version,
|
|
payload: JSON.parse(draft.payload) as ArticleDraft,
|
|
}
|
|
: null,
|
|
revisions: revisions.map((r) => ({
|
|
id: r.id,
|
|
createdAt: r.createdAt.toISOString(),
|
|
payload: JSON.parse(r.payload) as ArticleDraft,
|
|
})),
|
|
};
|
|
}
|
|
export async function autosaveArticle(
|
|
key: string,
|
|
expectedVersion: number,
|
|
form: FormData,
|
|
) {
|
|
const staff = await requirePermission(PERMS.NEWS_EDIT);
|
|
key = articleKey(key);
|
|
const payload = JSON.stringify(readArticleDraft(form));
|
|
if (!Number.isSafeInteger(expectedVersion) || expectedVersion < 0)
|
|
throw new Error("draftInvalid");
|
|
return db.transaction(async (tx) => {
|
|
if (key !== "new") {
|
|
const [article] = await tx
|
|
.select({ id: WebsiteArticles.id })
|
|
.from(WebsiteArticles)
|
|
.where(eq(WebsiteArticles.id, BigInt(key)))
|
|
.limit(1)
|
|
.for("update");
|
|
if (!article) return { ok: false as const };
|
|
}
|
|
await tx
|
|
.insert(ArticleDrafts)
|
|
.values({ userId: staff.id, articleKey: key, version: 0, payload: "{}" })
|
|
.onDuplicateKeyUpdate({ set: { articleKey: key } });
|
|
const where = and(
|
|
eq(ArticleDrafts.userId, staff.id),
|
|
eq(ArticleDrafts.articleKey, key),
|
|
);
|
|
const [draft] = await tx
|
|
.select()
|
|
.from(ArticleDrafts)
|
|
.where(where)
|
|
.for("update");
|
|
if (!draft || draft.version !== expectedVersion)
|
|
return { ok: false as const };
|
|
await tx
|
|
.update(ArticleDrafts)
|
|
.set({ payload, version: expectedVersion + 1, updatedAt: new Date() })
|
|
.where(where);
|
|
return { ok: true as const, version: expectedVersion + 1 };
|
|
});
|
|
}
|
|
|
|
export async function clearArticleRecovery(
|
|
key: string,
|
|
expectedVersion: number,
|
|
) {
|
|
const staff = await requirePermission(PERMS.NEWS_EDIT);
|
|
key = articleKey(key);
|
|
await db
|
|
.update(ArticleDrafts)
|
|
.set({ payload: "{}", version: expectedVersion + 1, updatedAt: new Date() })
|
|
.where(
|
|
and(
|
|
eq(ArticleDrafts.userId, staff.id),
|
|
eq(ArticleDrafts.articleKey, key),
|
|
eq(ArticleDrafts.version, expectedVersion),
|
|
),
|
|
);
|
|
}
|