Add database initialization and schema
This commit is contained in:
156
server/db/init.js
Normal file
156
server/db/init.js
Normal file
@@ -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);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user