// seed.cjs - Prisma 7 Seed Script // Note: Bun loads .env automatically - no need for dotenv const fs = require('fs'); const path = require('path'); const { PrismaClient } = require('@prisma/client'); const { Pool } = require('pg'); const { PrismaPg } = require('@prisma/adapter-pg'); const bcrypt = require('bcryptjs'); const pool = new Pool({ connectionString: process.env.DATABASE_URL }); const adapter = new PrismaPg(pool); const prisma = new PrismaClient({ adapter }); const scentNames = [ 'ตะไคร้', 'ลาเวนเดอร์', 'วานิลลา', 'ซากุระ', 'มะลิ', 'ยูคาลิปตัส', 'เฟรชลาเวนเดอร์', 'เปเปอร์มิ้น', 'โอเชี่ยน', 'เซเว่นซี', 'ดอกแก้ว', 'ดอกโมก', 'ลีลาวดี', 'ส้ม', 'มะนาว', 'แคนตาลูป', 'แอปเปิ้ลแดง', 'แอปเปิ้ลเขียว', 'ดอกลิลลี่', 'โรสเมรี่', 'อัญชัน', 'กุหลาบแดง', 'หมากฝรั่ง', 'ผลไม้รวม', 'มะกรูด', 'แซนด้าวูส', 'ดอกปีบ', 'ดอกบัว', 'โอโซน', 'ไทยสปา', 'กล้วย', 'กาแฟ', 'ดอกชมนาด', 'กลิ่นน้ำนม', 'ดอกการะเวก', 'ดอกจำปา', 'ดอกพิกุล', 'ดอกพุด', 'ดอกราตรี', 'ดอกลำดวน', 'ทับทิม', 'มะละกอสุก', 'ดอกราชาวดี', 'กลิ่นลูกพีช', 'กลิ่นองุ่น' ]; const categoryConfigs = [ { name: 'Oil Ozone', sizes: ['30ml', '60ml', '85ml', '120ml'], priceMap: { '30ml': 69, '60ml': 100, '85ml': 120, '120ml': 150 } }, { name: 'Aroma Oil', sizes: ['30ml', '60ml', '85ml', '120ml'], priceMap: { '30ml': 90, '60ml': 150, '85ml': 165, '120ml': 225 } // 120ml ปรับเป็น 225 (฿1.875/ml) ให้ถูกกว่า 85ml ต่อ ml }, { name: 'Reed Diffuser', sizes: ['60ml', '85ml', '120ml'], priceMap: { '60ml': 120, '85ml': 160, '120ml': 190 } }, { name: 'Room Spray', sizes: ['85ml'], priceMap: { '85ml': 150 } }, { name: 'Car Diffuser', sizes: ['25ml'], priceMap: { '25ml': 60 } } ]; async function main() { process.stdout.write('🚀 Starting Advanced Seeding (Products & Variants)...\n'); // Load or create category description files const descriptionsDir = path.join(__dirname, 'descriptions'); if (!fs.existsSync(descriptionsDir)) { fs.mkdirSync(descriptionsDir); } const categoryDescriptions = {}; for (const config of categoryConfigs) { const safeName = config.name.replace(/\s+/g, '_'); const descPath = path.join(descriptionsDir, `${safeName}.txt`); // Auto-create with default template if not exists if (!fs.existsSync(descPath)) { const defaultDesc = `ผลิตภัณฑ์ ${config.name} กลิ่น {{scent}} มอบประสบการณ์ความหอมที่ผ่อนคลายและสดชื่น ช่วยให้บรรยากาศในห้องของคุณน่าอยู่มากยิ่งขึ้น`; fs.writeFileSync(descPath, defaultDesc, 'utf8'); } categoryDescriptions[config.name] = fs.readFileSync(descPath, 'utf8'); // Auto-create category details const detailsDir = path.join(__dirname, 'details'); if (!fs.existsSync(detailsDir)) { fs.mkdirSync(detailsDir); } const detailPath = path.join(detailsDir, `${safeName}.txt`); if (!fs.existsSync(detailPath)) { const defaultDetail = `รายละเอียดฉบับเต็มของ ${config.name} กลิ่น {{scent}}\n- ส่วนประกอบนำเข้าคุณภาพสูง\n- กระจายความหอมได้ยาวนานในห้อง\n- ปลอดภัยต่อสุขภาพและเหมาะสำหรับทุกคนในครอบครัว`; fs.writeFileSync(detailPath, defaultDetail, 'utf8'); } categoryConfigs[config.name] = categoryConfigs[config.name] || {}; categoryConfigs[config.name].detailsTemplate = fs.readFileSync(detailPath, 'utf8'); } // 0. Seed Admin User const adminPassword = await bcrypt.hash('irelax123', 10); await prisma.user.upsert({ where: { username: 'admin' }, update: { password: adminPassword }, create: { username: 'admin', password: adminPassword, role: 'ADMIN' } }); process.stdout.write('✅ Seeded Admin User (admin / irelax123).\n'); // 1. Seed Scents & Categories const scents = []; for (const name of scentNames) { const s = await prisma.scent.upsert({ where: { name }, update: {}, create: { name } }); scents.push(s); } process.stdout.write(`✅ Seeded ${scents.length} Scents.\n`); const categories = []; for (const config of categoryConfigs) { const c = await prisma.category.upsert({ where: { name: config.name }, update: {}, create: { name: config.name } }); categories.push({ ...c, allowedSizes: config.sizes, priceMap: config.priceMap, detailsTemplate: categoryConfigs[config.name].detailsTemplate }); } process.stdout.write(`✅ Seeded ${categories.length} Categories.\n`); // 2. Cross Scents x Categories to create Products process.stdout.write('📦 Generating Products and Variants...\n'); let productCount = 0; let variantCount = 0; for (const scent of scents) { for (const category of categories) { const productName = `${scent.name} ${category.name}`; // Get category description template and replace {{scent}} let productDescription = categoryDescriptions[category.name] || `Product ${scent.name} in category ${category.name}`; productDescription = productDescription.replace(/{{scent}}/g, scent.name); let productDetails = category.detailsTemplate || ''; productDetails = productDetails.replace(/{{scent}}/g, scent.name); // Upsert Product const product = await prisma.product.upsert({ where: { scentId_categoryId: { scentId: scent.id, categoryId: category.id } }, update: { name: productName, description: productDescription, details: productDetails }, create: { name: productName, scentId: scent.id, categoryId: category.id, description: productDescription, details: productDetails } }); productCount++; // 3. Create Default Product Image // Using existing category-based image paths as initial seed data const defaultImagePath = category.name === 'Oil Ozone' || category.name === 'Aroma Oil' || category.name === 'Car Diffuser' ? '/images/oil_ozone.png' : '/images/reed_diffuser.png'; await prisma.productImage.upsert({ where: { id: (product.id * 10) + 1 }, // Pseudo-stable ID for seed update: { url: defaultImagePath }, create: { id: (product.id * 10) + 1, productId: product.id, url: defaultImagePath, order: 0 } }).catch(() => { // Fallback for cases where ID might conflict or upsert on non-unique field return prisma.productImage.create({ data: { productId: product.id, url: defaultImagePath, order: 0 } }); }); // 4. Create Variants (Sizes) with prices from priceMap for (const size of category.allowedSizes) { const price = category.priceMap?.[size] ?? 0; await prisma.productVariant.upsert({ where: { productId_size: { productId: product.id, size: size } }, update: { price }, // อัปเดตราคาเมื่อ re-seed create: { productId: product.id, size: size, price: price } }); variantCount++; } } process.stdout.write(`.`); // Progress indicator } process.stdout.write(`\n✨ Seeding Complete!\n`); process.stdout.write(`📊 Summary:\n`); process.stdout.write(` - Scents: ${scents.length}\n`); process.stdout.write(` - Categories: ${categories.length}\n`); process.stdout.write(` - Products: ${productCount}\n`); process.stdout.write(` - Variants: ${variantCount}\n`); } main() .catch((e) => { process.stderr.write(`❌ Error: ${e.message}\n`); process.exit(1); }) .finally(async () => { await prisma.$disconnect(); });