fix: serve static files in production

- Added static file serving in Express for production
- Added SPA fallback for client-side routing
- Created Dockerfile with NODE_ENV=production
- Frontend now properly served at dash.descomplicar.pt

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-04 23:43:36 +00:00
parent 75f29ee6d5
commit 4af01c0f36
3 changed files with 52 additions and 0 deletions

View File

@@ -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** - 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) -**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) -**Monitoring Service** - Corrigido cálculo de stats (MySQL retornava strings)
-**server.ts** - Adicionado serving de ficheiros estáticos em produção
### Changed ### Changed
- ✅ Interface `MonitorData` actualizada para reflectir estrutura real da API - ✅ Interface `MonitorData` actualizada para reflectir estrutura real da API
- ✅ Mock data actualizado com dados realistas da BD - ✅ Mock data actualizado com dados realistas da BD
- ✅ Sumário usa `data.stats` em vez de `data.summary` para totais - ✅ 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 ### Technical Notes
- API endpoint: `/api/monitor` retorna dados de `tbl_eal_monitoring` - API endpoint: `/api/monitor` retorna dados de `tbl_eal_monitoring`
- Categorias disponíveis: server, service, site, container, backup, wp_update, storage - Categorias disponíveis: server, service, site, container, backup, wp_update, storage
- Stats calculados server-side com conversão explícita para números - Stats calculados server-side com conversão explícita para números
- Em produção: Express serve `dist/` e faz fallback para SPA
--- ---

25
Dockerfile Normal file
View File

@@ -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"]

View File

@@ -5,6 +5,8 @@
import 'dotenv/config' import 'dotenv/config'
import express from 'express' import express from 'express'
import cors from 'cors' import cors from 'cors'
import path from 'path'
import { fileURLToPath } from 'url'
import dashboardRouter from './routes/dashboard.js' import dashboardRouter from './routes/dashboard.js'
import monitorRouter from './routes/monitor.js' import monitorRouter from './routes/monitor.js'
import diagnosticRouter from './routes/diagnostic.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 wpMonitorRouter from './routes/wp-monitor.js'
import serverMetricsRouter from './routes/server-metrics.js' import serverMetricsRouter from './routes/server-metrics.js'
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
const app = express() const app = express()
const PORT = process.env.API_PORT || 3001 const PORT = process.env.API_PORT || 3001
const isProduction = process.env.NODE_ENV === 'production'
// Middleware // Middleware
app.use(cors({ app.use(cors({
@@ -35,6 +41,20 @@ app.use('/api/hetzner', hetznerRouter)
app.use('/api/wp-monitor', wpMonitorRouter) app.use('/api/wp-monitor', wpMonitorRouter)
app.use('/api/server-metrics', serverMetricsRouter) 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 // Error handling
app.use((err: any, _req: express.Request, res: express.Response, _next: express.NextFunction) => { app.use((err: any, _req: express.Request, res: express.Response, _next: express.NextFunction) => {
console.error('Server error:', err) console.error('Server error:', err)