Files
Claro-cases/legacy/server.js
T
bryan_garcia 8f044567c0 feat: migrar dashboard a React 19 + TypeScript + Vite + Tailwind v4
- Módulo HITL (/cases): 6 patrones de formularios dinámicos para 53 tipos de caso con validación Zod
- Módulo Monitor (/monitor): streaming token-a-token en tiempo real, auto-scroll y notas internas vía WebSocket
- Arquitectura híbrida: REST (canal autoritativo) + WebSocket (difusión/streaming)
- MSW para desarrollo sin backend, hooks de notificaciones/sonido/título preservados
- Backend legacy movido a legacy/, archivos residuales eliminados de raíz
2026-07-23 18:27:03 -05:00

152 lines
4.6 KiB
JavaScript

const express = require('express');
const cors = require('cors');
const path = require('path');
const db = require('./db');
require('dotenv').config();
const app = express();
const PORT = process.env.PORT || 3000;
app.use(cors());
app.use(express.json());
app.use(express.static(path.join(__dirname, 'public')));
// Store active Server-Sent Events (SSE) clients
let sseClients = [];
// SSE Registration Endpoint
app.get('/api/sse', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
res.flushHeaders();
sseClients.push(res);
console.log(`🔌 Client connected to SSE stream. Total clients: ${sseClients.length}`);
req.on('close', () => {
sseClients = sseClients.filter(client => client !== res);
console.log(`🔌 Client disconnected. Total clients: ${sseClients.length}`);
});
});
// Function to broadcast new request details to all connected SSE clients
function broadcastNewRequest(request) {
const data = JSON.stringify(request);
sseClients.forEach(client => {
client.write(`data: ${data}\n\n`);
});
}
// API: Get all requests (ordered by newest first)
app.get('/api/requests', async (req, res) => {
try {
const rows = await db.query('SELECT * FROM requests ORDER BY created_at DESC');
res.json(rows);
} catch (error) {
console.error('Error fetching requests:', error);
res.status(500).json({ error: 'Database query failed' });
}
});
// API: Post new request (receives external data)
app.post('/api/requests', async (req, res) => {
const { title, description, external_id, status, payload, cedula } = req.body;
const tipo_solicitud = req.body.tipo_solicitud || req.body['Tipo de solicitud'] || req.body.tipo_de_solicitud || null;
if (!title) {
return res.status(400).json({ error: 'Title is required' });
}
try {
const queryText = `
INSERT INTO requests (title, description, external_id, status, cedula, tipo_solicitud, payload)
VALUES (?, ?, ?, ?, ?, ?, ?)
`;
const parsedPayload = payload ? (typeof payload === 'object' ? payload : { raw: payload }) : {};
const values = [
title,
description || '',
external_id || null,
status || 'Pending',
cedula || null,
tipo_solicitud,
JSON.stringify(parsedPayload)
];
const result = await db.query(queryText, values);
const insertId = result.insertId;
// Fetch the newly inserted record to broadcast
const rows = await db.query('SELECT * FROM requests WHERE id = ?', [insertId]);
const newRequest = rows[0];
// Broadcast the new request to UI clients in real time
broadcastNewRequest(newRequest);
res.status(201).json(newRequest);
} catch (error) {
console.error('Error saving new request:', error);
res.status(500).json({ error: 'Database insertion failed' });
}
});
// API: Update request (save status, handling time, and optionally payload)
app.put('/api/requests/:id', async (req, res) => {
const { id } = req.params;
const { status, handling_time, payload } = req.body;
try {
if (payload !== undefined) {
const parsedPayload = typeof payload === 'object' ? payload : { raw: payload };
await db.query(
'UPDATE requests SET status = ?, handling_time = ?, payload = ? WHERE id = ?',
[status || 'Pending', handling_time || 0, JSON.stringify(parsedPayload), id]
);
} else {
await db.query(
'UPDATE requests SET status = ?, handling_time = ? WHERE id = ?',
[status || 'Pending', handling_time || 0, id]
);
}
const rows = await db.query('SELECT * FROM requests WHERE id = ?', [id]);
if (rows.length === 0) {
return res.status(404).json({ error: 'Request not found' });
}
res.json(rows[0]);
} catch (error) {
console.error('Error updating request:', error);
res.status(500).json({ error: 'Database update failed' });
}
});
// API: Delete request
app.delete('/api/requests/:id', async (req, res) => {
const { id } = req.params;
try {
await db.query('DELETE FROM requests WHERE id = ?', [id]);
res.json({ success: true, message: 'Request deleted successfully' });
} catch (error) {
console.error('Error deleting request:', error);
res.status(500).json({ error: 'Database deletion failed' });
}
});
// Start server after ensuring DB connection and schema setup
async function startServer() {
await db.initializeSchema();
app.listen(PORT, () => {
console.log(`🚀 Claro Cases server running at http://localhost:${PORT}`);
});
}
startServer().catch(err => {
console.error('💥 Server startup failed:', err);
});