const express = require('express'); const router = express.Router(); const pool = require('../db'); const { db } = require('../activityDb'); // Helper to fetch details of a list of tickets from OTRS DB async function fetchTicketsDetails(ticketIds) { if (!ticketIds || ticketIds.length === 0) return []; try { const placeholders = ticketIds.map((_, i) => `$${i + 1}`).join(', '); const query = ` SELECT t.id, t.tn, t.title, t.queue_id, q.name AS queue_name, t.ticket_state_id, ts.name AS state_name, tst.name AS state_type, t.ticket_priority_id, tp.name AS priority_name, t.user_id, u.first_name AS owner_first, u.last_name AS owner_last, u.login AS owner_login FROM ticket t JOIN queue q ON t.queue_id = q.id JOIN ticket_state ts ON t.ticket_state_id = ts.id JOIN ticket_state_type tst ON ts.type_id = tst.id JOIN ticket_priority tp ON t.ticket_priority_id = tp.id LEFT JOIN users u ON t.user_id = u.id WHERE t.id IN (${placeholders}) `; const res = await pool.query(query, ticketIds); return res.rows; } catch (err) { console.error('Error fetching ticket details from OTRS DB:', err); return []; } } // GET /api/groups - List all groups with member counts router.get('/', (req, res) => { try { const groups = db.prepare(` SELECT g.*, (SELECT COUNT(*) FROM ticket_group_members WHERE group_id = g.id) AS member_count FROM ticket_groups g ORDER BY g.nome ASC `).all(); res.json(groups); } catch (err) { res.status(500).json({ error: 'Errore nel recupero dei gruppi', message: err.message }); } }); // GET /api/groups/by-ticket/:ticket_id - Get groups associated to a specific ticket router.get('/by-ticket/:ticket_id', (req, res) => { try { const { ticket_id } = req.params; const ticketIdNum = parseInt(ticket_id, 10); if (isNaN(ticketIdNum)) { return res.status(400).json({ error: 'ID ticket non valido' }); } const asMaster = db.prepare('SELECT id, nome, descrizione FROM ticket_groups WHERE master_ticket_id = ?').all(ticketIdNum); const asMember = db.prepare(` SELECT g.id, g.nome, g.descrizione FROM ticket_groups g JOIN ticket_group_members m ON g.id = m.group_id WHERE m.ticket_id = ? `).all(ticketIdNum); res.json({ asMaster, asMember }); } catch (err) { res.status(500).json({ error: 'Errore nel recupero dei gruppi del ticket', message: err.message }); } }); // POST /api/groups - Create a new group router.post('/', (req, res) => { try { const { nome, descrizione, master_ticket_id } = req.body; if (!nome) { return res.status(400).json({ error: 'Il nome del gruppo è obbligatorio' }); } const masterId = master_ticket_id ? parseInt(master_ticket_id, 10) : null; const info = db.prepare(` INSERT INTO ticket_groups (nome, descrizione, master_ticket_id) VALUES (?, ?, ?) `).run(nome, descrizione || '', masterId); res.json({ id: info.lastInsertRowid, nome, descrizione, master_ticket_id: masterId }); } catch (err) { res.status(500).json({ error: 'Errore nella creazione del gruppo', message: err.message }); } }); // GET /api/groups/:id - Detail of a single group router.get('/:id', async (req, res) => { try { const { id } = req.params; const group = db.prepare('SELECT * FROM ticket_groups WHERE id = ?').get(id); if (!group) { return res.status(404).json({ error: 'Gruppo non trovato' }); } // Get members list const memberRows = db.prepare('SELECT ticket_id FROM ticket_group_members WHERE group_id = ?').all(id); const memberIds = memberRows.map(r => r.ticket_id); // Fetch details of master and member tickets from OTRS let masterTicket = null; if (group.master_ticket_id) { const details = await fetchTicketsDetails([group.master_ticket_id]); if (details.length > 0) { masterTicket = details[0]; } } let memberTickets = []; if (memberIds.length > 0) { memberTickets = await fetchTicketsDetails(memberIds); } res.json({ group, masterTicket, memberTickets }); } catch (err) { res.status(500).json({ error: 'Errore nel recupero dei dettagli del gruppo', message: err.message }); } }); // PUT /api/groups/:id - Update group info router.put('/:id', (req, res) => { try { const { id } = req.params; const { nome, descrizione, master_ticket_id } = req.body; if (!nome) { return res.status(400).json({ error: 'Il nome del gruppo è obbligatorio' }); } const masterId = master_ticket_id ? parseInt(master_ticket_id, 10) : null; const info = db.prepare(` UPDATE ticket_groups SET nome = ?, descrizione = ?, master_ticket_id = ? WHERE id = ? `).run(nome, descrizione || '', masterId, id); if (info.changes === 0) { return res.status(404).json({ error: 'Gruppo non trovato o nessuna modifica' }); } res.json({ id: parseInt(id, 10), nome, descrizione, master_ticket_id: masterId }); } catch (err) { res.status(500).json({ error: 'Errore nell\'aggiornamento del gruppo', message: err.message }); } }); // DELETE /api/groups/:id - Delete group router.delete('/:id', (req, res) => { try { const { id } = req.params; const info = db.prepare('DELETE FROM ticket_groups WHERE id = ?').run(id); if (info.changes === 0) { return res.status(404).json({ error: 'Gruppo non trovato' }); } res.json({ success: true, message: 'Gruppo eliminato con successo' }); } catch (err) { res.status(500).json({ error: 'Errore nella cancellazione del gruppo', message: err.message }); } }); // POST /api/groups/:id/tickets - Add ticket(s) to group router.post('/:id/tickets', async (req, res) => { try { const { id } = req.params; const { ticket_identifier } = req.body; // Can be ticket ID or ticket number (tn) if (!ticket_identifier) { return res.status(400).json({ error: 'Identificativo ticket obbligatorio' }); } const cleanIdentifier = String(ticket_identifier).trim(); let queryTicketSql; let queryParams; if (/^\d+$/.test(cleanIdentifier)) { // It's a number - check if it matches id or tn queryTicketSql = 'SELECT id, tn, title FROM ticket WHERE id = $1 OR tn = $2'; queryParams = [parseInt(cleanIdentifier, 10), cleanIdentifier]; } else { // Check tn queryTicketSql = 'SELECT id, tn, title FROM ticket WHERE tn = $1'; queryParams = [cleanIdentifier]; } const otrsRes = await pool.query(queryTicketSql, queryParams); if (otrsRes.rows.length === 0) { return res.status(404).json({ error: `Ticket con identificativo '${cleanIdentifier}' non trovato` }); } const ticket = otrsRes.rows[0]; // Check if group exists const group = db.prepare('SELECT id FROM ticket_groups WHERE id = ?').get(id); if (!group) { return res.status(404).json({ error: 'Gruppo non trovato' }); } // Insert to membership try { db.prepare(` INSERT INTO ticket_group_members (group_id, ticket_id) VALUES (?, ?) `).run(id, ticket.id); } catch (dbErr) { if (dbErr.code === 'SQLITE_CONSTRAINT_PRIMARYKEY') { return res.status(409).json({ error: 'Il ticket appartiene già a questo gruppo' }); } throw dbErr; } res.json({ success: true, ticket }); } catch (err) { res.status(500).json({ error: 'Errore nell\'associazione del ticket', message: err.message }); } }); // DELETE /api/groups/:id/tickets/:ticket_id - Remove ticket from group router.delete('/:id/tickets/:ticket_id', (req, res) => { try { const { id, ticket_id } = req.params; const info = db.prepare('DELETE FROM ticket_group_members WHERE group_id = ? AND ticket_id = ?').run(id, ticket_id); if (info.changes === 0) { return res.status(404).json({ error: 'Associazione non trovata' }); } res.json({ success: true, message: 'Ticket rimosso dal gruppo' }); } catch (err) { res.status(500).json({ error: 'Errore nella rimozione del ticket', message: err.message }); } }); module.exports = router;