133 lines
7.4 KiB
TypeScript
133 lines
7.4 KiB
TypeScript
import { PrismaClient } from "@prisma/client";
|
|
|
|
const prisma = new PrismaClient();
|
|
|
|
async function main() {
|
|
console.log("Seeding audit data...");
|
|
|
|
const brands = await prisma.brand.findMany();
|
|
if (brands.length === 0) { console.log("No brands. Run main seed first."); return; }
|
|
|
|
for (const brand of brands) {
|
|
// Clear existing
|
|
await prisma.technicalIssue.deleteMany({ where: { audit: { brandId: brand.id } } });
|
|
await prisma.technicalAudit.deleteMany({ where: { brandId: brand.id } });
|
|
await prisma.deadPageSource.deleteMany({ where: { deadPage: { brandId: brand.id } } });
|
|
await prisma.deadPage.deleteMany({ where: { brandId: brand.id } });
|
|
await prisma.performancePageScore.deleteMany({ where: { audit: { brandId: brand.id } } });
|
|
await prisma.performanceAudit.deleteMany({ where: { brandId: brand.id } });
|
|
|
|
// ── Technical Audit ──
|
|
await prisma.technicalAudit.create({
|
|
data: {
|
|
brandId: brand.id,
|
|
crawlHealth: 68,
|
|
pagesScanned: 1248,
|
|
duration: 262,
|
|
errorCount: 17,
|
|
warningCount: 34,
|
|
noticeCount: 12,
|
|
passedCount: 89,
|
|
issues: {
|
|
create: [
|
|
{ category: "metadata", severity: "critical", title: "Missing meta description", detail: "14 pages have no meta description tag", pageUrl: "/features", affected: 14 },
|
|
{ category: "metadata", severity: "critical", title: "Duplicate title tags", detail: "6 pages share identical title tags", pageUrl: "/products", affected: 6 },
|
|
{ category: "links", severity: "critical", title: "Broken internal links", detail: "17 links point to pages that return 404", pageUrl: "/blog", affected: 17 },
|
|
{ category: "headings", severity: "medium", title: "Images missing alt text", detail: "31 images have no alt attribute", pageUrl: "/about", affected: 31 },
|
|
{ category: "crawl", severity: "medium", title: "Oversized images (>200 KB)", detail: "23 images exceed recommended size", pageUrl: "/products", affected: 23 },
|
|
{ category: "metadata", severity: "medium", title: "No structured data", detail: "8 pages have no schema markup", pageUrl: "/services", affected: 8 },
|
|
{ category: "crawl", severity: "medium", title: "HTTP links on HTTPS page", detail: "4 mixed content resources detected", pageUrl: "/about", affected: 4 },
|
|
{ category: "headings", severity: "low", title: "Missing H1 tag", detail: "3 pages have no H1 element", pageUrl: "/contact", affected: 3 },
|
|
{ category: "crawl", severity: "low", title: "Slow page load (>3 s)", detail: "9 pages exceed 3 second load time", pageUrl: "/pricing", affected: 9 },
|
|
{ category: "indexability", severity: "low", title: "Missing canonical tags", detail: "11 pages have no canonical URL", pageUrl: "/blog", affected: 11 },
|
|
{ category: "redirects", severity: "medium", title: "Redirect chains", detail: "3 URLs have 2+ redirects before final", pageUrl: "/old-pricing", affected: 3 },
|
|
{ category: "indexability", severity: "medium", title: "Noindex on important pages", detail: "2 service pages accidentally blocked", pageUrl: "/services/seo", affected: 2 },
|
|
],
|
|
},
|
|
},
|
|
});
|
|
console.log(` Technical audit: 12 issues for ${brand.name}`);
|
|
|
|
// ── Dead 404 Pages ──
|
|
const deadPages = [
|
|
{ deadUrl: "/old-pricing", priority: "high", suggestedFix: "Redirect to /pricing", sources: [{ sourceUrl: "/about", anchorText: "our pricing" }, { sourceUrl: "/blog/launch", anchorText: "pricing page" }] },
|
|
{ deadUrl: "/blog/2022/launch", priority: "medium", suggestedFix: "Redirect to /blog", sources: [{ sourceUrl: "/blog", anchorText: "launch post" }] },
|
|
{ deadUrl: "/products/legacy", priority: "medium", suggestedFix: "Redirect to /products", sources: [{ sourceUrl: "/features", anchorText: "legacy product" }, { sourceUrl: "/about", anchorText: "our first product" }] },
|
|
{ deadUrl: "/team/jane-old-bio", priority: "low", suggestedFix: "Redirect to /about#team", sources: [{ sourceUrl: "/blog/team-update", anchorText: "Jane's bio" }] },
|
|
{ deadUrl: "/downloads/brochure-v1", priority: "low", suggestedFix: "Remove links or upload new version", sources: [{ sourceUrl: "/contact", anchorText: "download brochure" }] },
|
|
];
|
|
|
|
for (const dp of deadPages) {
|
|
await prisma.deadPage.create({
|
|
data: {
|
|
brandId: brand.id,
|
|
deadUrl: dp.deadUrl,
|
|
priority: dp.priority,
|
|
suggestedFix: dp.suggestedFix,
|
|
sources: { create: dp.sources },
|
|
},
|
|
});
|
|
}
|
|
console.log(` Dead pages: ${deadPages.length} for ${brand.name}`);
|
|
|
|
// ── Performance Audit (mobile) ──
|
|
const mobilePages = [
|
|
{ pageUrl: "/", performance: 64, accessibility: 89, bestPractices: 78, seo: 92, lcpMs: 3800, inpMs: 280, clsVal: 0.04, fcpMs: 2100, ttfbMs: 620 },
|
|
{ pageUrl: "/about", performance: 72, accessibility: 91, bestPractices: 83, seo: 95, lcpMs: 2900, inpMs: 180, clsVal: 0.02, fcpMs: 1800, ttfbMs: 540 },
|
|
{ pageUrl: "/pricing", performance: 45, accessibility: 78, bestPractices: 72, seo: 88, lcpMs: 5100, inpMs: 420, clsVal: 0.18, fcpMs: 3200, ttfbMs: 890 },
|
|
{ pageUrl: "/blog", performance: 81, accessibility: 93, bestPractices: 89, seo: 97, lcpMs: 1800, inpMs: 120, clsVal: 0.01, fcpMs: 1100, ttfbMs: 380 },
|
|
{ pageUrl: "/contact", performance: 91, accessibility: 95, bestPractices: 92, seo: 98, lcpMs: 1100, inpMs: 90, clsVal: 0.00, fcpMs: 800, ttfbMs: 340 },
|
|
{ pageUrl: "/products", performance: 52, accessibility: 82, bestPractices: 75, seo: 85, lcpMs: 4400, inpMs: 350, clsVal: 0.12, fcpMs: 2800, ttfbMs: 720 },
|
|
{ pageUrl: "/features", performance: 58, accessibility: 84, bestPractices: 77, seo: 87, lcpMs: 4000, inpMs: 310, clsVal: 0.09, fcpMs: 2500, ttfbMs: 680 },
|
|
{ pageUrl: "/login", performance: 93, accessibility: 96, bestPractices: 91, seo: 90, lcpMs: 900, inpMs: 70, clsVal: 0.00, fcpMs: 600, ttfbMs: 280 },
|
|
];
|
|
|
|
await prisma.performanceAudit.create({
|
|
data: {
|
|
brandId: brand.id,
|
|
device: "mobile",
|
|
pages: {
|
|
create: mobilePages.map((p) => ({
|
|
...p,
|
|
device: "mobile",
|
|
status: p.performance >= 90 ? "pass" : p.performance >= 50 ? "average" : "fail",
|
|
})),
|
|
},
|
|
},
|
|
});
|
|
console.log(` Performance audit: ${mobilePages.length} pages (mobile) for ${brand.name}`);
|
|
|
|
// ── Performance Audit (desktop) ──
|
|
const desktopPages = mobilePages.map((p) => ({
|
|
...p,
|
|
performance: Math.min(100, p.performance + 20),
|
|
lcpMs: Math.round(p.lcpMs * 0.5),
|
|
inpMs: Math.round(p.inpMs * 0.5),
|
|
clsVal: Math.round(p.clsVal * 0.5 * 100) / 100,
|
|
fcpMs: Math.round(p.fcpMs * 0.5),
|
|
ttfbMs: Math.round(p.ttfbMs * 0.6),
|
|
}));
|
|
|
|
await prisma.performanceAudit.create({
|
|
data: {
|
|
brandId: brand.id,
|
|
device: "desktop",
|
|
pages: {
|
|
create: desktopPages.map((p) => ({
|
|
...p,
|
|
device: "desktop",
|
|
status: p.performance >= 90 ? "pass" : p.performance >= 50 ? "average" : "fail",
|
|
})),
|
|
},
|
|
},
|
|
});
|
|
console.log(` Performance audit: ${desktopPages.length} pages (desktop) for ${brand.name}`);
|
|
}
|
|
|
|
console.log("Audit seeding complete!");
|
|
}
|
|
|
|
main()
|
|
.catch((e) => { console.error(e); process.exit(1); })
|
|
.finally(() => prisma.$disconnect());
|