const express = require('express'); const router = express.Router(); const pool = require('../db'); // Helper for OTRS CE GenericInterface REST API calls async function otrsRequest(method, path, bodyData = {}) { const OTRS_API_USER = process.env.OTRS_API_USER; const OTRS_API_PASSWORD = process.env.OTRS_API_PASSWORD; const OTRS_API_URL = process.env.OTRS_API_URL; if (!OTRS_API_URL || !OTRS_API_USER) { return null; } const url = `${OTRS_API_URL.replace(/\/$/, '')}${path.startsWith('/') ? path : '/' + path}`; const payload = { UserLogin: OTRS_API_USER, Password: OTRS_API_PASSWORD, ...bodyData }; const response = await fetch(url, { method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); if (!response.ok) { const errorText = await response.text(); throw new Error(`OTRS REST API error (${response.status}): ${errorText}`); } return await response.json(); } // GET /api/queues — Active queues router.get('/queues', async (req, res) => { try { const result = await pool.query( `SELECT q.id, q.name, q.comments FROM queue q WHERE q.valid_id = 1 ORDER BY q.name` ); res.json(result.rows); } catch (err) { console.error('Error fetching queues:', err); res.status(500).json({ error: err.message }); } }); // GET /api/states — Ticket states with state type router.get('/states', async (req, res) => { try { const result = await pool.query( `SELECT ts.id, ts.name, tst.name AS type_name FROM ticket_state ts JOIN ticket_state_type tst ON ts.type_id = tst.id WHERE ts.valid_id = 1 ORDER BY ts.id` ); res.json(result.rows); } catch (err) { console.error('Error fetching states:', err); res.status(500).json({ error: err.message }); } }); // GET /api/priorities — Ticket priorities router.get('/priorities', async (req, res) => { try { const result = await pool.query( `SELECT id, name, color FROM ticket_priority WHERE valid_id = 1 ORDER BY id` ); res.json(result.rows); } catch (err) { console.error('Error fetching priorities:', err); res.status(500).json({ error: err.message }); } }); // GET /api/users — Active agents/operators router.get('/users', async (req, res) => { try { const result = await pool.query( `SELECT id, login, first_name, last_name, title FROM users WHERE valid_id = 1 ORDER BY last_name, first_name` ); res.json(result.rows); } catch (err) { console.error('Error fetching users:', err); res.status(500).json({ error: err.message }); } }); // GET /api/types — Ticket types router.get('/types', async (req, res) => { try { const result = await pool.query( `SELECT id, name FROM ticket_type WHERE valid_id = 1 ORDER BY name` ); res.json(result.rows); } catch (err) { console.error('Error fetching types:', err); res.status(500).json({ error: err.message }); } }); // GET /api/lock-types — Ticket lock types router.get('/lock-types', async (req, res) => { try { const result = await pool.query( `SELECT id, name FROM ticket_lock_type WHERE valid_id = 1 ORDER BY id` ); res.json(result.rows); } catch (err) { console.error('Error fetching lock types:', err); res.status(500).json({ error: err.message }); } }); // GET /api/customer-companies/search — Search customer companies router.get('/customer-companies/search', async (req, res) => { try { const { q } = req.query; if (!q) { return res.json([]); } const searchTerm = `%${q}%`; const result = await pool.query( `SELECT customer_id, name FROM customer_company WHERE valid_id = 1 AND ( customer_id ILIKE $1 OR name ILIKE $1 ) ORDER BY name LIMIT 20`, [searchTerm] ); res.json(result.rows); } catch (err) { console.error('Error searching customer companies:', err); res.status(500).json({ error: err.message }); } }); // GET /api/customer-users/search — Search customer users router.get('/customer-users/search', async (req, res) => { try { const { q, customer_company_id } = req.query; if (!q) { return res.json([]); } // Try OTRS API first if configured const OTRS_API_URL = process.env.OTRS_API_URL; const OTRS_API_USER = process.env.OTRS_API_USER; if (OTRS_API_URL && OTRS_API_USER) { try { const searchParams = { Search: `*${q}*`, Valid: 1 }; if (customer_company_id) { searchParams.CustomerID = customer_company_id; } const searchRes = await otrsRequest('POST', '/CustomerUserSearch', searchParams); let logins = []; if (searchRes) { if (Array.isArray(searchRes.CustomerUserID)) { logins = searchRes.CustomerUserID; } else if (searchRes.Data && Array.isArray(searchRes.Data.CustomerUserID)) { logins = searchRes.Data.CustomerUserID; } else if (Array.isArray(searchRes)) { logins = searchRes; } } if (logins.length > 0) { // Limit to top 20 logins to avoid rate/performance issues const limitedLogins = logins.slice(0, 20); const detailPromises = limitedLogins.map(async (login) => { try { const detailRes = await otrsRequest('POST', '/CustomerUserGet', { UserLogin: login }); const userObj = detailRes?.CustomerUser; if (userObj) { return { login: userObj.UserLogin || login, email: userObj.UserEmail || '', first_name: userObj.UserFirstname || '', last_name: userObj.UserLastname || '', customer_id: userObj.UserCustomerID || '' }; } } catch (err) { console.error(`Error fetching details for user ${login}:`, err.message); } return null; }); const details = await Promise.all(detailPromises); const validUsers = details.filter(u => u !== null); if (validUsers.length > 0) { return res.json(validUsers); } } } catch (apiErr) { console.warn('OTRS CustomerUserSearch API request failed, falling back to local DB:', apiErr.message); } } // Fallback: local DB query const searchTerm = `%${q}%`; let queryText = ` SELECT login, email, first_name, last_name, customer_id FROM customer_user WHERE valid_id = 1 AND ( login ILIKE $1 OR email ILIKE $1 OR first_name ILIKE $1 OR last_name ILIKE $1 ) `; const queryParams = [searchTerm]; if (customer_company_id) { queryText += ` AND customer_id = $2`; queryParams.push(customer_company_id); } queryText += ` ORDER BY last_name, first_name LIMIT 20`; const result = await pool.query(queryText, queryParams); res.json(result.rows); } catch (err) { console.error('Error searching customer users:', err); res.status(500).json({ error: err.message }); } }); // GET /api/agents/search — Search active agents router.get('/agents/search', async (req, res) => { try { const { q } = req.query; const searchTerm = q ? `%${q}%` : '%'; const result = await pool.query( `SELECT id, login, first_name, last_name FROM users WHERE valid_id = 1 AND ( login ILIKE $1 OR first_name ILIKE $1 OR last_name ILIKE $1 ) ORDER BY last_name, first_name LIMIT 20`, [searchTerm] ); res.json(result.rows); } catch (err) { console.error('Error searching agents:', err); res.status(500).json({ error: err.message }); } }); // GET /api/queues/search — Search active queues router.get('/queues/search', async (req, res) => { try { const { q } = req.query; const searchTerm = q ? `%${q}%` : '%'; const result = await pool.query( `SELECT id, name FROM queue WHERE valid_id = 1 AND name ILIKE $1 ORDER BY name LIMIT 20`, [searchTerm] ); res.json(result.rows); } catch (err) { console.error('Error searching queues:', err); res.status(500).json({ error: err.message }); } }); // GET /api/states/search — Search active ticket states router.get('/states/search', async (req, res) => { try { const { q } = req.query; const searchTerm = q ? `%${q}%` : '%'; const result = await pool.query( `SELECT id, name FROM ticket_state WHERE valid_id = 1 AND name ILIKE $1 ORDER BY name LIMIT 20`, [searchTerm] ); res.json(result.rows); } catch (err) { console.error('Error searching states:', err); res.status(500).json({ error: err.message }); } }); module.exports = router;