canina/backend/scripts/seo-backfill.ts

129 lines
4.4 KiB
TypeScript

import process from 'node:process';
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
function stripHtml(html: string): string {
return html.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim();
}
async function runSeoBackfill() {
console.log('🚀 Starting SEO & Content Backfill Script for Canina Iran...\n');
// 1. Backfill Products
const products = await prisma.product.findMany();
console.log(`📦 Found ${products.length} products to check...`);
let updatedProducts = 0;
for (const product of products) {
const dataToUpdate: Record<string, any> = {};
if (!product.metaTitle || product.metaTitle.trim() === '') {
dataToUpdate.metaTitle = `${product.nameFa} (${product.packageSize} ${product.unit}) | کنینا ایران`;
}
if (!product.metaDescription || product.metaDescription.trim() === '') {
const baseDesc = product.shortDescription || stripHtml(product.description || '');
const cleanDesc = baseDesc.substring(0, 150).trim();
dataToUpdate.metaDescription = cleanDesc
? `${cleanDesc} | خرید اینترنتی با ضمانت اصالت کنینا آلمان.`
: `خرید آنلاین ${product.nameFa} با ضمانت ۱۰۰٪ اصالت آلمان و ارسال سریع در کنینا ایران.`;
}
if (!product.featuredImageAlt || product.featuredImageAlt.trim() === '') {
dataToUpdate.featuredImageAlt = `مکمل دارویی ${product.nameFa} Canina آلمان`;
}
if (!product.keywords || product.keywords.trim() === '') {
const tags = [
product.nameFa,
product.nameEn,
`مکمل ${product.suitableFor}`,
'کنینا آلمان',
'Canina',
product.categorySlug,
].filter(Boolean);
dataToUpdate.keywords = tags.join(', ');
}
if (Object.keys(dataToUpdate).length > 0) {
await prisma.product.update({
where: { id: product.id },
data: dataToUpdate,
});
updatedProducts++;
}
}
console.log(`✅ Products backfill complete: ${updatedProducts} products updated.\n`);
// 2. Backfill Blogs & Cross-Linking
const blogs = await prisma.blog.findMany();
console.log(`📰 Found ${blogs.length} blog posts to check...`);
let updatedBlogs = 0;
for (const blog of blogs) {
const dataToUpdate: Record<string, any> = {};
if (!blog.metaTitle || blog.metaTitle.trim() === '') {
dataToUpdate.metaTitle = `${blog.title} | مجله تخصصی کنینا`;
}
if (!blog.metaDescription || blog.metaDescription.trim() === '') {
const baseExcerpt = blog.excerpt || stripHtml(blog.content || '');
dataToUpdate.metaDescription = baseExcerpt.substring(0, 155).trim();
}
if (!blog.imageAlt || blog.imageAlt.trim() === '') {
dataToUpdate.imageAlt = `تصویر راهنمای تخصصی ${blog.title}`;
}
// Auto Cross-Linking: If relatedProductIds is empty, match top products by category or title keywords
if (!blog.relatedProductIds || blog.relatedProductIds.length === 0) {
const keywords = [blog.title, blog.keywords].filter(Boolean).join(' ');
const matchingProducts = await prisma.product.findMany({
where: {
OR: [
{ categorySlug: { contains: blog.categoryId || '', mode: 'insensitive' } },
{ nameFa: { contains: blog.title.split(' ')[0] || '', mode: 'insensitive' } },
{ description: { contains: blog.title.split(' ')[0] || '', mode: 'insensitive' } },
],
},
take: 3,
select: { id: true },
});
if (matchingProducts.length > 0) {
dataToUpdate.relatedProductIds = matchingProducts.map((p) => p.id);
} else {
// Fallback: pick top 2 featured products
const topProducts = await prisma.product.findMany({ take: 2, select: { id: true } });
dataToUpdate.relatedProductIds = topProducts.map((p) => p.id);
}
}
if (Object.keys(dataToUpdate).length > 0) {
await prisma.blog.update({
where: { id: blog.id },
data: dataToUpdate,
});
updatedBlogs++;
}
}
console.log(`✅ Blog posts backfill complete: ${updatedBlogs} posts updated with SEO & cross-links.\n`);
console.log('🎉 SEO Backfill and Internal Cross-Linking finished successfully!');
}
runSeoBackfill()
.catch((e) => {
console.error('❌ SEO Backfill failed:', e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});