Files

29 lines
2.0 KiB
TypeScript

import { cache } from 'react';
import sanitizeHtml from 'sanitize-html';
import { tenantApi, safeUrl } from './api';
export type Blog = { id: string | number; title: string; slug?: string; excerpt?: string; summary?: string; content?: string; text?: string; coverUrl?: string; imageUrl?: string; publishedAt?: string };
export type BlogList = { items: Blog[]; total: number; page: number; pageSize: number };
export const getBlogs = cache(async (page: number) => {
const data = await tenantApi<BlogList>(`/blogs?page=${page}&pageSize=12`);
if (!Array.isArray(data.items) || typeof data.total !== 'number') throw new Error('Invalid blog list response');
return data;
});
export const getBlog = cache(async (id: string) => {
const data = await tenantApi<{blog: Blog}>(`/blogs/by-id/${encodeURIComponent(id)}`);
if (!data.blog || !data.blog.id || typeof data.blog.title !== 'string') throw new Error('Invalid blog detail response');
return data.blog;
});
export function titleSlug(title: string) { return title.trim().replace(/[^\p{L}\p{N}\s-]/gu, '').replace(/[\s-]+/g, '-') || 'article'; }
export function blogHref(blog: Blog) { return `/blog/${encodeURIComponent(blog.id)}/${encodeURIComponent(titleSlug(blog.title))}`; }
export function plainText(value: string) { return sanitizeHtml(value, {allowedTags: [], allowedAttributes: {}}); }
export function summary(blog: Blog) { return plainText(blog.excerpt || blog.summary || blog.content || blog.text || '').slice(0,160); }
export function cover(blog: Blog) { return safeUrl(blog.coverUrl || blog.imageUrl); }
export function articleHtml(blog: Blog) {
return sanitizeHtml(blog.content || blog.text || '', {
allowedTags: ['p','br','h2','h3','h4','strong','b','em','i','ul','ol','li','blockquote','a','img','figure','figcaption','table','thead','tbody','tr','th','td','hr','pre','code'],
allowedAttributes: {a:['href','title'],img:['src','alt','width','height']},
allowedSchemes: ['https','http','mailto'], allowedSchemesByTag: {img:['https']},
allowProtocolRelative: false, transformTags: {h1:'h2'},
});
}