Merge pull request 'feature/enhanced-litters-and-pedigree' (#15) from feature/enhanced-litters-and-pedigree into master

Reviewed-on: #15
This commit was merged in pull request #15.
This commit is contained in:
2026-03-09 02:07:37 -05:00
4 changed files with 331 additions and 130 deletions

222
DATABASE.md Normal file
View File

@@ -0,0 +1,222 @@
# BREEDR Database Schema
## Overview
This document describes the clean database schema for BREEDR. **NO migrations** - fresh installs create the correct schema automatically.
## Schema Design
### Core Principle: Parents Table Approach
The `dogs` table **does NOT have sire/dam columns**. Parent relationships are stored in the separate `parents` table. This design:
- Keeps the schema clean and normalized
- Allows flexible parent relationships
- Supports future extensions (multiple sires, surrogates, etc.)
## Tables
### dogs
Core registry for all dogs.
```sql
CREATE TABLE 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
notes TEXT,
litter_id INTEGER, -- Links to litters table
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
);
```
**Important:** NO `sire_id` or `dam_id` columns!
### parents
Stores sire/dam relationships.
```sql
CREATE TABLE parents (
id INTEGER PRIMARY KEY AUTOINCREMENT,
dog_id INTEGER NOT NULL, -- The puppy
parent_id INTEGER NOT NULL, -- The parent
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) -- One sire, one dam per dog
);
```
### litters
Breeding records and litter tracking.
```sql
CREATE TABLE 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
Health tests, vaccinations, exams, treatments.
```sql
CREATE TABLE 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
Female heat cycle tracking for breeding timing.
```sql
CREATE TABLE 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
Genetic trait tracking and inheritance.
```sql
CREATE TABLE 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, -- Parent dog ID
notes TEXT,
FOREIGN KEY (dog_id) REFERENCES dogs(id) ON DELETE CASCADE,
FOREIGN KEY (inherited_from) REFERENCES dogs(id) ON DELETE SET NULL
);
```
## API Usage
### Creating a Dog with Parents
```javascript
POST /api/dogs
{
"name": "Puppy Name",
"breed": "Breed Name",
"sex": "male",
"sire_id": 5, // Parent male dog ID
"dam_id": 8, // Parent female dog ID
"litter_id": 2 // Optional: link to litter
}
```
The API route automatically:
1. Inserts the dog into `dogs` table (without sire/dam columns)
2. Creates entries in `parents` table linking to sire and dam
### Querying Parents
```sql
-- Get a dog's parents
SELECT p.parent_type, d.*
FROM parents p
JOIN dogs d ON p.parent_id = d.id
WHERE p.dog_id = ?;
-- Get a dog's offspring
SELECT d.*
FROM dogs d
JOIN parents p ON d.id = p.dog_id
WHERE p.parent_id = ?;
```
## Fresh Install
For a fresh install:
1. **Delete the old database** (if upgrading):
```bash
rm data/breedr.db
```
2. **Start the server** - it will create the correct schema automatically:
```bash
npm run dev
```
3. **Verify the schema**:
```bash
sqlite3 data/breedr.db ".schema dogs"
```
You should see `litter_id` but **NO** `sire_id` or `dam_id` columns.
## Troubleshooting
### "no such column: weight" or "no such column: sire_id"
**Solution:** Your database has an old schema. Delete it and let the app recreate it:
```bash
rm data/breedr.db
npm run dev
```
### Parent relationships not saving
Check server logs. You should see:
```
✓ Dog inserted with ID: 123
Adding sire relationship: dog 123 -> sire 5
✓ Sire relationship added
Adding dam relationship: dog 123 -> dam 8
✓ Dam relationship added
```
If relationships aren't being created, check that `sire_id` and `dam_id` are being sent in the API request.
## Database Files
- `server/db/init.js` - Creates clean schema, no migrations
- `server/routes/dogs.js` - Handles parent relationships via `parents` table
- `server/index.js` - Initializes database on startup
**NO MIGRATIONS!** The init file is the source of truth.

View File

@@ -16,7 +16,7 @@ function initDatabase(dbPath) {
console.log('Initializing database schema...'); console.log('Initializing database schema...');
// Dogs table - Core registry // Dogs table - NO sire/dam columns, only litter_id
db.exec(` db.exec(`
CREATE TABLE IF NOT EXISTS dogs ( CREATE TABLE IF NOT EXISTS dogs (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -29,9 +29,11 @@ function initDatabase(dbPath) {
microchip TEXT, microchip TEXT,
photo_urls TEXT, -- JSON array of photo URLs photo_urls TEXT, -- JSON array of photo URLs
notes TEXT, notes TEXT,
litter_id INTEGER,
is_active INTEGER DEFAULT 1, is_active INTEGER DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP, created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (litter_id) REFERENCES litters(id) ON DELETE SET NULL
) )
`); `);
@@ -42,7 +44,7 @@ function initDatabase(dbPath) {
WHERE microchip IS NOT NULL WHERE microchip IS NOT NULL
`); `);
// Parents table - Relationship mapping // Parents table - Stores sire/dam relationships
db.exec(` db.exec(`
CREATE TABLE IF NOT EXISTS parents ( CREATE TABLE IF NOT EXISTS parents (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -51,7 +53,7 @@ function initDatabase(dbPath) {
parent_type TEXT NOT NULL CHECK(parent_type IN ('sire', 'dam')), parent_type TEXT NOT NULL CHECK(parent_type IN ('sire', 'dam')),
FOREIGN KEY (dog_id) REFERENCES dogs(id) ON DELETE CASCADE, FOREIGN KEY (dog_id) REFERENCES dogs(id) ON DELETE CASCADE,
FOREIGN KEY (parent_id) REFERENCES dogs(id) ON DELETE CASCADE, FOREIGN KEY (parent_id) REFERENCES dogs(id) ON DELETE CASCADE,
UNIQUE(dog_id, parent_id, parent_type) UNIQUE(dog_id, parent_type)
) )
`); `);
@@ -122,6 +124,7 @@ function initDatabase(dbPath) {
db.exec(` db.exec(`
CREATE INDEX IF NOT EXISTS idx_dogs_name ON dogs(name); 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_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_dog ON parents(dog_id);
CREATE INDEX IF NOT EXISTS idx_parents_parent ON parents(parent_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_sire ON litters(sire_id);
@@ -141,7 +144,10 @@ function initDatabase(dbPath) {
END; END;
`); `);
console.log('Database schema initialized successfully!'); 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(); db.close();
return true; return true;
@@ -159,5 +165,11 @@ module.exports = { initDatabase, getDatabase };
// Run initialization if called directly // Run initialization if called directly
if (require.main === module) { if (require.main === module) {
const dbPath = process.env.DB_PATH || path.join(__dirname, '../../data/breedr.db'); 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); initDatabase(dbPath);
} console.log('\n✓ Database ready!\n');
}

View File

@@ -4,7 +4,6 @@ const helmet = require('helmet');
const path = require('path'); const path = require('path');
const fs = require('fs'); const fs = require('fs');
const { initDatabase } = require('./db/init'); const { initDatabase } = require('./db/init');
const { runMigrations } = require('./db/migrations');
const app = express(); const app = express();
const PORT = process.env.PORT || 3000; const PORT = process.env.PORT || 3000;
@@ -21,20 +20,9 @@ if (!fs.existsSync(UPLOAD_PATH)) {
} }
// Initialize database schema (creates tables if they don't exist) // Initialize database schema (creates tables if they don't exist)
console.log('Initializing database...');
initDatabase(DB_PATH); initDatabase(DB_PATH);
console.log('✓ Database ready!\n');
// Run migrations to ensure schema is up-to-date
try {
console.log('Running database migrations...');
runMigrations(DB_PATH);
console.log('Database migrations complete!\n');
} catch (error) {
console.error('\n⚠ Database migration failed!');
console.error('Error:', error.message);
console.error('\nThe application may not function correctly.');
console.error('Please check the database and try again.\n');
// Don't exit - let the app try to start anyway
}
// Middleware // Middleware
app.use(helmet({ app.use(helmet({
@@ -81,13 +69,13 @@ app.use((err, req, res, next) => {
// Start server // Start server
app.listen(PORT, '0.0.0.0', () => { app.listen(PORT, '0.0.0.0', () => {
console.log(`\n🐕 BREEDR Server Running`); console.log(`\n🐕 BREEDR Server Running`);
console.log(`=============================`); console.log(`==============================`);
console.log(`Environment: ${process.env.NODE_ENV || 'development'}`); console.log(`Environment: ${process.env.NODE_ENV || 'development'}`);
console.log(`Port: ${PORT}`); console.log(`Port: ${PORT}`);
console.log(`Database: ${DB_PATH}`); console.log(`Database: ${DB_PATH}`);
console.log(`Uploads: ${UPLOAD_PATH}`); console.log(`Uploads: ${UPLOAD_PATH}`);
console.log(`Access: http://localhost:${PORT}`); console.log(`Access: http://localhost:${PORT}`);
console.log(`=============================\n`); console.log(`==============================\n`);
}); });
module.exports = app; module.exports = app;

