226 lines
8.7 KiB
JavaScript
226 lines
8.7 KiB
JavaScript
// 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 { PrismaPg } = require('@prisma/adapter-pg');
|
|
const bcrypt = require('bcryptjs');
|
|
|
|
const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL });
|
|
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();
|
|
});
|