115 lines
5.7 KiB
TypeScript
115 lines
5.7 KiB
TypeScript
import { PrismaClient } from '@prisma/client';
|
||
import bcrypt from 'bcryptjs';
|
||
|
||
const db = new PrismaClient();
|
||
|
||
const CATEGORIES = [
|
||
{ slug: 'apparel', name: 'Apparel' },
|
||
{ slug: 'accessories', name: 'Accessories' },
|
||
{ slug: 'home', name: 'Home' },
|
||
];
|
||
|
||
const PRODUCTS = [
|
||
{ slug: 'classic-tee', name: 'Classic Tee', price: 2900, stock: 40, image: '/art/product-classic-tee.webp', category: 'apparel', description: 'A soft, durable everyday t-shirt.', variants: [{ name: 'S', priceDelta: 0 }, { name: 'M', priceDelta: 0 }, { name: 'L', priceDelta: 200 }] },
|
||
{ slug: 'hoodie', name: 'Heavyweight Hoodie', price: 5900, stock: 25, image: '/art/product-hoodie.webp', category: 'apparel', description: 'Brushed-back fleece with a relaxed fit.', variants: [{ name: 'M', priceDelta: 0 }, { name: 'L', priceDelta: 0 }, { name: 'XL', priceDelta: 300 }] },
|
||
{ slug: 'cap', name: 'Everyday Cap', price: 1900, stock: 60, image: '/art/product-cap.webp', category: 'apparel', description: 'Six-panel cotton cap with a curved brim.' },
|
||
{ slug: 'socks', name: 'Crew Socks (3-pack)', price: 1500, stock: 80, image: '/art/product-socks.webp', category: 'apparel', description: 'Cushioned cotton socks.' },
|
||
{ slug: 'tote', name: 'Canvas Tote', price: 2400, stock: 35, image: '/art/product-tote.webp', category: 'accessories', description: 'Heavy canvas tote with an inner pocket.' },
|
||
{ slug: 'mug', name: 'Enamel Mug', price: 1800, stock: 50, image: '/art/product-mug.webp', category: 'home', description: 'A sturdy 12oz enamel mug.' },
|
||
{ slug: 'bottle', name: 'Insulated Bottle', price: 3200, stock: 30, image: '/art/product-bottle.webp', category: 'accessories', description: 'Keeps drinks cold for 24 hours.' },
|
||
{ slug: 'notebook', name: 'Dotted Notebook', price: 1600, stock: 70, image: '/art/product-notebook.webp', category: 'home', description: 'A5 dotted journal, 160 pages.' },
|
||
{ slug: 'pen', name: 'Precision Pen', price: 900, stock: 120, image: '/art/product-pen.webp', category: 'accessories', description: 'Aluminium body, 0.5mm refill.' },
|
||
{ slug: 'stickers', name: 'Sticker Pack', price: 700, stock: 200, image: '/art/product-stickers.webp', category: 'accessories', description: 'Ten weatherproof vinyl stickers.' },
|
||
{ slug: 'lamp', name: 'Desk Lamp', price: 4200, stock: 18, image: '/art/product-lamp.webp', category: 'home', description: 'Warm dimmable LED desk lamp.' },
|
||
{ slug: 'poster', name: 'Framed Poster', price: 3800, stock: 22, image: '/art/product-poster.webp', category: 'home', description: 'Museum-grade print, 18×24″.' },
|
||
];
|
||
|
||
async function main() {
|
||
const password = await bcrypt.hash('password', 10);
|
||
|
||
const admin = await db.user.upsert({
|
||
where: { email: 'admin@example.com' },
|
||
update: { role: 'admin' },
|
||
create: { email: 'admin@example.com', name: 'Store Admin', role: 'admin', passwordHash: password },
|
||
});
|
||
|
||
const customer = await db.user.upsert({
|
||
where: { email: 'customer@example.com' },
|
||
update: {},
|
||
create: { email: 'customer@example.com', name: 'Demo Customer', role: 'user', passwordHash: password },
|
||
});
|
||
|
||
await db.subscription.upsert({ where: { userId: admin.id }, update: {}, create: { userId: admin.id, plan: 'pro', status: 'active', provider: 'mock' } });
|
||
await db.subscription.upsert({ where: { userId: customer.id }, update: {}, create: { userId: customer.id, plan: 'free', status: 'active', provider: 'mock' } });
|
||
|
||
const categories = new Map<string, string>();
|
||
for (const c of CATEGORIES) {
|
||
const row = await db.category.upsert({ where: { slug: c.slug }, update: { name: c.name }, create: c });
|
||
categories.set(c.slug, row.id);
|
||
}
|
||
|
||
const products: Record<string, string> = {};
|
||
for (const p of PRODUCTS) {
|
||
const row = await db.product.upsert({
|
||
where: { slug: p.slug },
|
||
update: { price: p.price, stock: p.stock, name: p.name, description: p.description, image: p.image, variants: p.variants ?? undefined, categoryId: categories.get(p.category) },
|
||
create: { slug: p.slug, name: p.name, description: p.description, price: p.price, stock: p.stock, image: p.image, variants: p.variants ?? undefined, categoryId: categories.get(p.category) },
|
||
});
|
||
products[p.slug] = row.id;
|
||
}
|
||
|
||
if ((await db.order.count()) === 0) {
|
||
await db.order.create({
|
||
data: {
|
||
userId: customer.id,
|
||
email: customer.email,
|
||
address: '1 Demo Street, Springfield',
|
||
status: 'paid',
|
||
total: 2900 + 5900 * 2,
|
||
currency: 'USD',
|
||
provider: 'mock',
|
||
providerRef: 'seed_order_1',
|
||
items: {
|
||
create: [
|
||
{ productId: products['classic-tee'], name: 'Classic Tee', variant: 'M', price: 2900, quantity: 1 },
|
||
{ productId: products['hoodie'], name: 'Heavyweight Hoodie', variant: 'L', price: 5900, quantity: 2 },
|
||
],
|
||
},
|
||
},
|
||
});
|
||
await db.order.create({
|
||
data: {
|
||
userId: customer.id,
|
||
email: customer.email,
|
||
address: '1 Demo Street, Springfield',
|
||
status: 'shipped',
|
||
total: 2400,
|
||
currency: 'USD',
|
||
provider: 'mock',
|
||
providerRef: 'seed_order_2',
|
||
items: { create: [{ productId: products['tote'], name: 'Canvas Tote', price: 2400, quantity: 1 }] },
|
||
},
|
||
});
|
||
}
|
||
|
||
if ((await db.auditLog.count()) === 0) {
|
||
await db.auditLog.createMany({
|
||
data: [
|
||
{ actorId: admin.id, action: 'seed', target: 'system', meta: 'Initial demo data' },
|
||
{ actorId: admin.id, action: 'catalog.loaded', target: `${PRODUCTS.length} products` },
|
||
],
|
||
});
|
||
}
|
||
|
||
console.log(`Seeded store: ${admin.email} (admin), ${customer.email} (customer) — password: "password"`);
|
||
}
|
||
|
||
main()
|
||
.catch((e) => {
|
||
console.error(e);
|
||
process.exit(1);
|
||
})
|
||
.finally(async () => {
|
||
await db.$disconnect();
|
||
});
|