/** * SEO Audit Script * Runs comprehensive SEO audits across all content */ import { db } from '../db/index.js'; import { pages, translations } from '../db/schema.js'; import { eq } from 'drizzle-orm'; import { runSeoAudit, generateSitemap } from '../services/seo-service.js'; import { ALL_SITES } from '../config/sites.js'; import { writeFileSync, mkdirSync } from 'fs'; import { join } from 'path'; async function runSeoAuditBatch(options: { domain?: string; language?: string; generateSitemaps?: boolean; outputDir?: string; } = {}) { console.log('πŸ” Starting SEO audit...\n'); const outputDir = options.outputDir || './output/seo'; mkdirSync(outputDir, { recursive: true }); // Audit source pages const sourcePages = db.select().from(pages) .where(eq(pages.status, 'published')) .all(); console.log(`Auditing ${sourcePages.length} source pages...\n`); const results: Array<{ url: string; language: string; score: number; issues: number; }> = []; for (let i = 0; i < sourcePages.length; i++) { const page = sourcePages[i]; console.log(` [${i + 1}/${sourcePages.length}] ${page.title}`); try { // Audit source const sourceAudit = runSeoAudit(page.id); results.push({ url: sourceAudit.url, language: sourceAudit.language, score: sourceAudit.score, issues: sourceAudit.issues.length }); // Audit translations const pageTranslations = db.select().from(translations) .where(eq(translations.pageId, page.id)) .all(); for (const translation of pageTranslations) { if (options.language && translation.targetLanguage !== options.language) continue; const transAudit = runSeoAudit(page.id, translation.targetLanguage); results.push({ url: transAudit.url, language: transAudit.language, score: transAudit.score, issues: transAudit.issues.length }); } } catch (err) { console.log(` βœ— Error: ${err instanceof Error ? err.message : 'Unknown'}`); } } // Summary const avgScore = results.length > 0 ? results.reduce((sum, r) => sum + r.score, 0) / results.length : 0; const lowScorePages = results.filter(r => r.score < 70); console.log(`\nπŸ“Š SEO Audit Summary`); console.log(` Total audited: ${results.length}`); console.log(` Average score: ${Math.round(avgScore * 10) / 10}/100`); console.log(` Pages needing attention: ${lowScorePages.length}`); if (lowScorePages.length > 0) { console.log(`\n⚠️ Low scores:`); lowScorePages.forEach(r => { console.log(` - ${r.url} (${r.language}): ${Math.round(r.score)}/100`); }); } // Generate sitemaps if (options.generateSitemaps) { console.log(`\nπŸ—ΊοΈ Generating sitemaps...`); for (const site of ALL_SITES) { const sitemap = generateSitemap(site.domain); const filename = join(outputDir, `sitemap-${site.domain}.xml`); writeFileSync(filename, sitemap); console.log(` βœ“ ${filename}`); } } // Save report const report = { timestamp: new Date().toISOString(), summary: { totalAudited: results.length, averageScore: Math.round(avgScore * 10) / 10, needsAttention: lowScorePages.length }, results }; const reportFile = join(outputDir, `audit-report-${new Date().toISOString().split('T')[0]}.json`); writeFileSync(reportFile, JSON.stringify(report, null, 2)); console.log(`\nπŸ“ Report saved: ${reportFile}`); console.log('\nβœ… SEO audit complete!'); } // CLI const args = process.argv.slice(2); const options: { domain?: string; language?: string; generateSitemaps?: boolean; } = {}; if (args.includes('--sitemaps')) options.generateSitemaps = true; const domainArg = args.find(a => a.startsWith('--domain=')); if (domainArg) options.domain = domainArg.split('=')[1]; const langArg = args.find(a => a.startsWith('--lang=')); if (langArg) options.language = langArg.split('=')[1]; runSeoAuditBatch(options).catch(console.error);