48 lines
1.5 KiB
TypeScript
48 lines
1.5 KiB
TypeScript
import type { ActionResult } from "./safe-action-shared";
|
|
export type ArticleSaveResult = ActionResult<{ redirectTo: string }>;
|
|
export class ArticleInputError extends Error {
|
|
constructor(
|
|
public readonly code:
|
|
| "titleRequired"
|
|
| "titleTooLong"
|
|
| "summaryTooLong"
|
|
| "imageTooLong"
|
|
| "invalidStatus"
|
|
| "publishDateRequired",
|
|
) {
|
|
super(code);
|
|
}
|
|
}
|
|
export function readArticleInput(form: FormData) {
|
|
const text = (key: string) =>
|
|
String(form.get(key) ?? "")
|
|
.normalize("NFC")
|
|
.trim();
|
|
const title = text("title"),
|
|
shortStory = text("shortStory"),
|
|
fullStory = text("fullStory"),
|
|
image = text("image");
|
|
if (!title) throw new ArticleInputError("titleRequired");
|
|
if (title.length > 255) throw new ArticleInputError("titleTooLong");
|
|
if (shortStory.length > 255) throw new ArticleInputError("summaryTooLong");
|
|
if (image.length > 255) throw new ArticleInputError("imageTooLong");
|
|
const status = text("status") || "published";
|
|
if (status !== "published" && status !== "draft" && status !== "scheduled")
|
|
throw new ArticleInputError("invalidStatus");
|
|
const publishAt = status === "scheduled" ? new Date(text("publishAt")) : null;
|
|
if (publishAt && Number.isNaN(publishAt.getTime()))
|
|
throw new ArticleInputError("publishDateRequired");
|
|
return {
|
|
title,
|
|
shortStory,
|
|
fullStory,
|
|
image,
|
|
status,
|
|
publishAt,
|
|
rawSlug: text("slug"),
|
|
};
|
|
}
|
|
export function articleSlug(value: string | string[]): string {
|
|
return Array.isArray(value) ? value.join("/") : value;
|
|
}
|