const express = require('express'); const cors = require('cors'); const helmet = require('helmet'); const path = require('path'); const fs = require('fs'); const { runMigrations } = require('./db/migrations'); const { initDatabase } = require('./db/init'); const { logStartupBanner } = require('./utils/startupLog'); const app = express(); const PORT = process.env.PORT || 3000; // Ensure required directories exist const UPLOAD_PATH = process.env.UPLOAD_PATH || path.join(__dirname, '../uploads'); const STATIC_PATH = process.env.STATIC_PATH || path.join(__dirname, '../static'); const DATA_DIR = process.env.DATA_DIR || path.join(__dirname, '../data'); [DATA_DIR, UPLOAD_PATH, STATIC_PATH].forEach(dir => { if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); }); // Run migrations BEFORE initializing the DB connection used by routes const DB_PATH = process.env.DB_PATH || path.join(__dirname, '../data/breedr.db'); console.log('Running database migrations...'); try { runMigrations(DB_PATH); } catch (err) { console.error('Migration failed — aborting startup:', err.message); process.exit(1); } // Init DB (path is managed internally by db/init.js) console.log('Initializing database...'); initDatabase(); const dbStatus = '✓ Connected'; console.log('✓ Database ready!\n'); // ── Middleware ───────────────────────────────────────────────────────── app.use(helmet({ contentSecurityPolicy: false })); app.use(cors()); app.use(express.json()); app.use(express.urlencoded({ extended: true })); // ── Static file serving ────────────────────────────────────────────── app.use('/uploads', express.static(UPLOAD_PATH)); app.use('/static', express.static(STATIC_PATH)); app.use('/uploads', (_req, res) => res.status(404).json({ error: 'Upload not found' })); app.use('/static', (_req, res) => res.status(404).json({ error: 'Static asset not found' })); // ── API Routes ────────────────────────────────────────────────────────── app.use('/api/dogs', require('./routes/dogs')); app.use('/api/litters', require('./routes/litters')); app.use('/api/health', require('./routes/health')); app.use('/api/genetics', require('./routes/genetics')); app.use('/api/pedigree', require('./routes/pedigree')); app.use('/api/breeding', require('./routes/breeding')); app.use('/api/settings', require('./routes/settings')); // ── Production SPA fallback ──────────────────────────────────────────────── if (process.env.NODE_ENV === 'production') { const clientBuild = path.join(__dirname, '../client/dist'); app.use(express.static(clientBuild)); app.get(/^(?!\/(?:api|static|uploads)\/).*$/, (_req, res) => { res.sendFile(path.join(clientBuild, 'index.html')); }); } // ── Global error handler ────────────────────────────────────────────────── app.use((err, _req, res, _next) => { console.error('Error:', err); res.status(err.status || 500).json({ error: err.message || 'Internal server error', ...(process.env.NODE_ENV === 'development' && { stack: err.stack }) }); }); app.listen(PORT, '0.0.0.0', () => { logStartupBanner({ appName: 'BREEDR', port: PORT, environment: process.env.NODE_ENV || 'development', dataDir: DATA_DIR, uploadPath: UPLOAD_PATH, staticPath: STATIC_PATH, dbStatus: dbStatus }); }); module.exports = app;