Files
breedr/server/db/init.js

163 lines
5.0 KiB
JavaScript

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,
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
)
`);
// 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 - 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);
}