/** * 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