/** * Express API Server * @author Descomplicar® | @link descomplicar.pt | @copyright 2026 */ import 'dotenv/config' import express from 'express' import cors from 'cors' import path from 'path' import { fileURLToPath } from 'url' import dashboardRouter from './routes/dashboard.js' import monitorRouter from './routes/monitor.js' import diagnosticRouter from './routes/diagnostic.js' import hetznerRouter from './routes/hetzner.js' import wpMonitorRouter from './routes/wp-monitor.js' import serverMetricsRouter from './routes/server-metrics.js' import financialRouter from './routes/financial.js' import { collectAllServerMetrics } from './services/server-metrics.js' const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) const app = express() const PORT = process.env.API_PORT || 3001 const isProduction = process.env.NODE_ENV === 'production' // Middleware app.use(cors({ origin: process.env.FRONTEND_URL || 'http://localhost:5173', credentials: true })) app.use(express.json()) // Health check app.get('/api/health', (_req, res) => { res.json({ status: 'ok', timestamp: new Date().toISOString() }) }) // Routes app.use('/api/dashboard', dashboardRouter) app.use('/api/monitor', monitorRouter) app.use('/api/diagnostic', diagnosticRouter) app.use('/api/hetzner', hetznerRouter) app.use('/api/wp-monitor', wpMonitorRouter) app.use('/api/server-metrics', serverMetricsRouter) app.use('/api/financial', financialRouter) // Serve static files in production if (isProduction) { // __dirname is /app/api/dist, need to go up 2 levels to /app/dist const distPath = path.join(__dirname, '..', '..', 'dist') app.use(express.static(distPath)) // SPA fallback - serve index.html for all non-API routes app.get('*', (req, res, next) => { if (req.path.startsWith('/api')) { return next() } res.sendFile(path.join(distPath, 'index.html')) }) } // Error handling app.use((err: any, _req: express.Request, res: express.Response, _next: express.NextFunction) => { console.error('Server error:', err) res.status(500).json({ error: 'Internal server error' }) }) // Start server app.listen(PORT, () => { console.log('='.repeat(50)) console.log(`API Server running on http://localhost:${PORT}`) console.log(`Dashboard: http://localhost:${PORT}/api/dashboard`) console.log(`Monitor: http://localhost:${PORT}/api/monitor`) console.log(`Hetzner: http://localhost:${PORT}/api/hetzner`) console.log('='.repeat(50)) // Auto-collect server metrics every 5 minutes if (isProduction) { const INTERVAL = 5 * 60 * 1000 console.log('[SCHEDULER] Server metrics collection every 5min') // Initial collection after 30s (let server stabilize) setTimeout(() => { collectAllServerMetrics().catch(err => console.error('[SCHEDULER] Initial collection failed:', err) ) }, 30000) // Recurring collection setInterval(() => { collectAllServerMetrics().catch(err => console.error('[SCHEDULER] Collection failed:', err) ) }, INTERVAL) } })