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 controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 6000); try { const response = await fetch(url, { method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), signal: controller.signal }); clearTimeout(timeoutId); if (!response.ok) { const errorText = await response.text(); throw new Error(`OTRS REST API error (${response.status}): ${errorText}`); } return await response.json(); } catch (err) { clearTimeout(timeoutId); if (err.name === 'AbortError') { throw new Error('OTRS REST API request timed out (6s limit exceeded)'); } throw err; } } // 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/config — Application config router.get('/config', (req, res) => { res.json({ defaultAgentLogin: process.env.OTRS_API_USER || '', dailyTargetTime: parseInt(process.env.DAILY_TARGET_TIME, 10) || 480 }); }); // GET /api/users/time-today — Get sum of today's time units for active agent router.get('/users/time-today', async (req, res) => { try { const operatorId = parseInt(req.headers['x-agent-id'], 10) || 1; const result = await pool.query( `SELECT COALESCE(SUM(time_unit), 0) AS total_today FROM time_accounting WHERE create_by = $1 AND DATE(create_time) = CURRENT_DATE`, [operatorId] ); res.json({ totalToday: parseFloat(result.rows[0].total_today) }); } catch (err) { console.error('Error fetching today\'s time units:', 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; let result; if (!q) { result = await pool.query( `SELECT customer_id, name FROM customer_company WHERE valid_id = 1 ORDER BY name LIMIT 20` ); } else { const searchTerm = `%${q}%`; 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 (always from local DB) router.get('/customer-users/search', async (req, res) => { try { const { q = '', customer_company_id } = req.query; let queryText; let queryParams; if (q) { const searchTerm = `%${q}%`; 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 ) `; queryParams = [searchTerm]; if (customer_company_id) { queryText += ` AND customer_id = $2`; queryParams.push(customer_company_id); } } else { queryText = ` SELECT login, email, first_name, last_name, customer_id FROM customer_user WHERE valid_id = 1 `; queryParams = []; if (customer_company_id) { queryText += ` AND customer_id = $1`; 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;