const Database = require('better-sqlite3'); const path = require('path'); const fs = require('fs'); function initDatabase(dbPath) { // Ensure data directory exists const dir = path.dirname(dbPath); if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } const db = new Database(dbPath); // Enable foreign keys db.pragma('foreign_keys = ON'); console.log('Initializing database schema...'); // Dogs table - NO sire/dam columns, only litter_id db.exec(` CREATE TABLE IF NOT EXISTS dogs ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, registration_number TEXT UNIQUE, breed TEXT NOT NULL, sex TEXT NOT NULL CHECK(sex IN ('male', 'female')), birth_date DATE, color TEXT, microchip TEXT, photo_urls TEXT, -- JSON array of photo URLs notes TEXT, litter_id INTEGER, is_active INTEGER DEFAULT 1, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (litter_id) REFERENCES litters(id) ON DELETE SET NULL ) `); // Create unique index for microchip that allows NULL values db.exec(` CREATE UNIQUE INDEX IF NOT EXISTS idx_dogs_microchip ON dogs(microchip) WHERE microchip IS NOT NULL `); // Parents table - Stores sire/dam relationships db.exec(` CREATE TABLE IF NOT EXISTS parents ( id INTEGER PRIMARY KEY AUTOINCREMENT, dog_id INTEGER NOT NULL, parent_id INTEGER NOT NULL, parent_type TEXT NOT NULL CHECK(parent_type IN ('sire', 'dam')), FOREIGN KEY (dog_id) REFERENCES dogs(id) ON DELETE CASCADE, FOREIGN KEY (parent_id) REFERENCES dogs(id) ON DELETE CASCADE, UNIQUE(dog_id, parent_type) ) `); // Litters table - Breeding records db.exec(` CREATE TABLE IF NOT EXISTS litters ( id INTEGER PRIMARY KEY AUTOINCREMENT, sire_id INTEGER NOT NULL, dam_id INTEGER NOT NULL, breeding_date DATE NOT NULL, whelping_date DATE, puppy_count INTEGER DEFAULT 0, notes TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (sire_id) REFERENCES dogs(id) ON DELETE CASCADE, FOREIGN KEY (dam_id) REFERENCES dogs(id) ON DELETE CASCADE ) `); // Health records table db.exec(` CREATE TABLE IF NOT EXISTS health_records ( id INTEGER PRIMARY KEY AUTOINCREMENT, dog_id INTEGER NOT NULL, record_type TEXT NOT NULL CHECK(record_type IN ('test', 'vaccination', 'exam', 'treatment', 'certification')), test_name TEXT, test_date DATE NOT NULL, result TEXT, document_url TEXT, notes TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (dog_id) REFERENCES dogs(id) ON DELETE CASCADE ) `); // Heat cycles table db.exec(` CREATE TABLE IF NOT EXISTS heat_cycles ( id INTEGER PRIMARY KEY AUTOINCREMENT, dog_id INTEGER NOT NULL, start_date DATE NOT NULL, end_date DATE, progesterone_peak_date DATE, breeding_date DATE, breeding_successful INTEGER DEFAULT 0, notes TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (dog_id) REFERENCES dogs(id) ON DELETE CASCADE ) `); // Traits table - Genetic trait tracking db.exec(` CREATE TABLE IF NOT EXISTS traits ( id INTEGER PRIMARY KEY AUTOINCREMENT, dog_id INTEGER NOT NULL, trait_category TEXT NOT NULL, trait_name TEXT NOT NULL, trait_value TEXT NOT NULL, inherited_from INTEGER, notes TEXT, FOREIGN KEY (dog_id) REFERENCES dogs(id) ON DELETE CASCADE, FOREIGN KEY (inherited_from) REFERENCES dogs(id) ON DELETE SET NULL ) `); // Create indexes for performance db.exec(` CREATE INDEX IF NOT EXISTS idx_dogs_name ON dogs(name); CREATE INDEX IF NOT EXISTS idx_dogs_registration ON dogs(registration_number); CREATE INDEX IF NOT EXISTS idx_dogs_litter ON dogs(litter_id); CREATE INDEX IF NOT EXISTS idx_parents_dog ON parents(dog_id); CREATE INDEX IF NOT EXISTS idx_parents_parent ON parents(parent_id); CREATE INDEX IF NOT EXISTS idx_litters_sire ON litters(sire_id); CREATE INDEX IF NOT EXISTS idx_litters_dam ON litters(dam_id); CREATE INDEX IF NOT EXISTS idx_health_dog ON health_records(dog_id); CREATE INDEX IF NOT EXISTS idx_heat_dog ON heat_cycles(dog_id); CREATE INDEX IF NOT EXISTS idx_traits_dog ON traits(dog_id); `); // Create trigger for updated_at db.exec(` CREATE TRIGGER IF NOT EXISTS update_dogs_timestamp AFTER UPDATE ON dogs FOR EACH ROW BEGIN UPDATE dogs SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id; END; `); console.log('✓ Database schema initialized successfully!'); console.log('✓ Dogs table: NO sire/dam columns, uses parents table'); console.log('✓ Parents table: Stores sire/dam relationships'); console.log('✓ Litters table: Links puppies via litter_id'); db.close(); return true; } function getDatabase() { const dbPath = process.env.DB_PATH || path.join(__dirname, '../../data/breedr.db'); const db = new Database(dbPath); db.pragma('foreign_keys = ON'); return db; } module.exports = { initDatabase, getDatabase }; // Run initialization if called directly if (require.main === module) { const dbPath = process.env.DB_PATH || path.join(__dirname, '../../data/breedr.db'); console.log('\n=========================================='); console.log('BREEDR Database Initialization'); console.log('=========================================='); console.log(`Database: ${dbPath}`); console.log('==========================================\n'); initDatabase(dbPath); console.log('\n✓ Database ready!\n'); }