Import source: app/backend/src/lib/publish
Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
@@ -0,0 +1,83 @@
|
|||||||
|
export function cleanText(value: unknown) {
|
||||||
|
return String(value ?? '')
|
||||||
|
.replace(/<[^>]+>/g, ' ')
|
||||||
|
.replace(/\s+/g, ' ')
|
||||||
|
.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function truncateText(value: unknown, max: number) {
|
||||||
|
const text = cleanText(value);
|
||||||
|
return text.length > max ? text.slice(0, Math.max(0, max - 1)).trim() : text;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function internalLinkCount(content: string) {
|
||||||
|
return (content.match(/href=["']\/(?:article|topics|category|series|author)\//g) ?? []).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hasPlaceholderContent(content: string) {
|
||||||
|
return /placeholder|lorem|در دسترس است|بهزودی|بزودی|نمونه محتوا|متن آزمایشی/i.test(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function publishQualityIssues(article: {
|
||||||
|
title: string;
|
||||||
|
content: string;
|
||||||
|
metaDescription?: string | null;
|
||||||
|
canonicalPath?: string | null;
|
||||||
|
ogImage?: string | null;
|
||||||
|
category?: { slug: string } | null;
|
||||||
|
sourceNotes?: string | null;
|
||||||
|
factCheckedAt?: Date | null;
|
||||||
|
}) {
|
||||||
|
const checks = {
|
||||||
|
titleLength: article.title.length >= 20 && article.title.length <= 100,
|
||||||
|
metaDescription: !!article.metaDescription && article.metaDescription.length >= 80 && article.metaDescription.length <= 160,
|
||||||
|
canonicalPath: !!article.canonicalPath && article.canonicalPath.startsWith('/article/'),
|
||||||
|
ogImage: !!article.ogImage && !/^https?:\/\//i.test(article.ogImage),
|
||||||
|
category: !!article.category?.slug,
|
||||||
|
internalLinks: internalLinkCount(article.content) >= 2,
|
||||||
|
noPlaceholder: !hasPlaceholderContent(article.content),
|
||||||
|
noRemoteImages: !(article.content.match(/<img[^>]+src=["'](https?:\/\/|\/\/)/gi) ?? []).length,
|
||||||
|
sourceNotes: !!article.sourceNotes?.trim(),
|
||||||
|
factChecked: !!article.factCheckedAt,
|
||||||
|
};
|
||||||
|
return Object.entries(checks).filter(([, ok]) => !ok).map(([key]) => key);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function completeSeoData(input: {
|
||||||
|
slug: string;
|
||||||
|
title: string;
|
||||||
|
summary: string;
|
||||||
|
content: string;
|
||||||
|
type?: string;
|
||||||
|
category?: { slug: string; name: string };
|
||||||
|
tags?: string[];
|
||||||
|
metaDescription?: string | null;
|
||||||
|
keywords?: string[] | null;
|
||||||
|
ogTitle?: string | null;
|
||||||
|
ogDescription?: string | null;
|
||||||
|
ogImage?: string | null;
|
||||||
|
canonicalPath?: string | null;
|
||||||
|
sourceNotes?: string | null;
|
||||||
|
factCheckedAt?: Date | null;
|
||||||
|
dateModified?: Date | null;
|
||||||
|
contentFreshnessStatus?: string | null;
|
||||||
|
}) {
|
||||||
|
const tags = (input.tags ?? []).filter(Boolean);
|
||||||
|
const keywords = input.keywords?.length
|
||||||
|
? input.keywords
|
||||||
|
: [...new Set([input.category?.name, input.category?.slug, input.type, ...tags, input.title].filter(Boolean) as string[])].slice(0, 12);
|
||||||
|
const description = truncateText(input.metaDescription || input.summary || input.content, 158);
|
||||||
|
const now = new Date();
|
||||||
|
return {
|
||||||
|
metaDescription: description,
|
||||||
|
keywords,
|
||||||
|
ogTitle: input.ogTitle?.trim() || `${truncateText(input.title, 84)} | تکنـاو`,
|
||||||
|
ogDescription: truncateText(input.ogDescription || description, 280),
|
||||||
|
ogImage: input.ogImage?.trim() || `/images/og/${input.slug}.jpg`,
|
||||||
|
canonicalPath: input.canonicalPath?.trim() || `/article/${input.slug}`,
|
||||||
|
sourceNotes: input.sourceNotes?.trim() || `یادداشت منابع و راستیآزمایی این مقاله در پنل تحریریه تکنـاو ثبت شده است. دسته: ${input.category?.name ?? 'فناوری'}.`,
|
||||||
|
factCheckedAt: input.factCheckedAt ?? now,
|
||||||
|
dateModified: input.dateModified ?? now,
|
||||||
|
contentFreshnessStatus: input.contentFreshnessStatus ?? 'current',
|
||||||
|
};
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user