2026-03-09 22:23:41 -05:00
|
|
|
const Database = require('better-sqlite3');
|
|
|
|
|
const path = require('path');
|
|
|
|
|
const fs = require('fs');
|
|
|
|
|
|
|
|
|
|
const dbPath = path.join(__dirname, '../../data');
|
|
|
|
|
const db = new Database(path.join(dbPath, 'breedr.db'));
|
|
|
|
|
|
|
|
|
|
function getDatabase() {
|
|
|
|
|
return db;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function initDatabase() {
|
|
|
|
|
db.pragma('foreign_keys = ON');
|
|
|
|
|
db.pragma('journal_mode = WAL');
|
|
|
|
|
|
|
|
|
|
// ── Dogs ────────────────────────────────────────────────────────────
|
|
|
|
|
db.exec(`
|
|
|
|
|
CREATE TABLE IF NOT EXISTS dogs (
|
|
|
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
|
|
name TEXT NOT NULL,
|
|
|
|
|
registration_number TEXT,
|
|
|
|
|
breed TEXT NOT NULL,
|
|
|
|
|
sex TEXT NOT NULL CHECK(sex IN ('male', 'female')),
|
|
|
|
|
birth_date TEXT,
|
|
|
|
|
color TEXT,
|
|
|
|
|
microchip TEXT,
|
|
|
|
|
litter_id INTEGER,
|
|
|
|
|
is_active INTEGER DEFAULT 1,
|
|
|
|
|
is_champion INTEGER DEFAULT 0,
|
|
|
|
|
photo_urls TEXT DEFAULT '[]',
|
|
|
|
|
notes TEXT,
|
|
|
|
|
created_at TEXT DEFAULT (datetime('now')),
|
|
|
|
|
updated_at TEXT DEFAULT (datetime('now'))
|
|
|
|
|
)
|
|
|
|
|
`);
|
|
|
|
|
|
|
|
|
|
// migrate: add is_champion if missing (safe on existing DBs)
|
|
|
|
|
try {
|
|
|
|
|
db.exec(`ALTER TABLE dogs ADD COLUMN is_champion INTEGER DEFAULT 0`);
|
|
|
|
|
} catch (_) { /* column already exists */ }
|
|
|
|
|
|
|
|
|
|
// ── Parents ─────────────────────────────────────────────────────────
|
|
|
|
|
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),
|
|
|
|
|
FOREIGN KEY (parent_id) REFERENCES dogs(id)
|
|
|
|
|
)
|
|
|
|
|
`);
|
|
|
|
|
|
|
|
|
|
// ── Breeding Records ────────────────────────────────────────────────
|
|
|
|
|
db.exec(`
|
|
|
|
|
CREATE TABLE IF NOT EXISTS breeding_records (
|
|
|
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
|
|
sire_id INTEGER NOT NULL,
|
|
|
|
|
dam_id INTEGER NOT NULL,
|
|
|
|
|
breeding_date TEXT,
|
|
|
|
|
due_date TEXT,
|
|
|
|
|
conception_method TEXT CHECK(conception_method IN ('natural', 'ai', 'frozen', 'surgical')),
|
|
|
|
|
notes TEXT,
|
|
|
|
|
created_at TEXT DEFAULT (datetime('now')),
|
|
|
|
|
updated_at TEXT DEFAULT (datetime('now')),
|
|
|
|
|
FOREIGN KEY (sire_id) REFERENCES dogs(id),
|
|
|
|
|
FOREIGN KEY (dam_id) REFERENCES dogs(id)
|
|
|
|
|
)
|
|
|
|
|
`);
|
|
|
|
|
|
|
|
|
|
// ── Litters ─────────────────────────────────────────────────────────
|
|
|
|
|
db.exec(`
|
|
|
|
|
CREATE TABLE IF NOT EXISTS litters (
|
|
|
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
|
|
breeding_id INTEGER,
|
|
|
|
|
sire_id INTEGER NOT NULL,
|
|
|
|
|
dam_id INTEGER NOT NULL,
|
|
|
|
|
whelp_date TEXT,
|
|
|
|
|
total_count INTEGER DEFAULT 0,
|
|
|
|
|
male_count INTEGER DEFAULT 0,
|
|
|
|
|
female_count INTEGER DEFAULT 0,
|
|
|
|
|
stillborn_count INTEGER DEFAULT 0,
|
|
|
|
|
notes TEXT,
|
|
|
|
|
created_at TEXT DEFAULT (datetime('now')),
|
|
|
|
|
updated_at TEXT DEFAULT (datetime('now')),
|
|
|
|
|
FOREIGN KEY (breeding_id) REFERENCES breeding_records(id),
|
|
|
|
|
FOREIGN KEY (sire_id) REFERENCES dogs(id),
|
|
|
|
|
FOREIGN KEY (dam_id) REFERENCES dogs(id)
|
|
|
|
|
)
|
|
|
|
|
`);
|
|
|
|
|
|
|
|
|
|
// ── Health Records ──────────────────────────────────────────────────
|
|
|
|
|
db.exec(`
|
|
|
|
|
CREATE TABLE IF NOT EXISTS health_records (
|
|
|
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
|
|
dog_id INTEGER NOT NULL,
|
|
|
|
|
record_type TEXT NOT NULL,
|
|
|
|
|
date TEXT NOT NULL,
|
|
|
|
|
title TEXT NOT NULL,
|
|
|
|
|
description TEXT,
|
|
|
|
|
vet_name TEXT,
|
|
|
|
|
notes TEXT,
|
|
|
|
|
result TEXT,
|
|
|
|
|
next_due TEXT,
|
|
|
|
|
created_at TEXT DEFAULT (datetime('now')),
|
|
|
|
|
updated_at TEXT DEFAULT (datetime('now')),
|
|
|
|
|
FOREIGN KEY (dog_id) REFERENCES dogs(id)
|
|
|
|
|
)
|
|
|
|
|
`);
|
|
|
|
|
|
|
|
|
|
// ── Settings ─────────────────────────────────────────────────────────
|
|
|
|
|
db.exec(`
|
|
|
|
|
CREATE TABLE IF NOT EXISTS settings (
|
|
|
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
|
|
|
kennel_name TEXT DEFAULT 'BREEDR',
|
|
|
|
|
kennel_tagline TEXT,
|
|
|
|
|
kennel_address TEXT,
|
|
|
|
|
kennel_phone TEXT,
|
|
|
|
|
kennel_email TEXT,
|
|
|
|
|
kennel_website TEXT,
|
|
|
|
|
kennel_akc_id TEXT,
|
|
|
|
|
kennel_breed TEXT,
|
|
|
|
|
owner_name TEXT,
|
|
|
|
|
created_at TEXT DEFAULT (datetime('now')),
|
|
|
|
|
updated_at TEXT DEFAULT (datetime('now'))
|
|
|
|
|
)
|
|
|
|
|
`);
|
|
|
|
|
|
|
|
|
|
// migrate: add new kennel columns if missing (safe on existing DBs)
|
|
|
|
|
const kennelCols = [
|
|
|
|
|
['kennel_name', "TEXT DEFAULT 'BREEDR'"],
|
|
|
|
|
['kennel_tagline', 'TEXT'],
|
|
|
|
|
['kennel_address', 'TEXT'],
|
|
|
|
|
['kennel_phone', 'TEXT'],
|
|
|
|
|
['kennel_email', 'TEXT'],
|
|
|
|
|
['kennel_website', 'TEXT'],
|
|
|
|
|
['kennel_akc_id', 'TEXT'],
|
|
|
|
|
['kennel_breed', 'TEXT'],
|
|
|
|
|
['owner_name', 'TEXT'],
|
|
|
|
|
];
|
|
|
|
|
for (const [col, def] of kennelCols) {
|
|
|
|
|
try {
|
|
|
|
|
db.exec(`ALTER TABLE settings ADD COLUMN ${col} ${def}`);
|
|
|
|
|
} catch (_) { /* already exists */ }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Seed a default settings row if none exists
|
|
|
|
|
const existing = db.prepare('SELECT id FROM settings LIMIT 1').get();
|
|
|
|
|
if (!existing) {
|
|
|
|
|
db.prepare(`INSERT INTO settings (kennel_name) VALUES (?)`).run('BREEDR');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('✓ Database initialized successfully');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
module.exports = { getDatabase, initDatabase };
|