Files
meseo-staging/prisma/seed-ai-competitors.ts
T
Andres Resiri 70daeb214a
CI / Quality gate (push) Has been cancelled
Initial project commit for Coolify staging
2026-08-03 10:45:41 -04:00

116 lines
4.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
async function main() {
console.log("Seeding AI visibility + competitors...");
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.aiVisibilitySnapshot.deleteMany({ where: { brandId: brand.id } });
await prisma.competitorSnapshot.deleteMany({ where: { brandId: brand.id } });
await prisma.competitor.deleteMany({ where: { brandId: brand.id } });
// ── AI Visibility: 30 days of snapshots ──
const platforms = ["chatgpt", "claude", "perplexity", "gemini", "google_aio"];
const topics = [
"best project management tools",
"SEO audit checklist",
"technical SEO guide",
"AI visibility optimization",
"website speed optimization",
];
const pages = ["/features", "/blog/seo-guide", "/technical-audit", "/pricing", "/about"];
for (let d = 30; d >= 0; d--) {
const date = new Date();
date.setDate(date.getDate() - d);
date.setHours(12, 0, 0, 0);
for (let p = 0; p < platforms.length; p++) {
const baseMentions = [5, 4, 3, 2, 1][p];
const growth = 1 + ((30 - d) / 30) * 0.2;
const noise = () => 0.7 + Math.random() * 0.6;
for (let t = 0; t < 2; t++) {
const topicIdx = (p + t) % topics.length;
const mentions = Math.round(baseMentions * growth * noise());
const citations = Math.round(mentions * (0.3 + Math.random() * 0.4));
await prisma.aiVisibilitySnapshot.create({
data: {
brandId: brand.id,
date,
platform: platforms[p],
topic: topics[topicIdx],
mentions,
citations,
visibilityScore: Math.min(100, Math.round(mentions * 8 + citations * 12)),
sourceUrl: pages[topicIdx],
change: d < 28 ? Math.round((Math.random() - 0.3) * 3) : 0,
},
});
}
}
}
console.log(` AI visibility: 30 days × 5 platforms × 2 topics for ${brand.name}`);
// ── Competitors ──
const comps = [
{ name: "RankBoost.io", domain: "rankboost.io", type: "national", strongestPage: "/features/seo-audit", whyWinning: "Published 4 new long-form guides targeting core keywords. Strong internal linking structure.", watchlist: true },
{ name: "ContentKing", domain: "contentking.com", type: "national", strongestPage: "/technical-seo-guide", whyWinning: "12,000-word technical SEO guide displacing competitors in AI citations.", watchlist: true },
{ name: "LocalSEO Pro", domain: "localseepro.com", type: "local", strongestPage: "/local-seo-services", whyWinning: "Dominates local pack for city-specific queries. Strong GBP optimization.", watchlist: false },
{ name: "AgencyFirst", domain: "agencyfirst.io", type: "emerging", strongestPage: "/blog/ai-seo", whyWinning: "New player publishing AI-focused SEO content rapidly.", watchlist: false },
];
for (const c of comps) {
const competitor = await prisma.competitor.create({
data: {
brandId: brand.id,
name: c.name,
domain: c.domain,
type: c.type,
strongestPage: c.strongestPage,
whyWinning: c.whyWinning,
watchlist: c.watchlist,
},
});
// Add weekly snapshots for 12 weeks
for (let w = 12; w >= 0; w--) {
const date = new Date();
date.setDate(date.getDate() - w * 7);
date.setHours(12, 0, 0, 0);
const threat = c.watchlist ? (w < 4 ? "high" : "medium") : "low";
const baseAi = c.name === "RankBoost.io" ? 8 : c.name === "ContentKing" ? 6 : 3;
await prisma.competitorSnapshot.create({
data: {
competitorId: competitor.id,
brandId: brand.id,
date,
movement: w < 3 ? `#${5 - w} → #${3 - w > 0 ? 3 - w : 1}` : "Stable",
rankChange: w < 4 ? Math.round(Math.random() * 3) : 0,
aiMentions: Math.round(baseAi * (1 + (12 - w) / 12 * 0.3) + (Math.random() - 0.5) * 2),
aiMentionChange: w < 6 ? Math.round((Math.random() - 0.3) * 3) : 0,
trafficEstimate: Math.round((5000 + Math.random() * 10000) * (1 + (12 - w) / 12 * 0.15)),
topKeyword: c.name === "RankBoost.io" ? "project management SEO tool" : c.name === "ContentKing" ? "technical SEO audit" : "local SEO services",
threat,
},
});
}
}
console.log(` Competitors: ${comps.length} with 13 weekly snapshots each for ${brand.name}`);
}
console.log("AI visibility + competitors seeding complete!");
}
main()
.catch((e) => { console.error(e); process.exit(1); })
.finally(() => prisma.$disconnect());