diff --git a/server/db/init.js b/server/db/init.js new file mode 100644 index 0000000..42bfcd6 --- /dev/null +++ b/server/db/init.js @@ -0,0 +1,156 @@ +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 - Core registry + 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 UNIQUE, + photo_urls TEXT, -- JSON array of photo URLs + notes TEXT, + is_active INTEGER DEFAULT 1, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP + ) + `); + + // Parents table - Relationship mapping + 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_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_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!'); + + 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'); + initDatabase(dbPath); +} \ No newline at end of file