// prisma/seed.js const { PrismaClient, DealStatus, SaleType, AffiliateType } = require("@prisma/client") const fs = require("fs") const path = require("path") const prisma = new PrismaClient() function randInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min } // Stabil gerçek foto linkleri (redirect yok, hotlink derdi az) function realImage(seed, w = 1200, h = 900) { return `https://picsum.photos/seed/${encodeURIComponent(seed)}/${w}/${h}` } // Son N gün içinde random tarih function randomDateWithinLastDays(days = 5) { const now = Date.now() const maxBack = days * 24 * 60 * 60 * 1000 const offset = randInt(0, maxBack) return new Date(now - offset) } function normalizeSlug(s) { return String(s ?? "").trim().toLowerCase() } function toNumberOrNull(v) { if (v === null || v === undefined || v === "") return null const n = Number(v) return Number.isFinite(n) ? n : null } async function upsertTagBySlug(slug, name) { const s = normalizeSlug(slug) return prisma.tag.upsert({ where: { slug: s }, update: { name }, create: { slug: s, name }, }) } async function attachTagsToDeal(dealId, tagSlugs) { const unique = [...new Set((tagSlugs ?? []).map(normalizeSlug).filter(Boolean))] if (!unique.length) return const tags = await prisma.$transaction( unique.map((slug) => prisma.tag.upsert({ where: { slug }, update: {}, create: { slug, name: slug }, }) ) ) await prisma.dealTag.createMany({ data: tags.map((t) => ({ dealId, tagId: t.id })), skipDuplicates: true, }) } function loadCategoriesJson(filePath) { const raw = fs.readFileSync(filePath, "utf-8") const arr = JSON.parse(raw) if (!Array.isArray(arr)) throw new Error("categories.json array olmalı") const cats = arr.map((c) => ({ id: Number(c.id), name: String(c.name ?? "").trim(), slug: normalizeSlug(c.slug), description: c.description, parentId: c.parentId === null || c.parentId === undefined ? null : Number(c.parentId), })) for (const c of cats) { if (!Number.isInteger(c.id)) throw new Error(`Category id invalid: ${c.id}`) if (!c.name) throw new Error(`Category name boş olamaz (id=${c.id})`) if (!c.slug) throw new Error(`Category slug boş olamaz (id=${c.id})`) } const has0 = cats.some((c) => c.id === 0) if (!has0) { cats.unshift({ id: 0, name: "Undefined", slug: "undefined", parentId: null }) } const idSet = new Set() const slugSet = new Set() for (const c of cats) { if (idSet.has(c.id)) throw new Error(`categories.json duplicate id: ${c.id}`) idSet.add(c.id) if (slugSet.has(c.slug)) throw new Error(`categories.json duplicate slug: ${c.slug}`) slugSet.add(c.slug) } return cats } async function seedCategoriesFromJson(categoriesFilePath) { const categories = loadCategoriesJson(categoriesFilePath) await prisma.$transaction( categories.map((c) => prisma.category.upsert({ where: { id: c.id }, update: { name: c.name, slug: c.slug, description: c.description, }, create: { id: c.id, name: c.name, slug: c.slug, description: c.description, parentId: null, }, }) ), { timeout: 60_000 } ) await prisma.$transaction( categories.map((c) => prisma.category.update({ where: { id: c.id }, data: { parentId: c.parentId }, }) ), { timeout: 60_000 } ) await prisma.$executeRawUnsafe(` SELECT setval( pg_get_serial_sequence('"Category"', 'id'), COALESCE((SELECT MAX(id) FROM "Category"), 0) + 1, false ); `) return { count: categories.length } } function loadDealsJson(filePath) { const raw = fs.readFileSync(filePath, "utf-8") const arr = JSON.parse(raw) if (!Array.isArray(arr)) throw new Error("deals.json array olmalı") const items = arr.map((d, idx) => { const title = String(d.title ?? "").trim() const url = String(d.url ?? "").trim() const q = String(d.q ?? "").trim() const price = toNumberOrNull(d.price) const originalPrice = toNumberOrNull(d.originalPrice) const shippingPrice = toNumberOrNull(d.shippingPrice) if (!title) throw new Error(`deals.json: title boş (index=${idx})`) if (!url) throw new Error(`deals.json: url boş (index=${idx})`) if (price === null) throw new Error(`deals.json: price invalid (index=${idx})`) // Mantık: originalPrice varsa price'dan küçük olamaz if (originalPrice !== null && originalPrice < price) { throw new Error(`deals.json: originalPrice < price (index=${idx})`) } return { title, url, q, price, originalPrice, shippingPrice, } }) // url unique olsun (seed idempotent) const urlSet = new Set() for (const it of items) { if (urlSet.has(it.url)) throw new Error(`deals.json duplicate url: ${it.url}`) urlSet.add(it.url) } return items } // deals.json’dan seed + her deal’a 3 foto + score 0-200 + tarih dağılımı: // - %70: son 5 gün // - %30: 9-11 gün önce async function seedDealsFromJson({ userId, sellerId, categoryId, dealsFilePath }) { const baseItems = loadDealsJson(dealsFilePath) // 30 adet olacak şekilde çoğalt (title/url benzersizleşsin) const items = [] for (let i = 0; i < 30; i++) { const base = baseItems[i % baseItems.length] const n = i + 1 // price'ı hafif oynat (base price üzerinden) const price = Number((base.price * (0.9 + randInt(0, 30) / 100)).toFixed(2)) // originalPrice varsa, yeni price'a göre ölçekle (mantık korunur) let originalPrice = null if (base.originalPrice !== null && base.originalPrice !== undefined) { const ratio = base.originalPrice / base.price // >= 1 olmalı originalPrice = Number((price * ratio).toFixed(2)) if (originalPrice < price) originalPrice = Number((price * 1.05).toFixed(2)) } // shippingPrice varsa bazen aynen, bazen 0/ufak varyasyon (ama null değilse) let shippingPrice = null if (base.shippingPrice !== null && base.shippingPrice !== undefined) { // 70% aynı, 30% küçük oynat if (Math.random() < 0.7) { shippingPrice = Number(base.shippingPrice) } else { const candidates = [0, 19.9, 29.9, 39.9, 49.9, 59.9] shippingPrice = candidates[randInt(0, candidates.length - 1)] } } items.push({ title: `${base.title} #${n}`, price, originalPrice, shippingPrice, url: `${base.url}${base.url.includes("?") ? "&" : "?"}seed=${n}`, q: base.q || "product", }) } for (let i = 0; i < items.length; i++) { const it = items[i] const older = Math.random() < 0.3 const createdAt = older ? new Date(Date.now() - randInt(9, 11) * 24 * 60 * 60 * 1000 - randInt(0, 12) * 60 * 60 * 1000) : randomDateWithinLastDays(5) const dealData = { title: it.title, description: "Seed test deal açıklaması (otomatik üretim).", url: it.url, price: it.price, originalPrice: it.originalPrice ?? null, shippingPrice: it.shippingPrice ?? null, status: DealStatus.ACTIVE, saletype: SaleType.ONLINE, affiliateType: AffiliateType.NON_AFFILIATE, commentCount: randInt(0, 25), userId, sellerId, categoryId, score: randInt(0, 200), createdAt, } const existing = await prisma.deal.findFirst({ where: { url: it.url }, select: { id: true }, }) const deal = existing ? await prisma.deal.update({ where: { id: existing.id }, data: dealData }) : await prisma.deal.create({ data: dealData }) await prisma.dealImage.deleteMany({ where: { dealId: deal.id } }) await prisma.dealImage.createMany({ data: [ { dealId: deal.id, imageUrl: realImage(`${it.q}-${i}-1`), order: 0 }, { dealId: deal.id, imageUrl: realImage(`${it.q}-${i}-2`), order: 1 }, { dealId: deal.id, imageUrl: realImage(`${it.q}-${i}-3`), order: 2 }, ], }) } } async function main() { const hashedPassword = "$2b$10$PVfLq2NmcGmKbhE5VK3yNeVj46O/1w2p/2BNu4h1CYacqSgkCcoCW" // ---------- USERS ---------- const admin = await prisma.user.upsert({ where: { email: "test" }, update: {}, create: { username: "test", email: "test", passwordHash: hashedPassword, role: "ADMIN", }, }) const user = await prisma.user.upsert({ where: { email: "test2" }, update: {}, create: { username: "test2", email: "test2", passwordHash: hashedPassword, role: "USER", }, }) // ---------- SELLER ---------- const amazon = await prisma.seller.upsert({ where: { name: "Amazon" }, update: { isActive: true }, create: { name: "Amazon", url: "https://www.amazon.com.tr", sellerLogo:"https://1000logos.net/wp-content/uploads/2016/10/Amazon-logo-meaning.jpg", isActive: true, createdById: admin.id, }, }) // ---------- SELLER DOMAINS ---------- const domains = ["amazon.com", "amazon.com.tr"] for (const domain of domains) { await prisma.sellerDomain.upsert({ where: { domain }, update: { sellerId: amazon.id }, create: { domain, sellerId: amazon.id, createdById: admin.id, }, }) } // ---------- CATEGORIES (FROM JSON) ---------- const categoriesFilePath = path.join(__dirname, "categories.json") const { count } = await seedCategoriesFromJson(categoriesFilePath) const catSSD = await prisma.category.findUnique({ where: { slug: "pc-ssd" }, select: { id: true }, }) // ---------- TAGS ---------- await upsertTagBySlug("ssd", "SSD") await upsertTagBySlug("nvme", "NVMe") await upsertTagBySlug("1tb", "1TB") // ---------- DEAL (tek örnek) ---------- const dealUrl = "https://www.amazon.com.tr/dp/test" const existing = await prisma.deal.findFirst({ where: { url: dealUrl }, select: { id: true }, }) const dealData = { title: "Samsung SSD 1TB", description: "Test deal açıklaması", url: dealUrl, price: 1299.99, originalPrice: 1499.99, // örnek shippingPrice: 0, // örnek status: DealStatus.ACTIVE, saletype: SaleType.ONLINE, affiliateType: AffiliateType.NON_AFFILIATE, commentCount: 1, userId: user.id, sellerId: amazon.id, categoryId: catSSD?.id ?? 0, // score: randInt(0, 200), // modelinde varsa aç } const deal = existing ? await prisma.deal.update({ where: { id: existing.id }, data: dealData }) : await prisma.deal.create({ data: dealData }) // ---------- DEAL TAGS ---------- await attachTagsToDeal(deal.id, ["ssd", "nvme", "1tb"]) // ---------- DEAL IMAGES (tek örnek) ---------- await prisma.dealImage.deleteMany({ where: { dealId: deal.id } }) await prisma.dealImage.createMany({ data: [ { dealId: deal.id, imageUrl: realImage("nvme-ssd-single-1"), order: 0 }, { dealId: deal.id, imageUrl: realImage("nvme-ssd-single-2"), order: 1 }, { dealId: deal.id, imageUrl: realImage("nvme-ssd-single-3"), order: 2 }, ], }) // ✅ ---------- deals.json’dan 30 DEAL ÜRET ---------- const dealsFilePath = path.join(__dirname, "deals.json") await seedDealsFromJson({ userId: user.id, sellerId: amazon.id, categoryId: catSSD?.id ?? 0, dealsFilePath, }) // ---------- VOTE ---------- await prisma.dealVote.upsert({ where: { dealId_userId: { dealId: deal.id, userId: admin.id } }, update: { voteType: 1, lastVotedAt: new Date() }, create: { dealId: deal.id, userId: admin.id, voteType: 1 }, }) // ---------- COMMENT ---------- const hasComment = await prisma.comment.findFirst({ where: { dealId: deal.id, userId: admin.id, text: "Gerçekten iyi fırsat" }, select: { id: true }, }) if (!hasComment) { await prisma.comment.create({ data: { text: "Gerçekten iyi fırsat", userId: admin.id, dealId: deal.id }, }) } console.log(`✅ Seed tamamlandı (categories.json yüklendi: ${count} kategori)`) console.log("✅ deals.json baz alınarak 30 adet test deal + 3'er görsel + score(0-200) + tarih dağılımı eklendi/güncellendi") } main() .catch((err) => { console.error(err) process.exit(1) }) .finally(async () => { await prisma.$disconnect() })