import express from 'express'; import cors from 'cors'; import { checkConnection, query, initDb } from './db.js'; import { randomUUID } from 'crypto'; const app = express(); const PORT = process.env.PORT || 3000; app.use(cors()); app.use(express.json()); // --- HELPER FUNCTIONS --- const formatDate = (date) => new Date(date).toISOString(); // --- AUTH ENDPOINTS --- app.post('/api/auth/login', async (req, res) => { const { email, password } = req.body; try { // 1. Check Agents const agents = await query('SELECT * FROM agents WHERE email = ? AND password = ?', [email, password]); if (agents.length > 0) { const agent = agents[0]; // Parse JSON fields agent.queues = typeof agent.queues === 'string' ? JSON.parse(agent.queues) : agent.queues; agent.skills = typeof agent.skills === 'string' ? JSON.parse(agent.skills) : agent.skills; agent.avatarConfig = typeof agent.avatar_config === 'string' ? JSON.parse(agent.avatar_config) : agent.avatar_config; return res.json({ user: agent, role: agent.role }); } // 2. Check Clients const clients = await query('SELECT * FROM client_users WHERE email = ? AND password = ?', [email, password]); if (clients.length > 0) { const client = clients[0]; return res.json({ user: client, role: 'client' }); } res.status(401).json({ error: 'Credenziali non valide' }); } catch (e) { console.error(e); res.status(500).json({ error: e.message }); } }); app.post('/api/auth/register', async (req, res) => { const { name, email, password, company } = req.body; const id = randomUUID(); try { await query( 'INSERT INTO client_users (id, name, email, password, company, status) VALUES (?, ?, ?, ?, ?, ?)', [id, name, email, password, company || null, 'active'] ); res.json({ success: true, user: { id, name, email, company, status: 'active' } }); } catch (e) { res.status(500).json({ error: 'Errore registrazione. Email potrebbe essere già in uso.' }); } }); // --- DATA FETCHING ENDPOINTS --- app.get('/api/initial-data', async (req, res) => { try { // Fetch Agents const agents = await query('SELECT * FROM agents'); const parsedAgents = agents.map(a => ({ ...a, queues: typeof a.queues === 'string' ? JSON.parse(a.queues) : a.queues, skills: typeof a.skills === 'string' ? JSON.parse(a.skills) : a.skills, avatarConfig: typeof a.avatar_config === 'string' ? JSON.parse(a.avatar_config) : a.avatar_config })); // Fetch Client Users const clientUsers = await query('SELECT * FROM client_users'); // Fetch Queues const queues = await query('SELECT * FROM queues'); // Fetch KB Articles const articles = await query('SELECT * FROM kb_articles ORDER BY last_updated DESC'); // Fetch Surveys const surveys = await query('SELECT * FROM survey_results ORDER BY timestamp DESC'); res.json({ agents: parsedAgents, clientUsers, queues, articles, surveys }); } catch (e) { console.error(e); res.status(500).json({ error: e.message }); } }); // --- TICKET ENDPOINTS --- app.get('/api/tickets', async (req, res) => { try { // Complex query to get tickets + messages + attachments // Note: We use a subquery or JSON_ARRAYAGG to bundle messages const sql = ` SELECT t.*, t.assigned_agent_id as assignedAgentId, t.customer_name as customerName, t.created_at as createdAt, ( SELECT JSON_ARRAYAGG( JSON_OBJECT( 'id', m.id, 'role', m.role, 'content', m.content, 'timestamp', m.timestamp ) ) FROM ticket_messages m WHERE m.ticket_id = t.id ) as messages FROM tickets t ORDER BY t.created_at DESC `; const rows = await query(sql); const tickets = rows.map(t => ({ ...t, attachments: typeof t.attachments === 'string' ? JSON.parse(t.attachments || '[]') : t.attachments, messages: typeof t.messages === 'string' ? JSON.parse(t.messages || '[]') : (t.messages || []) })); res.json(tickets); } catch (e) { console.error(e); res.status(500).json({ error: e.message }); } }); app.post('/api/tickets', async (req, res) => { const { subject, description, priority, queue, customerName, attachments } = req.body; const id = `T-${Date.now()}`; // Generate a simple ID or UUID try { await query( 'INSERT INTO tickets (id, subject, description, status, priority, customer_name, queue, attachments) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', [id, subject, description, 'APERTO', priority, customerName, queue, JSON.stringify(attachments || [])] ); // Fetch the created ticket to return it const newTicket = { id, subject, description, status: 'APERTO', priority, customerName, queue, attachments: attachments || [], messages: [], createdAt: new Date().toISOString() }; res.json(newTicket); } catch (e) { console.error(e); res.status(500).json({ error: e.message }); } }); app.patch('/api/tickets/:id', async (req, res) => { const { id } = req.params; const updates = req.body; const fields = []; const values = []; if (updates.status) { fields.push('status = ?'); values.push(updates.status); } if (updates.priority) { fields.push('priority = ?'); values.push(updates.priority); } if (updates.assignedAgentId !== undefined) { fields.push('assigned_agent_id = ?'); values.push(updates.assignedAgentId || null); } if (updates.queue) { fields.push('queue = ?'); values.push(updates.queue); } if (fields.length === 0) return res.json({ success: true }); // Nothing to update values.push(id); try { await query(`UPDATE tickets SET ${fields.join(', ')} WHERE id = ?`, values); res.json({ success: true }); } catch (e) { res.status(500).json({ error: e.message }); } }); // --- MESSAGING ENDPOINTS --- app.post('/api/tickets/:id/messages', async (req, res) => { const { id } = req.params; // Ticket ID const { role, content } = req.body; const messageId = `m-${Date.now()}`; try { await query( 'INSERT INTO ticket_messages (id, ticket_id, role, content) VALUES (?, ?, ?, ?)', [messageId, id, role, content] ); // Se il messaggio è dell'utente e il ticket era risolto, riapriamolo if (role === 'user') { await query("UPDATE tickets SET status = 'APERTO' WHERE id = ? AND status = 'RISOLTO'", [id]); } res.json({ id: messageId, role, content, timestamp: new Date().toISOString() }); } catch (e) { res.status(500).json({ error: e.message }); } }); // --- SURVEY ENDPOINTS --- app.post('/api/surveys', async (req, res) => { const { rating, comment, source, referenceId } = req.body; const id = randomUUID(); try { await query( 'INSERT INTO survey_results (id, rating, comment, source, reference_id) VALUES (?, ?, ?, ?, ?)', [id, rating, comment, source, referenceId || null] ); res.json({ success: true, id }); } catch (e) { res.status(500).json({ error: e.message }); } }); // --- KB ENDPOINTS --- app.post('/api/articles', async (req, res) => { const { title, content, category, type, url, source } = req.body; const id = `kb-${Date.now()}`; try { await query( 'INSERT INTO kb_articles (id, title, content, category, type, url, source) VALUES (?, ?, ?, ?, ?, ?, ?)', [id, title, content, category, type, url, source] ); res.json({ success: true, id, lastUpdated: new Date().toISOString() }); } catch (e) { res.status(500).json({ error: e.message }); } }); app.patch('/api/articles/:id', async (req, res) => { const { id } = req.params; const { title, content, category, type, url } = req.body; try { await query( 'UPDATE kb_articles SET title=?, content=?, category=?, type=?, url=? WHERE id=?', [title, content, category, type, url, id] ); res.json({ success: true }); } catch (e) { res.status(500).json({ error: e.message }); } }); // Avvio del server const startServer = async () => { await initDb(); app.listen(PORT, () => { console.log(`🚀 Backend Server running on port ${PORT}`); checkConnection(); }); }; startServer();