import * as bcrypt from 'bcrypt'; import { PrismaClient } from '@prisma/client'; import { v4 as uuidv4 } from 'uuid'; import * as crypto from 'crypto'; const prisma = new PrismaClient(); async function main() { console.log('Seeding database...'); // Clean existing data so seed is idempotent await prisma.auditLog.deleteMany(); await prisma.tabEvent.deleteMany(); await prisma.tabReminder.deleteMany(); await prisma.plannedTab.deleteMany(); await prisma.remoteCommand.deleteMany(); await prisma.settlement.deleteMany(); await prisma.tab.deleteMany(); await prisma.stockAdjustment.deleteMany(); await prisma.product.deleteMany(); await prisma.productImage.deleteMany(); await prisma.refreshToken.deleteMany(); await prisma.registrationCode.deleteMany(); await prisma.device.deleteMany(); await prisma.clientUser.deleteMany(); await prisma.venue.deleteMany(); console.log('Cleared existing data'); // Create venue const venueId = uuidv4(); await prisma.venue.create({ data: { id: venueId, name: 'KoolTap Demo Bar' }, }); console.log(`Created venue: KoolTap Demo Bar (${venueId})`); // Create admin user const adminHash = await bcrypt.hash('Admin123!', 10); await prisma.clientUser.create({ data: { id: uuidv4(), venueId, email: 'admin@example.com', passwordHash: adminHash, name: 'Eigenaar', role: 'admin', }, }); console.log('Created admin user: admin@example.com / Admin123!'); // Create viewer user const viewerHash = await bcrypt.hash('Viewer123!', 10); await prisma.clientUser.create({ data: { id: uuidv4(), venueId, email: 'viewer@example.com', passwordHash: viewerHash, name: 'Bartender', role: 'manager', }, }); console.log('Created viewer user: viewer@example.com / Viewer123!'); // Create one device const deviceId = uuidv4(); const token = 'dev_' + crypto.randomBytes(48).toString('base64url'); await prisma.device.create({ data: { id: deviceId, venueId, name: 'Bar terminal 1', tokenHash: crypto.createHash('sha256').update(token).digest('hex'), tokenPrefix: token.substring(0, 8), tokenExpiresAt: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000), lastSeenAt: new Date(Date.now() - 1 * 60 * 1000), }, }); console.log(`Created device: Bar terminal 1 (${deviceId})`); const tabsData = [ { tabId: uuidv4(), customerName: 'Jan', version: 5, totalInCents: 2450, itemCount: 3, reportedAt: new Date(Date.now() - 2 * 60 * 1000) }, { tabId: uuidv4(), customerName: 'Marie', version: 3, totalInCents: 1800, itemCount: 2, reportedAt: new Date(Date.now() - 5 * 60 * 1000) }, { tabId: uuidv4(), customerName: 'Thomas', version: 7, totalInCents: 4200, itemCount: 5, reportedAt: new Date(Date.now() - 3 * 60 * 1000) }, { tabId: uuidv4(), customerName: 'Lisa', version: 2, totalInCents: 900, itemCount: 1, reportedAt: new Date(Date.now() - 4 * 60 * 1000) }, { tabId: uuidv4(), customerName: 'Emma', version: 4, totalInCents: 3200, itemCount: 4, reportedAt: new Date(Date.now() - 16 * 60 * 1000) }, { tabId: uuidv4(), customerName: 'David', version: 1, totalInCents: 1500, itemCount: 2, reportedAt: new Date(Date.now() - 20 * 60 * 1000) }, ]; for (const tab of tabsData) { // One line with itemCount units, priced so the tab adds up to about totalInCents const lineId = uuidv4(); const addedAt = tab.reportedAt.toISOString(); await prisma.tab.create({ data: { venueId, id: tab.tabId, customerName: tab.customerName, openedAt: tab.reportedAt, version: tab.version, lines: [{ id: lineId, productId: null, productName: 'Pils', unitPriceInCents: Math.round(tab.totalInCents / tab.itemCount), units: Array.from({ length: tab.itemCount }, (_, i) => ({ id: `${lineId}:${i}`, addedAt, removedAt: null })), }], }, }); } console.log('Created 6 open tabs'); // Create sample commands const now = new Date(); // Completed command await prisma.remoteCommand.create({ data: { id: uuidv4(), venueId, deviceId, targetTabId: tabsData[1].tabId, type: 'close_tab', expectedVersion: 3, paymentMethod: 'cash', requestedBy: 'Eigenaar', status: 'completed', result: 'Tab closed successfully', acknowledgedAt: new Date(now.getTime() - 30 * 60 * 1000), createdAt: new Date(now.getTime() - 35 * 60 * 1000), expiresAt: new Date(now.getTime() + 23 * 60 * 60 * 1000), }, }); // Pending command await prisma.remoteCommand.create({ data: { id: uuidv4(), venueId, deviceId, targetTabId: tabsData[0].tabId, type: 'close_tab', expectedVersion: 5, paymentMethod: 'payconiq', requestedBy: 'Eigenaar', status: 'pending', createdAt: new Date(now.getTime() - 5 * 60 * 1000), expiresAt: new Date(now.getTime() + 23 * 60 * 60 * 1000), }, }); // Failed command await prisma.remoteCommand.create({ data: { id: uuidv4(), venueId, deviceId, targetTabId: tabsData[4].tabId, type: 'close_tab', expectedVersion: 4, paymentMethod: 'payment_pending', requestedBy: 'Bartender', status: 'failed', result: 'Tab could not be closed', acknowledgedAt: new Date(now.getTime() - 60 * 60 * 1000), createdAt: new Date(now.getTime() - 65 * 60 * 1000), expiresAt: new Date(now.getTime() + 23 * 60 * 60 * 1000), }, }); // Conflict command await prisma.remoteCommand.create({ data: { id: uuidv4(), venueId, deviceId, targetTabId: tabsData[2].tabId, type: 'close_tab', expectedVersion: 7, paymentMethod: 'cash', requestedBy: 'Eigenaar', status: 'conflict', result: 'Version mismatch: expected 7, got 8', acknowledgedAt: new Date(now.getTime() - 2 * 60 * 60 * 1000), createdAt: new Date(now.getTime() - 2 * 60 * 60 * 1000 - 5 * 60 * 1000), expiresAt: new Date(now.getTime() + 22 * 60 * 60 * 1000), }, }); console.log('Created sample commands: completed, pending, failed, conflict'); // Create audit log entries await prisma.auditLog.create({ data: { id: uuidv4(), venueId, actorType: 'client_user', actorId: 'admin', action: 'command_created', details: JSON.stringify({ commandId: 'sample', tabId: tabsData[0].tabId }), }, }); console.log('Seeding complete!'); console.log(''); console.log('Demo credentials:'); console.log(' Admin: admin@example.com / Admin123!'); console.log(' Viewer: viewer@example.com / Viewer123!'); console.log(`\nDevice token (for simulator): ${token}`); } main() .catch(console.error) .finally(() => prisma.$disconnect());