const { query, toISODate, closePool, dbReady } = require('./db'); const Cycle = require('../models/Cycle'); const Camera = require('../models/Camera'); const ApiKey = require('../models/ApiKey'); const crypto = require('crypto'); // Helper function to hash API keys function hashApiKey(key) { return crypto.createHash('sha256').update(key).digest('hex'); } // Helper function to generate API key function generateApiKey() { const randomBytes = crypto.randomBytes(24); return 'cpa_' + randomBytes.toString('hex'); } // Seed data - minimal cycles with no assumed data const cycles = [ { id: 'CYCLE-JBW-2025-05-20', totalDays: 42, currentDay: 42, startDate: new Date('2025-05-20'), status: 'Completed', docInCount: null, chickInWeight: null, }, { id: 'CYCLE-JBW-2025-07-11', totalDays: 42, currentDay: 42, startDate: new Date('2025-07-11'), status: 'Completed', docInCount: null, chickInWeight: null, }, { id: 'CYCLE-JBW-2025-10-22', totalDays: 42, currentDay: 42, startDate: new Date('2025-10-22'), status: 'Completed', docInCount: null, chickInWeight: null, }, { id: 'CYCLE-JBW-2025-12-10', totalDays: 42, currentDay: 42, startDate: new Date('2025-12-10'), status: 'Completed', docInCount: null, chickInWeight: null, }, { id: 'CYCLE-JBW-2026-02-09', totalDays: 42, currentDay: 2, startDate: new Date('2026-02-09'), status: 'Active', docInCount: 20000, chickInWeight: null, }, ]; // Location hierarchy seed data const locations = [ { id: 'loc1', name: 'Peternakan Pusat, Jawa Barat', address: 'Jl. Peternakan No. 123, Bandung, Jawa Barat', }, ]; const coops = [ { id: 'coop1', name: 'Kandang A (Broiler)', locationId: 'loc1', capacity: 15000, }, { id: 'coop2', name: 'Kandang B (Layer)', locationId: 'loc1', capacity: 12000, }, ]; const floors = [ { id: 'floor1', name: 'Lantai 1', coopId: 'coop1', floorNumber: 1, }, { id: 'floor2', name: 'Lantai 2', coopId: 'coop1', floorNumber: 2, }, { id: 'floor3', name: 'Lantai 1', coopId: 'coop2', floorNumber: 1, }, ]; const cameras = [ { id: 'cam1', name: 'Kamera Sudut Kanan Zona A', cameraType: 'STANDARD', floorId: 'floor1', model: 'HIK-2MP-IR', status: 'online', }, { id: 'cam2', name: 'Kamera Sudut Kiri Zona A', cameraType: 'STANDARD', floorId: 'floor1', model: 'HIK-2MP-IR', status: 'online', }, { id: 'cam3', name: 'Kamera Tengah Zona B', cameraType: 'INFRARED', floorId: 'floor2', model: 'HIK-4MP-IR', status: 'online', }, { id: 'cam4', name: 'Kamera Pintu Masuk', cameraType: 'STANDARD', floorId: 'floor2', model: 'HIK-2MP', status: 'online', }, { id: 'cam5', name: 'Kamera Zona Layer 1', cameraType: 'STANDARD', floorId: 'floor3', model: 'HIK-2MP-IR', status: 'online', }, ]; async function seedDatabase() { try { // Wait for schema to be initialized before querying await dbReady; // Check if cycles already exist const existingCycles = await query('SELECT COUNT(*) as count FROM cycles', []); const cycleCount = parseInt(existingCycles.rows[0].count); if (cycleCount > 0) { return; } // Insert locations (skip if already exist) for (const loc of locations) { await query( 'INSERT INTO locations (id, name, address) VALUES ($1, $2, $3) ON CONFLICT (id) DO NOTHING', [loc.id, loc.name, loc.address || null] ); } // Insert coops (skip if already exist) for (const coop of coops) { await query( 'INSERT INTO coops (id, name, location_id, capacity) VALUES ($1, $2, $3, $4) ON CONFLICT (id) DO NOTHING', [coop.id, coop.name, coop.locationId, coop.capacity || null] ); } // Insert floors (skip if already exist) for (const floor of floors) { await query( 'INSERT INTO floors (id, name, coop_id, floor_number) VALUES ($1, $2, $3, $4) ON CONFLICT (id) DO NOTHING', [floor.id, floor.name, floor.coopId, floor.floorNumber || null] ); } for (const cam of cameras) { await Camera.create({ id: cam.id, name: cam.name, cameraType: cam.cameraType, floorId: cam.floorId || null, model: cam.model || null, status: cam.status, }); } for (const cycle of cycles) { const endDate = new Date(cycle.startDate); endDate.setDate(endDate.getDate() + cycle.totalDays - 1); await Cycle.create({ id: cycle.id, totalDays: cycle.totalDays, currentDay: cycle.currentDay, startDate: cycle.startDate, endDate: endDate, chickInWeight: cycle.chickInWeight, docInCount: cycle.docInCount, status: cycle.status, }); } // Display seeded cycles const seededCycles = await Cycle.getAll(); seededCycles.forEach((c) => {}); const devKey = generateApiKey(); const keyHash = hashApiKey(devKey); const keyId = 'apk_dev_' + Date.now(); await query( `INSERT INTO api_keys (id, key_hash, name, description, created_by, is_active) VALUES ($1, $2, $3, $4, $5, $6)`, [ keyId, keyHash, 'Development Key', 'Auto-generated key for testing and development', 'seed-script', 1, ] ); } catch (error) { console.error('✗ Error seeding database:', error.message); console.error(error.stack); throw error; } } // Run seed if this file is executed directly if (require.main === module) { seedDatabase() .then(() => { return closePool(); }) .then(() => { process.exit(0); }) .catch((error) => { console.error('Fatal error:', error); closePool().then(() => { process.exit(1); }); }); } module.exports = { seedDatabase };