79 lines
2.2 KiB
JavaScript
79 lines
2.2 KiB
JavaScript
import { httpError } from '../../shared/http/httpError.mjs';
|
|
|
|
export function normalizeArticle(input) {
|
|
if (!input || typeof input !== 'object' || Array.isArray(input)) {
|
|
throw httpError(400, 'Article payload must be an object');
|
|
}
|
|
|
|
const article = {
|
|
...input,
|
|
slug: readRequiredString(input, 'slug'),
|
|
title: readRequiredString(input, 'title'),
|
|
date: readRequiredString(input, 'date'),
|
|
author: readRequiredString(input, 'author'),
|
|
categories: normalizeCategories(input.categories),
|
|
cover: readRequiredString(input, 'cover'),
|
|
excerpt: readRequiredString(input, 'excerpt'),
|
|
contentHtml: readRequiredString(input, 'contentHtml', { preserveWhitespace: true }),
|
|
readingMinutes: normalizeReadingMinutes(input.readingMinutes),
|
|
};
|
|
|
|
if (/[/?#]/.test(article.slug)) {
|
|
throw httpError(400, 'Slug must not contain "/", "?" or "#"');
|
|
}
|
|
|
|
if (Number.isNaN(new Date(article.date).getTime())) {
|
|
throw httpError(400, 'Date must be a valid date string');
|
|
}
|
|
|
|
return article;
|
|
}
|
|
|
|
export function assertUniqueSlug(articles, slug, exceptSlug) {
|
|
const duplicate = articles.find((article) => article.slug === slug && article.slug !== exceptSlug);
|
|
|
|
if (duplicate) {
|
|
throw httpError(409, `Slug "${slug}" is already used`);
|
|
}
|
|
}
|
|
|
|
function readRequiredString(input, key, options = {}) {
|
|
const value = input[key];
|
|
|
|
if (typeof value !== 'string') {
|
|
throw httpError(400, `"${key}" must be a string`);
|
|
}
|
|
|
|
const normalized = options.preserveWhitespace ? value : value.trim();
|
|
if (!normalized) {
|
|
throw httpError(400, `"${key}" is required`);
|
|
}
|
|
|
|
return normalized;
|
|
}
|
|
|
|
function normalizeCategories(value) {
|
|
const categories = Array.isArray(value)
|
|
? value
|
|
: String(value || '')
|
|
.split(',')
|
|
.map((category) => category.trim());
|
|
|
|
const normalized = categories.filter(Boolean);
|
|
if (normalized.length === 0) {
|
|
throw httpError(400, 'At least one category is required');
|
|
}
|
|
|
|
return [...new Set(normalized)];
|
|
}
|
|
|
|
function normalizeReadingMinutes(value) {
|
|
const minutes = Number(value);
|
|
|
|
if (!Number.isInteger(minutes) || minutes < 1) {
|
|
throw httpError(400, '"readingMinutes" must be a positive integer');
|
|
}
|
|
|
|
return minutes;
|
|
}
|