// Seed bron: docs/scrum4me-backlog.md → prisma/seed-data/parse-backlog.ts. // Pas alléén de backlog-markdown aan als je seed-data wilt wijzigen, niet deze file. import { PrismaClient } from '@prisma/client' import * as dotenv from 'dotenv' import * as path from 'path' import * as bcrypt from 'bcryptjs' import { loadBacklog } from './seed-data/parse-backlog' // Load env from project root const root = path.resolve(__dirname, '..') dotenv.config({ path: path.join(root, '.env.local'), override: true }) dotenv.config({ path: path.join(root, '.env') }) let prisma: PrismaClient async function main() { const url = process.env.DATABASE_URL if (!url) throw new Error('DATABASE_URL is not set. Check .env.local') const { Pool } = await import('pg') const { PrismaPg } = await import('@prisma/adapter-pg') const pool = new Pool({ connectionString: url }) const adapter = new PrismaPg(pool) prisma = new PrismaClient({ adapter }) console.log('Seeding database...') // Create main demo user const demoHash = await bcrypt.hash('demo1234', 12) const demo = await prisma.user.upsert({ where: { username: 'demo' }, update: {}, create: { username: 'demo', password_hash: demoHash, is_demo: true, roles: { create: [ { role: 'PRODUCT_OWNER' }, { role: 'DEVELOPER' }, ], }, }, }) console.log(`Demo user: ${demo.username} (id: ${demo.id})`) // Create seed user for the product const userHash = await bcrypt.hash('scrum4me123', 12) const user = await prisma.user.upsert({ where: { username: 'lars' }, update: {}, create: { username: 'lars', password_hash: userHash, is_demo: false, roles: { create: [ { role: 'PRODUCT_OWNER' }, { role: 'SCRUM_MASTER' }, { role: 'DEVELOPER' }, ], }, }, }) console.log(`Main user: ${user.username} (id: ${user.id})`) // Create tester user for cross-user isolation tests (no products) const testerHash = await bcrypt.hash('tester123', 12) const tester = await prisma.user.upsert({ where: { username: 'tester' }, update: {}, create: { username: 'tester', password_hash: testerHash, is_demo: false, roles: { create: [ { role: 'PRODUCT_OWNER' }, { role: 'DEVELOPER' }, ], }, }, }) console.log(`Tester user: ${tester.username} (id: ${tester.id})`) // Reset demo product data — delete in dependency order to avoid FK violations const existingProducts = await prisma.product.findMany({ where: { user_id: demo.id }, select: { id: true } }) for (const p of existingProducts) { // Stories reference product_id directly (no cascade), so delete PBIs first (cascades to stories via pbi_id) const existingPbis = await prisma.pbi.findMany({ where: { product_id: p.id }, select: { id: true } }) for (const pbi of existingPbis) { await prisma.story.deleteMany({ where: { pbi_id: pbi.id } }) } await prisma.pbi.deleteMany({ where: { product_id: p.id } }) await prisma.sprint.deleteMany({ where: { product_id: p.id } }) } await prisma.product.deleteMany({ where: { user_id: demo.id } }) const product = await prisma.product.create({ data: { user_id: demo.id, name: 'Scrum4Me', code: 'SCRUM4ME', description: 'Een desktop-first fullstack webapplicatie voor solo developers en kleine Scrum Teams die meerdere softwareprojecten parallel beheren.', repo_url: 'https://github.com/madhura68/Scrum4Me', definition_of_done: 'Code is geïmplementeerd, type-checked, getest, gedocumenteerd in code en docs, en gedeployed naar Vercel zonder regressies.', archived: false, }, }) console.log(`Product created: ${product.name} (id: ${product.id}, owner: demo)`) await prisma.productMember.create({ data: { product_id: product.id, user_id: user.id }, }) console.log(` Added ${user.username} as member of ${product.name}`) const milestones = await loadBacklog(root) console.log(`Loaded backlog: ${milestones.length} milestones, ${milestones.reduce((acc, m) => acc + m.stories.length, 0)} stories`) let productTaskCounter = 0 for (const ms of milestones) { const pbi = await prisma.pbi.create({ data: { product_id: product.id, code: ms.key, title: ms.title, description: ms.goal, priority: ms.priority, sort_order: ms.sort_order, }, }) const sprint = await prisma.sprint.create({ data: { product_id: product.id, sprint_goal: `${ms.key} — ${ms.goal}`, status: ms.sprint_status, }, }) console.log( ` PBI ${pbi.title} (priority ${pbi.priority}) + sprint ${ms.sprint_status}`, ) // M3.5 = de huidige sprint die nog moet beginnen — alle stories en taken // worden geforceerd op niet-uitgevoerd, ongeacht de checkbox in de backlog. const forceOpen = ms.key === 'M3.5' for (const s of ms.stories) { const isActive = ms.sprint_status === 'ACTIVE' const effectivelyDone = !forceOpen && s.status === 'DONE' const inSprint = isActive || effectivelyDone const storyStatus = effectivelyDone ? 'DONE' : isActive ? 'IN_SPRINT' : 'OPEN' const storySummary = s.tasks.map((t) => t.title).join('; ') const story = await prisma.story.create({ data: { pbi_id: pbi.id, product_id: product.id, sprint_id: inSprint ? sprint.id : null, code: s.ref, title: s.title, description: storySummary, acceptance_criteria: s.acceptance_criteria, priority: ms.priority, sort_order: s.sort_order, status: storyStatus, }, }) for (const t of s.tasks) { productTaskCounter += 1 await prisma.task.create({ data: { story_id: story.id, product_id: product.id, sprint_id: inSprint ? sprint.id : null, code: `T-${productTaskCounter}`, title: t.title, description: t.description, priority: ms.priority, sort_order: t.sort_order, status: effectivelyDone ? 'DONE' : 'TO_DO', }, }) } } } const modelPrices = [ { model_id: 'claude-opus-4-7', input_price_per_1m: 15.0, output_price_per_1m: 75.0, cache_read_price_per_1m: 1.5, cache_write_price_per_1m: 18.75, }, { model_id: 'claude-sonnet-4-6', input_price_per_1m: 3.0, output_price_per_1m: 15.0, cache_read_price_per_1m: 0.3, cache_write_price_per_1m: 3.75, }, { model_id: 'claude-haiku-4-5-20251001', input_price_per_1m: 0.8, output_price_per_1m: 4.0, cache_read_price_per_1m: 0.08, cache_write_price_per_1m: 1.0, }, ] for (const mp of modelPrices) { await prisma.modelPrice.upsert({ where: { model_id: mp.model_id }, update: mp, create: mp, }) console.log(` ModelPrice upserted: ${mp.model_id}`) } console.log('\nSeeding complete!') console.log('Demo user: username=demo password=demo1234') console.log('Main user: username=lars password=scrum4me123') } main() .catch((e) => { console.error(e) process.exit(1) }) .finally(async () => { await prisma?.$disconnect() })