Files
omnisupport-ai/backend/index.js
2026-02-17 09:52:33 +01:00

280 lines
8.3 KiB
JavaScript

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();