feat(editor): secure page autosave action
This commit is contained in:
@@ -0,0 +1,71 @@
|
|||||||
|
"use server";
|
||||||
|
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import type { AutosaveResponse } from "@/lib/editor/autosave";
|
||||||
|
import { savePageDraft } from "@/lib/content/mutations";
|
||||||
|
import type { PageSnapshot } from "@/lib/content/types";
|
||||||
|
import { requireAdmin } from "@/lib/auth/server";
|
||||||
|
|
||||||
|
const serializedBlockSchema = z.strictObject({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
type: z.string().min(1).max(64),
|
||||||
|
schemaVersion: z.number().int().nonnegative(),
|
||||||
|
config: z.unknown(),
|
||||||
|
responsiveLayout: z.unknown().optional(),
|
||||||
|
sortOrder: z.number().int().nonnegative(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const editorDraftSchema = z.strictObject({
|
||||||
|
pageId: z.string().uuid(),
|
||||||
|
expectedVersion: z.number().int().positive(),
|
||||||
|
title: z.string().max(300),
|
||||||
|
slug: z.string().max(120),
|
||||||
|
visible: z.boolean(),
|
||||||
|
publicNote: z.string().max(10_000).nullable().optional(),
|
||||||
|
blocks: z.array(serializedBlockSchema).max(500),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type EditorSaveInput = z.input<typeof editorDraftSchema>;
|
||||||
|
export type EditorSaveResult = AutosaveResponse<PageSnapshot>;
|
||||||
|
|
||||||
|
export async function saveEditorPage(input: unknown): Promise<EditorSaveResult> {
|
||||||
|
const admin = await requireAdmin();
|
||||||
|
const parsed = editorDraftSchema.safeParse(input);
|
||||||
|
if (!parsed.success) {
|
||||||
|
return {
|
||||||
|
status: "validation-error",
|
||||||
|
issues: parsed.error.issues.map((issue) => issue.message),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.env.BUZZ_DEMO_MODE === "true") {
|
||||||
|
return { status: "saved", version: parsed.data.expectedVersion + 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await savePageDraft({
|
||||||
|
...parsed.data,
|
||||||
|
authorId: admin.user.id,
|
||||||
|
});
|
||||||
|
if (result.status === "accepted") {
|
||||||
|
return {
|
||||||
|
status: "saved",
|
||||||
|
version: result.value.page.version,
|
||||||
|
snapshot: result.value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (result.status === "not-found") {
|
||||||
|
return {
|
||||||
|
status: "validation-error",
|
||||||
|
issues: ["ไม่พบหน้าที่ต้องการบันทึก"],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} catch (cause) {
|
||||||
|
return {
|
||||||
|
status: "transient-error",
|
||||||
|
message: cause instanceof Error ? cause.message : "บันทึกไม่สำเร็จ",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user