Files
LogWhispererAI/tools/fake-backend/server.js
Luca Sacchi Ricciardi 26879acba4 feat: add production configuration with environment variables
- Add .env file for production deployment with reverse proxy
- Add docker-compose.prod.yml for production profile
- Add docker-compose.override.yml for local development
- Update docker-compose.yml with all configurable variables
- Update frontend to use VITE_* environment variables
- Update backend to support CORS_ORIGINS and WEBHOOK_BASE_URL
- Add vite.config.ts allowedHosts for reverse proxy
- Add documentation for docker-compose and reverse proxy setup

All URLs are now configurable via environment variables:
- VITE_API_URL: Backend API endpoint
- VITE_WEBHOOK_BASE_URL: Webhook base URL
- VITE_INSTALL_SCRIPT_URL: Install script URL
- VITE_APP_URL: Frontend URL
- CORS_ORIGINS: Allowed CORS origins
- WEBHOOK_BASE_URL: Backend webhook base URL
2026-04-03 18:49:53 +02:00

273 lines
9.4 KiB
JavaScript

/**
* Fake Backend Server for LogWhispererAI
*
* Simulates AI analysis responses for frontend development
* without requiring real backend or OpenRouter API calls.
*
* @author LogWhispererAI Team
* @version 1.0.0
*/
const express = require('express');
const cors = require('cors');
const app = express();
const PORT = process.env.PORT || 3000;
const DELAY_MS = parseInt(process.env.DELAY_MS) || 1500;
// CORS configuration - supports multiple origins via env var
// CORS_ORIGINS can be comma-separated list or '*' for all
const corsOrigins = process.env.CORS_ORIGINS || '*';
const corsOptions = corsOrigins === '*'
? { origin: '*', methods: ['GET', 'POST'], allowedHeaders: ['Content-Type'] }
: {
origin: corsOrigins.split(',').map(o => o.trim()),
methods: ['GET', 'POST'],
allowedHeaders: ['Content-Type'],
credentials: true
};
app.use(cors(corsOptions));
// Parse JSON bodies
app.use(express.json());
// Mock responses database
const MOCK_RESPONSES = {
// PostgreSQL errors
'memory': {
title: 'PostgreSQL Out of Memory',
description: 'Il database ha esaurito la memoria disponibile. Questo è spesso causato da query troppo pesanti o da un numero eccessivo di connessioni.',
command: 'ps aux | grep postgres | head -5 && free -h',
isSafe: true,
note: 'Verifica processi Postgres e memoria disponibile. Se necessario, riavvia il servizio con: sudo systemctl restart postgresql'
},
'oom': {
title: 'PostgreSQL Out of Memory',
description: 'Out Of Memory error rilevato. Il sistema ha esaurito la RAM disponibile.',
command: 'free -h && ps aux --sort=-%mem | head -10',
isSafe: true,
note: 'Identifica i processi che consumano più memoria e considera di aumentare la RAM o ottimizzare le query.'
},
// Nginx/Connection errors
'connection refused': {
title: 'Connessione al Backend Rifiutata',
description: 'Il reverse proxy (Nginx) non riesce a connettersi al backend. Il servizio potrebbe essere down.',
command: 'sudo systemctl status app-service && netstat -tlnp | grep 3000',
isSafe: true,
note: 'Verifica che il servizio backend sia in esecuzione. Se stopped, avvia con: sudo systemctl start app-service'
},
'502': {
title: 'Nginx 502 Bad Gateway',
description: 'Nginx riceve errore dal server upstream. Il backend non risponde correttamente.',
command: 'sudo systemctl status backend && tail -n 50 /var/log/backend/error.log',
isSafe: true,
note: 'Controlla i log del backend per errori specifici. Potrebbe essere necessario un riavvio.'
},
// Node.js errors
'econnrefused': {
title: 'Node.js - Connessione Database Rifiutata',
description: 'L\'applicazione Node non riesce a connettersi al database PostgreSQL sulla porta 5432.',
command: 'sudo systemctl status postgresql && sudo netstat -tlnp | grep 5432',
isSafe: true,
note: 'Verifica che PostgreSQL sia in esecuzione. Se down, avvia con: sudo systemctl start postgresql'
},
'exception': {
title: 'Node.js Exception',
description: 'Eccezione non gestita nell\'applicazione Node.js. Potrebbe essere un errore di connessione o configurazione.',
command: 'pm2 logs app --lines 50',
isSafe: true,
note: 'Controlla i log dell\'applicazione per l\'errore completo. Usa pm2 restart app se necessario.'
},
// Disk errors
'disk': {
title: 'Spazio su Disco Esaurito',
description: 'Il filesystem ha raggiunto il 100% di utilizzo. Nessuna scrittura possibile.',
command: 'df -h && du -sh /var/log/* | sort -hr | head -10',
isSafe: true,
note: 'Identifica quali directory occupano più spazio. Pulisci log vecchi con: sudo find /var/log -name "*.log" -mtime +7 -delete'
},
'no space': {
title: 'Spazio su Disco Esaurito',
description: 'Spazio insufficiente sul disco. Impossibile scrivere nuovi dati.',
command: 'df -h && du -sh /tmp /var/log /var/cache',
isSafe: true,
note: 'Libera spazio eliminando file temporanei o log vecchi.'
}
};
// Default response for unknown errors
const DEFAULT_RESPONSE = {
title: 'Errore di Sistema',
description: 'È stato rilevato un errore nel log. L\'analisi suggerisce di verificare lo stato dei servizi e le risorse di sistema.',
command: 'sudo systemctl status && df -h && free -h',
isSafe: true,
note: 'Esegui il comando sopra per verificare lo stato generale del sistema. Se il problema persiste, controlla i log specifici del servizio.'
};
/**
* Find matching mock response based on log content
* @param {string} logContent - The log content to analyze
* @returns {object} - Matching response or default
*/
function findMockResponse(logContent) {
const logLower = logContent.toLowerCase();
for (const [keyword, response] of Object.entries(MOCK_RESPONSES)) {
if (logLower.includes(keyword.toLowerCase())) {
return response;
}
}
return DEFAULT_RESPONSE;
}
/**
* POST /api/analyze
* Analyze log and return AI-like response
*/
app.post('/api/analyze', (req, res) => {
const { log } = req.body;
// Validate input
if (!log || typeof log !== 'string' || log.trim().length === 0) {
return res.status(400).json({
success: false,
error: 'Bad Request',
message: 'Il campo "log" è richiesto e non può essere vuoto',
timestamp: new Date().toISOString()
});
}
// Simulate AI processing delay
setTimeout(() => {
try {
const analysis = findMockResponse(log);
res.json({
success: true,
analysis: {
...analysis,
originalLog: log.substring(0, 500) // Include first 500 chars for reference
},
meta: {
processingTime: `${DELAY_MS}ms`,
model: 'fake-backend-mock-v1',
timestamp: new Date().toISOString()
}
});
} catch (error) {
console.error('Error processing request:', error);
res.status(500).json({
success: false,
error: 'Internal Server Error',
message: 'Errore durante l\'analisi del log',
timestamp: new Date().toISOString()
});
}
}, DELAY_MS);
});
// Environment configuration for webhook URLs
const WEBHOOK_BASE_URL = process.env.WEBHOOK_BASE_URL || 'https://logwhisperer.ai/webhook';
/**
* POST /api/webhook
* Generate a fake webhook URL for onboarding
*/
app.post('/api/webhook', (req, res) => {
// Simulate generation delay (500ms - 1s)
const delay = Math.floor(Math.random() * 500) + 500;
setTimeout(() => {
try {
// Generate fake UUID v4
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = Math.random() * 16 | 0;
const v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
res.json({
success: true,
webhookUrl: `${WEBHOOK_BASE_URL}/${uuid}`,
uuid: uuid,
meta: {
generatedAt: new Date().toISOString(),
expiresIn: '30 days'
}
});
} catch (error) {
console.error('Error generating webhook:', error);
res.status(500).json({
success: false,
error: 'Internal Server Error',
message: 'Errore durante la generazione del webhook',
timestamp: new Date().toISOString()
});
}
}, delay);
});
/**
* GET /health
* Health check endpoint
*/
app.get('/health', (req, res) => {
res.json({
status: 'ok',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
version: '1.0.0'
});
});
/**
* GET /
* Root endpoint with API info
*/
app.get('/', (req, res) => {
res.json({
name: 'LogWhispererAI - Fake Backend',
version: '1.0.0',
description: 'Mock API server for frontend development',
endpoints: {
'POST /api/analyze': 'Analyze log and return AI-like response',
'GET /health': 'Health check endpoint'
},
documentation: 'See docs/tools_fake_backend.md'
});
});
// Error handling middleware
app.use((err, req, res, next) => {
console.error('Unhandled error:', err);
res.status(500).json({
success: false,
error: 'Internal Server Error',
message: 'Errore imprevisto del server',
timestamp: new Date().toISOString()
});
});
// Start server - listen on all interfaces (0.0.0.0) to allow external connections
app.listen(PORT, '0.0.0.0', () => {
console.log(`
╔══════════════════════════════════════════════════════════════╗
║ LogWhispererAI - Fake Backend Server ║
║ ║
║ 🚀 Server running on http://localhost:${PORT}
║ 📖 Documentation: docs/tools_fake_backend.md ║
║ ⏱️ Simulated delay: ${DELAY_MS}ms ║
║ ║
║ Endpoints: ║
║ POST /api/analyze - Analyze log and get mock AI response ║
║ POST /api/webhook - Generate fake webhook URL ║
║ GET /health - Health check ║
║ ║
║ Press Ctrl+C to stop ║
╚══════════════════════════════════════════════════════════════╝
`);
});
module.exports = app; // Export for testing