View File

@@ -41,10 +41,9 @@ const emptyToNull = (value) => {
router.get('/', (req, res) => { router.get('/', (req, res) => {
try { try {
const db = getDatabase(); const db = getDatabase();
// Select only fields that exist in the schema (no weight/height)
const dogs = db.prepare(` const dogs = db.prepare(`
SELECT id, name, registration_number, breed, sex, birth_date, SELECT id, name, registration_number, breed, sex, birth_date,
color, microchip, photo_urls, notes, is_active, color, microchip, photo_urls, notes, litter_id, is_active,
created_at, updated_at created_at, updated_at
FROM dogs FROM dogs
WHERE is_active = 1 WHERE is_active = 1
@@ -58,18 +57,18 @@ router.get('/', (req, res) => {
res.json(dogs); res.json(dogs);
} catch (error) { } catch (error) {
console.error('Error fetching dogs:', error);
res.status(500).json({ error: error.message }); res.status(500).json({ error: error.message });
} }
}); });
// GET single dog by ID // GET single dog by ID with parents and offspring
router.get('/:id', (req, res) => { router.get('/:id', (req, res) => {
try { try {
const db = getDatabase(); const db = getDatabase();
// Select only fields that exist in the schema (no weight/height)
const dog = db.prepare(` const dog = db.prepare(`
SELECT id, name, registration_number, breed, sex, birth_date, SELECT id, name, registration_number, breed, sex, birth_date,
color, microchip, photo_urls, notes, is_active, color, microchip, photo_urls, notes, litter_id, is_active,
created_at, updated_at created_at, updated_at
FROM dogs FROM dogs
WHERE id = ? WHERE id = ?
@@ -101,6 +100,7 @@ router.get('/:id', (req, res) => {
res.json(dog); res.json(dog);
} catch (error) { } catch (error) {
console.error('Error fetching dog:', error);
res.status(500).json({ error: error.message }); res.status(500).json({ error: error.message });
} }
}); });
@@ -110,77 +110,64 @@ router.post('/', (req, res) => {
try { try {
const { name, registration_number, breed, sex, birth_date, color, microchip, notes, sire_id, dam_id, litter_id } = req.body; const { name, registration_number, breed, sex, birth_date, color, microchip, notes, sire_id, dam_id, litter_id } = req.body;
console.log('Creating dog with data:', { name, breed, sex, sire_id, dam_id, litter_id });
if (!name || !breed || !sex) { if (!name || !breed || !sex) {
return res.status(400).json({ error: 'Name, breed, and sex are required' }); return res.status(400).json({ error: 'Name, breed, and sex are required' });
} }
const db = getDatabase(); const db = getDatabase();
// Check if litter_id column exists // Insert dog (dogs table has NO sire/dam columns)
let hasLitterId = false; const result = db.prepare(`
try { INSERT INTO dogs (name, registration_number, breed, sex, birth_date, color, microchip, notes, litter_id, photo_urls)
const columns = db.prepare("PRAGMA table_info(dogs)").all(); VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
hasLitterId = columns.some(col => col.name === 'litter_id'); `).run(
} catch (e) { name,
console.error('Error checking schema:', e); emptyToNull(registration_number),
} breed,
sex,
// Insert with or without litter_id depending on schema emptyToNull(birth_date),
let result; emptyToNull(color),
if (hasLitterId) { emptyToNull(microchip),
result = db.prepare(` emptyToNull(notes),
INSERT INTO dogs (name, registration_number, breed, sex, birth_date, color, microchip, notes, litter_id, photo_urls) emptyToNull(litter_id),
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) '[]'
`).run( );
name,
emptyToNull(registration_number),
breed,
sex,
emptyToNull(birth_date),
emptyToNull(color),
emptyToNull(microchip),
emptyToNull(notes),
emptyToNull(litter_id),
'[]'
);
} else {
result = db.prepare(`
INSERT INTO dogs (name, registration_number, breed, sex, birth_date, color, microchip, notes, photo_urls)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
`).run(
name,
emptyToNull(registration_number),
breed,
sex,
emptyToNull(birth_date),
emptyToNull(color),
emptyToNull(microchip),
emptyToNull(notes),
'[]'
);
}
const dogId = result.lastInsertRowid; const dogId = result.lastInsertRowid;
console.log(`✓ Dog inserted with ID: ${dogId}`);
// Add parent relationships // Add sire relationship if provided
if (sire_id) { if (sire_id && sire_id !== '' && sire_id !== null) {
db.prepare('INSERT INTO parents (dog_id, parent_id, parent_type) VALUES (?, ?, "sire")').run(dogId, sire_id); console.log(` Adding sire relationship: dog ${dogId} -> sire ${sire_id}`);
} db.prepare('INSERT INTO parents (dog_id, parent_id, parent_type) VALUES (?, ?, ?)').
if (dam_id) { run(dogId, sire_id, 'sire');
db.prepare('INSERT INTO parents (dog_id, parent_id, parent_type) VALUES (?, ?, "dam")').run(dogId, dam_id); console.log(` ✓ Sire relationship added`);
} }
// Add dam relationship if provided
if (dam_id && dam_id !== '' && dam_id !== null) {
console.log(` Adding dam relationship: dog ${dogId} -> dam ${dam_id}`);
db.prepare('INSERT INTO parents (dog_id, parent_id, parent_type) VALUES (?, ?, ?)').
run(dogId, dam_id, 'dam');
console.log(` ✓ Dam relationship added`);
}
// Fetch the created dog
const dog = db.prepare(` const dog = db.prepare(`
SELECT id, name, registration_number, breed, sex, birth_date, SELECT id, name, registration_number, breed, sex, birth_date,
color, microchip, photo_urls, notes, is_active, color, microchip, photo_urls, notes, litter_id, is_active,
created_at, updated_at created_at, updated_at
FROM dogs FROM dogs
WHERE id = ? WHERE id = ?
`).get(dogId); `).get(dogId);
dog.photo_urls = []; dog.photo_urls = [];
console.log(`✓ Dog created successfully: ${dog.name} (ID: ${dogId})`);
res.status(201).json(dog); res.status(201).json(dog);
} catch (error) { } catch (error) {
console.error('Error creating dog:', error);
res.status(500).json({ error: error.message }); res.status(500).json({ error: error.message });
} }
}); });
@@ -190,76 +177,64 @@ router.put('/:id', (req, res) => {
try { try {
const { name, registration_number, breed, sex, birth_date, color, microchip, notes, sire_id, dam_id, litter_id } = req.body; const { name, registration_number, breed, sex, birth_date, color, microchip, notes, sire_id, dam_id, litter_id } = req.body;
console.log(`Updating dog ${req.params.id} with data:`, { name, breed, sex, sire_id, dam_id, litter_id });
const db = getDatabase(); const db = getDatabase();
// Check if litter_id column exists // Update dog record (dogs table has NO sire/dam columns)
let hasLitterId = false; db.prepare(`
try { UPDATE dogs
const columns = db.prepare("PRAGMA table_info(dogs)").all(); SET name = ?, registration_number = ?, breed = ?, sex = ?,
hasLitterId = columns.some(col => col.name === 'litter_id'); birth_date = ?, color = ?, microchip = ?, notes = ?, litter_id = ?
} catch (e) { WHERE id = ?
console.error('Error checking schema:', e); `).run(
} name,
emptyToNull(registration_number),
breed,
sex,
emptyToNull(birth_date),
emptyToNull(color),
emptyToNull(microchip),
emptyToNull(notes),
emptyToNull(litter_id),
req.params.id
);
console.log(` ✓ Dog record updated`);
// Update with or without litter_id // Remove existing parent relationships
if (hasLitterId) {
db.prepare(`
UPDATE dogs
SET name = ?, registration_number = ?, breed = ?, sex = ?,
birth_date = ?, color = ?, microchip = ?, notes = ?, litter_id = ?
WHERE id = ?
`).run(
name,
emptyToNull(registration_number),
breed,
sex,
emptyToNull(birth_date),
emptyToNull(color),
emptyToNull(microchip),
emptyToNull(notes),
emptyToNull(litter_id),
req.params.id
);
} else {
db.prepare(`
UPDATE dogs
SET name = ?, registration_number = ?, breed = ?, sex = ?,
birth_date = ?, color = ?, microchip = ?, notes = ?
WHERE id = ?
`).run(
name,
emptyToNull(registration_number),
breed,
sex,
emptyToNull(birth_date),
emptyToNull(color),
emptyToNull(microchip),
emptyToNull(notes),
req.params.id
);
}
// Update parent relationships
db.prepare('DELETE FROM parents WHERE dog_id = ?').run(req.params.id); db.prepare('DELETE FROM parents WHERE dog_id = ?').run(req.params.id);
console.log(` ✓ Old parent relationships removed`);
if (sire_id) { // Add new sire relationship if provided
db.prepare('INSERT INTO parents (dog_id, parent_id, parent_type) VALUES (?, ?, "sire")').run(req.params.id, sire_id); if (sire_id && sire_id !== '' && sire_id !== null) {
} console.log(` Adding sire relationship: dog ${req.params.id} -> sire ${sire_id}`);
if (dam_id) { db.prepare('INSERT INTO parents (dog_id, parent_id, parent_type) VALUES (?, ?, ?)').
db.prepare('INSERT INTO parents (dog_id, parent_id, parent_type) VALUES (?, ?, "dam")').run(req.params.id, dam_id); run(req.params.id, sire_id, 'sire');
console.log(` ✓ Sire relationship added`);
} }
// Add new dam relationship if provided
if (dam_id && dam_id !== '' && dam_id !== null) {
console.log(` Adding dam relationship: dog ${req.params.id} -> dam ${dam_id}`);
db.prepare('INSERT INTO parents (dog_id, parent_id, parent_type) VALUES (?, ?, ?)').
run(req.params.id, dam_id, 'dam');
console.log(` ✓ Dam relationship added`);
}
// Fetch updated dog
const dog = db.prepare(` const dog = db.prepare(`
SELECT id, name, registration_number, breed, sex, birth_date, SELECT id, name, registration_number, breed, sex, birth_date,
color, microchip, photo_urls, notes, is_active, color, microchip, photo_urls, notes, litter_id, is_active,
created_at, updated_at created_at, updated_at
FROM dogs FROM dogs
WHERE id = ? WHERE id = ?
`).get(req.params.id); `).get(req.params.id);
dog.photo_urls = dog.photo_urls ? JSON.parse(dog.photo_urls) : []; dog.photo_urls = dog.photo_urls ? JSON.parse(dog.photo_urls) : [];
console.log(`✓ Dog updated successfully: ${dog.name} (ID: ${req.params.id})`);
res.json(dog); res.json(dog);
} catch (error) { } catch (error) {
console.error('Error updating dog:', error);
res.status(500).json({ error: error.message }); res.status(500).json({ error: error.message });
} }
}); });
@@ -269,8 +244,10 @@ router.delete('/:id', (req, res) => {
try { try {
const db = getDatabase(); const db = getDatabase();
db.prepare('UPDATE dogs SET is_active = 0 WHERE id = ?').run(req.params.id); db.prepare('UPDATE dogs SET is_active = 0 WHERE id = ?').run(req.params.id);
console.log(`✓ Dog soft-deleted: ID ${req.params.id}`);
res.json({ message: 'Dog deleted successfully' }); res.json({ message: 'Dog deleted successfully' });
} catch (error) { } catch (error) {
console.error('Error deleting dog:', error);
res.status(500).json({ error: error.message }); res.status(500).json({ error: error.message });
} }
}); });
@@ -296,6 +273,7 @@ router.post('/:id/photos', upload.single('photo'), (req, res) => {
res.json({ url: `/uploads/${req.file.filename}`, photos: photoUrls }); res.json({ url: `/uploads/${req.file.filename}`, photos: photoUrls });
} catch (error) { } catch (error) {
console.error('Error uploading photo:', error);
res.status(500).json({ error: error.message }); res.status(500).json({ error: error.message });
} }
}); });
@@ -327,6 +305,7 @@ router.delete('/:id/photos/:photoIndex', (req, res) => {
res.json({ photos: photoUrls }); res.json({ photos: photoUrls });
} catch (error) { } catch (error) {
console.error('Error deleting photo:', error);
res.status(500).json({ error: error.message }); res.status(500).json({ error: error.message });
} }
}); });