diff --git a/CHANGELOG.md b/CHANGELOG.md index 8610fed..a27dfc6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,16 +8,23 @@ Todas as alterações notáveis neste projecto serão documentadas neste ficheir - ✅ **Monitor.tsx** - Corrigido URL da API de `/api/monitor.php` para `/api/monitor` - ✅ **Monitor.tsx** - Adaptada estrutura de dados para nova resposta da API (items agrupados por categoria) - ✅ **Monitoring Service** - Corrigido cálculo de stats (MySQL retornava strings) +- ✅ **server.ts** - Adicionado serving de ficheiros estáticos em produção ### Changed - ✅ Interface `MonitorData` actualizada para reflectir estrutura real da API - ✅ Mock data actualizado com dados realistas da BD - ✅ Sumário usa `data.stats` em vez de `data.summary` para totais +### Added +- ✅ **Dockerfile** - Configuração Docker com NODE_ENV=production +- ✅ **Static serving** - Frontend servido pelo Express em produção +- ✅ **SPA fallback** - Rotas não-API devolvem index.html + ### Technical Notes - API endpoint: `/api/monitor` retorna dados de `tbl_eal_monitoring` - Categorias disponíveis: server, service, site, container, backup, wp_update, storage - Stats calculados server-side com conversão explícita para números +- Em produção: Express serve `dist/` e faz fallback para SPA --- diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..58e22e6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,25 @@ +FROM node:22-alpine + +WORKDIR /app + +# Copy package files +COPY package*.json ./ + +# Install ALL dependencies (needed for build) +RUN npm ci + +# Copy source +COPY . . + +# Build frontend and API +RUN npm run build + +# Set production environment +ENV NODE_ENV=production +ENV API_PORT=3001 + +# Expose port +EXPOSE 3001 + +# Start command (serves API + static frontend) +CMD ["npm", "start"] diff --git a/api/server.ts b/api/server.ts index 3d4fdc9..2951e3b 100644 --- a/api/server.ts +++ b/api/server.ts @@ -5,6 +5,8 @@ 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' @@ -12,8 +14,12 @@ import hetznerRouter from './routes/hetzner.js' import wpMonitorRouter from './routes/wp-monitor.js' import serverMetricsRouter from './routes/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({ @@ -35,6 +41,20 @@ app.use('/api/hetzner', hetznerRouter) app.use('/api/wp-monitor', wpMonitorRouter) app.use('/api/server-metrics', serverMetricsRouter) +// Serve static files in production +if (isProduction) { + 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)