fix: corretto veramente ldap e d altro
This commit is contained in:
+109
-42
@@ -1,9 +1,23 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const pool = require('../db');
|
||||
const { db } = require('../activityDb');
|
||||
|
||||
// In-memory cache for customer users fetched from LDAP
|
||||
let ldapCustomerUsersCache = [];
|
||||
// Ensure SQLite tables for customer user cache exist in internal.db
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS customer_user_cache (
|
||||
login TEXT PRIMARY KEY,
|
||||
email TEXT,
|
||||
first_name TEXT,
|
||||
last_name TEXT,
|
||||
customer_id TEXT,
|
||||
phone TEXT
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS sync_status (
|
||||
key TEXT PRIMARY KEY,
|
||||
val TEXT
|
||||
);
|
||||
`);
|
||||
|
||||
// Helper for OTRS CE GenericInterface REST API calls
|
||||
async function otrsRequest(method, path, bodyData = {}) {
|
||||
@@ -37,7 +51,11 @@ async function otrsRequest(method, path, bodyData = {}) {
|
||||
const errorText = await response.text();
|
||||
throw new Error(`OTRS REST API error (${response.status}): ${errorText}`);
|
||||
}
|
||||
return await response.json();
|
||||
const result = await response.json();
|
||||
if (result && result.Error) {
|
||||
throw new Error(`OTRS API Error: ${result.Error.ErrorMessage} (${result.Error.ErrorCode})`);
|
||||
}
|
||||
return result;
|
||||
} catch (err) {
|
||||
clearTimeout(timeoutId);
|
||||
if (err.name === 'AbortError') {
|
||||
@@ -208,34 +226,41 @@ router.get('/customer-users/search', async (req, res) => {
|
||||
|
||||
// 1. Try to search via OTRS GenericInterface REST API if configured
|
||||
if (process.env.OTRS_API_URL && process.env.OTRS_API_USER) {
|
||||
// Search the in-memory cache first if it contains data
|
||||
if (ldapCustomerUsersCache.length > 0) {
|
||||
const searchTerms = q.toLowerCase().split(/\s+/).filter(Boolean);
|
||||
let matchedUsers = ldapCustomerUsersCache;
|
||||
|
||||
if (searchTerms.length > 0) {
|
||||
matchedUsers = matchedUsers.filter(u => {
|
||||
const login = (u.login || '').toLowerCase();
|
||||
const email = (u.email || '').toLowerCase();
|
||||
const first = (u.first_name || '').toLowerCase();
|
||||
const last = (u.last_name || '').toLowerCase();
|
||||
const company = (u.customer_id || '').toLowerCase();
|
||||
|
||||
return searchTerms.every(term =>
|
||||
login.includes(term) ||
|
||||
email.includes(term) ||
|
||||
first.includes(term) ||
|
||||
last.includes(term) ||
|
||||
company.includes(term)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
// Query the local SQLite cache table first
|
||||
const searchTerm = q ? `%${q}%` : '%';
|
||||
let rows;
|
||||
if (customer_company_id) {
|
||||
matchedUsers = matchedUsers.filter(u => u.customer_id === customer_company_id);
|
||||
rows = db.prepare(`
|
||||
SELECT login, email, first_name, last_name, customer_id
|
||||
FROM customer_user_cache
|
||||
WHERE customer_id = ? AND (
|
||||
login LIKE ? OR
|
||||
email LIKE ? OR
|
||||
first_name LIKE ? OR
|
||||
last_name LIKE ?
|
||||
)
|
||||
ORDER BY last_name, first_name
|
||||
LIMIT 20
|
||||
`).all(customer_company_id, searchTerm, searchTerm, searchTerm, searchTerm);
|
||||
} else {
|
||||
rows = db.prepare(`
|
||||
SELECT login, email, first_name, last_name, customer_id
|
||||
FROM customer_user_cache
|
||||
WHERE login LIKE ? OR
|
||||
email LIKE ? OR
|
||||
first_name LIKE ? OR
|
||||
last_name LIKE ?
|
||||
ORDER BY last_name, first_name
|
||||
LIMIT 20
|
||||
`).all(searchTerm, searchTerm, searchTerm, searchTerm);
|
||||
}
|
||||
|
||||
return res.json(matchedUsers.slice(0, 20));
|
||||
if (rows.length > 0) {
|
||||
return res.json(rows);
|
||||
}
|
||||
} catch (dbErr) {
|
||||
console.warn('[LDAP Cache Search] SQLite query failed, falling back to live API:', dbErr.message);
|
||||
}
|
||||
|
||||
// Fallback to live API search if cache is not yet populated
|
||||
@@ -396,7 +421,7 @@ router.get('/states/search', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Helper function to sync customer users from LDAP (OTRS API) into in-memory cache
|
||||
// Helper function to sync customer users from LDAP (OTRS API) into SQLite cache
|
||||
async function syncCustomerUsers() {
|
||||
if (!process.env.OTRS_API_URL || !process.env.OTRS_API_USER) {
|
||||
console.log('[LDAP Sync] OTRS API not configured, skipping customer user sync.');
|
||||
@@ -420,7 +445,20 @@ async function syncCustomerUsers() {
|
||||
console.log(`[LDAP Sync] Found ${logins.length} customer users. Fetching details...`);
|
||||
|
||||
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
|
||||
const newCache = [];
|
||||
|
||||
// Prepare SQLite insert statement with upsert logic
|
||||
const upsertStmt = db.prepare(`
|
||||
INSERT INTO customer_user_cache (login, email, first_name, last_name, customer_id, phone)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT(login) DO UPDATE SET
|
||||
email=excluded.email,
|
||||
first_name=excluded.first_name,
|
||||
last_name=excluded.last_name,
|
||||
customer_id=excluded.customer_id,
|
||||
phone=excluded.phone
|
||||
`);
|
||||
|
||||
let syncCount = 0;
|
||||
// Fetch details in batches of 3 to avoid overloading the OTRS CGI server
|
||||
const batchSize = 3;
|
||||
for (let i = 0; i < logins.length; i += batchSize) {
|
||||
@@ -433,13 +471,17 @@ async function syncCustomerUsers() {
|
||||
const getData = (getResult && getResult.Data) || getResult;
|
||||
if (getData && getData.CustomerUser) {
|
||||
const u = getData.CustomerUser;
|
||||
newCache.push({
|
||||
login: u.UserLogin,
|
||||
email: u.UserEmail || '',
|
||||
first_name: u.UserFirstname || '',
|
||||
last_name: u.UserLastname || '',
|
||||
customer_id: u.UserCustomerID || ''
|
||||
});
|
||||
|
||||
// Run insertion in SQLite cache table
|
||||
upsertStmt.run(
|
||||
u.UserLogin,
|
||||
u.UserEmail || '',
|
||||
u.UserFirstname || '',
|
||||
u.UserLastname || '',
|
||||
u.UserCustomerID || '',
|
||||
u.UserPhone || ''
|
||||
);
|
||||
syncCount++;
|
||||
}
|
||||
break; // Success, break retry loop
|
||||
} catch (getErr) {
|
||||
@@ -456,9 +498,15 @@ async function syncCustomerUsers() {
|
||||
await delay(50);
|
||||
}
|
||||
|
||||
ldapCustomerUsersCache = newCache;
|
||||
console.log(`[LDAP Sync] Completed. Synced ${ldapCustomerUsersCache.length} users to in-memory cache.`);
|
||||
return ldapCustomerUsersCache.length;
|
||||
// Update last sync timestamp in SQLite sync_status table
|
||||
db.prepare(`
|
||||
INSERT INTO sync_status (key, val)
|
||||
VALUES ('last_ldap_sync_time', ?)
|
||||
ON CONFLICT(key) DO UPDATE SET val=excluded.val
|
||||
`).run(new Date().toISOString());
|
||||
|
||||
console.log(`[LDAP Sync] Completed. Synced ${syncCount} users to SQLite customer_user_cache.`);
|
||||
return syncCount;
|
||||
} catch (err) {
|
||||
console.error('[LDAP Sync] Error during customer user sync:', err);
|
||||
throw err;
|
||||
@@ -471,13 +519,32 @@ router.post('/customer-users/sync', async (req, res) => {
|
||||
const count = await syncCustomerUsers();
|
||||
res.json({ message: `Sincronizzazione completata! ${count} utenti sincronizzati.`, count });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: 'Errore durante la sincronizzazione', message: err.message });
|
||||
console.warn('[LDAP Sync] Sincronizzazione non riuscita:', err.message);
|
||||
res.json({ message: `Sincronizzazione LDAP ignorata o non disponibile: ${err.message}`, count: 0 });
|
||||
}
|
||||
});
|
||||
|
||||
// Run LDAP synchronization in the background on startup (after 5 seconds)
|
||||
// Run LDAP synchronization on startup if last sync was > LDAP_SYNC_INTERVAL_HOURS ago
|
||||
setTimeout(() => {
|
||||
try {
|
||||
const row = db.prepare("SELECT val FROM sync_status WHERE key = 'last_ldap_sync_time'").get();
|
||||
let shouldSync = true;
|
||||
if (row && row.val) {
|
||||
const lastSync = new Date(row.val);
|
||||
const intervalHours = parseFloat(process.env.LDAP_SYNC_INTERVAL_HOURS) || 24;
|
||||
const thresholdTime = new Date(Date.now() - intervalHours * 60 * 60 * 1000);
|
||||
if (lastSync > thresholdTime) {
|
||||
shouldSync = false;
|
||||
console.log(`[LDAP Sync] Last sync was on ${lastSync.toLocaleString()} (Threshold: ${intervalHours}h). Skipping auto-sync at startup.`);
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldSync) {
|
||||
syncCustomerUsers().catch(err => console.error('Startup LDAP sync failed:', err));
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[LDAP Sync] Failed to check LDAP sync status:', err.message);
|
||||
}
|
||||
}, 5000);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
+101
-7
@@ -1,7 +1,7 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const pool = require('../db');
|
||||
const { logAttivita } = require('../activityDb');
|
||||
const { db, logAttivita } = require('../activityDb');
|
||||
|
||||
// Helper: resolve agent name from DB (best-effort, non-blocking)
|
||||
async function resolveAgentName(agentId) {
|
||||
@@ -36,7 +36,7 @@ async function otrsRequest(method, path, bodyData = {}) {
|
||||
};
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), 6000);
|
||||
const timeoutId = setTimeout(() => controller.abort(), 15000);
|
||||
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
@@ -51,11 +51,15 @@ async function otrsRequest(method, path, bodyData = {}) {
|
||||
const errorText = await response.text();
|
||||
throw new Error(`OTRS REST API error (${response.status}): ${errorText}`);
|
||||
}
|
||||
return await response.json();
|
||||
const result = await response.json();
|
||||
if (result && result.Error) {
|
||||
throw new Error(`OTRS API Error: ${result.Error.ErrorMessage} (${result.Error.ErrorCode})`);
|
||||
}
|
||||
return result;
|
||||
} catch (err) {
|
||||
clearTimeout(timeoutId);
|
||||
if (err.name === 'AbortError') {
|
||||
throw new Error('OTRS REST API request timed out (6s limit exceeded)');
|
||||
throw new Error('OTRS REST API request timed out (15s limit exceeded)');
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
@@ -258,6 +262,62 @@ router.get('/:id', async (req, res) => {
|
||||
return res.status(404).json({ error: 'Ticket not found' });
|
||||
}
|
||||
|
||||
const ticket = ticketResult.rows[0];
|
||||
if (ticket.customer_user_id && !ticket.customer_first && process.env.OTRS_API_URL && process.env.OTRS_API_USER) {
|
||||
try {
|
||||
// 1. Try to read from local SQLite cache in internal.db
|
||||
let cachedUser = null;
|
||||
try {
|
||||
cachedUser = db.prepare('SELECT email, first_name, last_name, phone FROM customer_user_cache WHERE login = ?').get(ticket.customer_user_id);
|
||||
} catch (dbErr) {
|
||||
console.warn('[LDAP Cache Detail] SQLite cache read failed:', dbErr.message);
|
||||
}
|
||||
|
||||
if (cachedUser) {
|
||||
ticket.customer_first = cachedUser.first_name || '';
|
||||
ticket.customer_last = cachedUser.last_name || '';
|
||||
ticket.customer_email = cachedUser.email || '';
|
||||
ticket.customer_phone = cachedUser.phone || '';
|
||||
} else {
|
||||
// 2. Fallback to live API fetch
|
||||
const getResult = await otrsRequest('POST', '/CustomerUserGet', { UserLogin: ticket.customer_user_id });
|
||||
const getData = (getResult && getResult.Data) || getResult;
|
||||
if (getData && getData.CustomerUser) {
|
||||
const u = getData.CustomerUser;
|
||||
ticket.customer_first = u.UserFirstname || '';
|
||||
ticket.customer_last = u.UserLastname || '';
|
||||
ticket.customer_email = u.UserEmail || '';
|
||||
ticket.customer_phone = u.UserPhone || '';
|
||||
|
||||
// 3. Store in SQLite cache for subsequent detail queries
|
||||
try {
|
||||
db.prepare(`
|
||||
INSERT INTO customer_user_cache (login, email, first_name, last_name, customer_id, phone)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT(login) DO UPDATE SET
|
||||
email=excluded.email,
|
||||
first_name=excluded.first_name,
|
||||
last_name=excluded.last_name,
|
||||
customer_id=excluded.customer_id,
|
||||
phone=excluded.phone
|
||||
`).run(
|
||||
u.UserLogin,
|
||||
u.UserEmail || '',
|
||||
u.UserFirstname || '',
|
||||
u.UserLastname || '',
|
||||
u.UserCustomerID || '',
|
||||
u.UserPhone || ''
|
||||
);
|
||||
} catch (cacheErr) {
|
||||
console.warn('[LDAP Cache Detail] Failed to write fetched user to SQLite cache:', cacheErr.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (apiErr) {
|
||||
console.warn(`Failed to fetch LDAP customer details for detail view:`, apiErr.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch articles
|
||||
const articlesResult = await pool.query(
|
||||
`SELECT
|
||||
@@ -613,7 +673,7 @@ router.patch('/:id', async (req, res) => {
|
||||
}
|
||||
|
||||
// 1. Try to update via REST API if configured
|
||||
if (process.env.OTRS_API_URL && process.env.OTRS_API_USER) {
|
||||
if (process.env.OTRS_API_URL && process.env.OTRS_API_USER && process.env.FORCE_DB_UPDATE !== 'true') {
|
||||
try {
|
||||
const ticketFields = {};
|
||||
if (updates.ticket_state_id !== undefined) ticketFields.StateID = updates.ticket_state_id;
|
||||
@@ -664,6 +724,23 @@ router.patch('/:id', async (req, res) => {
|
||||
};
|
||||
}
|
||||
const result = await otrsRequest('PATCH', `/Ticket/${id}`, reqBody);
|
||||
|
||||
// Option 1: Log the time directly to the DB if the API succeeded but OTRS didn't save it
|
||||
if (!isNaN(timeUnit) && timeUnit > 0 && result && result.ArticleID) {
|
||||
try {
|
||||
await pool.query(
|
||||
`INSERT INTO time_accounting (
|
||||
ticket_id, article_id, time_unit,
|
||||
create_time, create_by, change_time, change_by
|
||||
) VALUES ($1, $2, $3, NOW(), $4, NOW(), $4)`,
|
||||
[id, result.ArticleID, timeUnit, operatorId]
|
||||
);
|
||||
console.log(`[Time Accounting] Successfully logged ${timeUnit} minutes for ticket ${id} via DB insert.`);
|
||||
} catch (timeDbErr) {
|
||||
console.error('[Time Accounting] Failed to log time unit in database:', timeDbErr.message);
|
||||
}
|
||||
}
|
||||
|
||||
const isClosing = updates.ticket_state_id && current.ticket_state_id !== updates.ticket_state_id;
|
||||
resolveAgentName(operatorId).then(agente_nome => {
|
||||
logAttivita({
|
||||
@@ -942,7 +1019,7 @@ router.post('/:id/articles', async (req, res) => {
|
||||
const contentType = isHtml ? 'text/html; charset=utf-8' : 'text/plain; charset=utf-8';
|
||||
|
||||
// 1. Try to add note via REST API if configured
|
||||
if (process.env.OTRS_API_URL && process.env.OTRS_API_USER) {
|
||||
if (process.env.OTRS_API_URL && process.env.OTRS_API_USER && process.env.FORCE_DB_UPDATE !== 'true') {
|
||||
try {
|
||||
const payload = {
|
||||
Article: {
|
||||
@@ -969,6 +1046,23 @@ router.post('/:id/articles', async (req, res) => {
|
||||
}
|
||||
|
||||
const result = await otrsRequest('PATCH', `/Ticket/${id}`, payload);
|
||||
|
||||
// Option 1: Log the time directly to the DB if the API succeeded but OTRS didn't save it
|
||||
if (time_unit && result && result.ArticleID) {
|
||||
try {
|
||||
await pool.query(
|
||||
`INSERT INTO time_accounting (
|
||||
ticket_id, article_id, time_unit,
|
||||
create_time, create_by, change_time, change_by
|
||||
) VALUES ($1, $2, $3, NOW(), $4, NOW(), $4)`,
|
||||
[id, result.ArticleID, parseFloat(time_unit), operatorId]
|
||||
);
|
||||
console.log(`[Time Accounting] Successfully logged ${time_unit} minutes for ticket ${id} via DB insert.`);
|
||||
} catch (timeDbErr) {
|
||||
console.error('[Time Accounting] Failed to log time unit in database:', timeDbErr.message);
|
||||
}
|
||||
}
|
||||
|
||||
resolveAgentName(operatorId).then(agente_nome => {
|
||||
logAttivita({
|
||||
agente_id: operatorId,
|
||||
@@ -1179,7 +1273,7 @@ router.patch('/batch/update', async (req, res) => {
|
||||
}
|
||||
|
||||
// 1. Try to update via REST API if configured
|
||||
if (process.env.OTRS_API_URL && process.env.OTRS_API_USER) {
|
||||
if (process.env.OTRS_API_URL && process.env.OTRS_API_USER && process.env.FORCE_DB_UPDATE !== 'true') {
|
||||
try {
|
||||
const ticketFields = {};
|
||||
if (updates.ticket_state_id !== undefined) ticketFields.StateID = updates.ticket_state_id;
|
||||
|
||||
Reference in New Issue
Block a user