712 lines
22 KiB
JavaScript
712 lines
22 KiB
JavaScript
const express = require('express');
|
|
const router = express.Router();
|
|
const pool = require('../db');
|
|
const { db } = require('../activityDb');
|
|
|
|
// 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 = {}) {
|
|
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(), 15000);
|
|
|
|
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}`);
|
|
}
|
|
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 (15s 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', async (req, res) => {
|
|
const agentId = parseInt(req.headers['x-agent-id'], 10) || 1;
|
|
let agentEmail = '';
|
|
try {
|
|
const prefRes = await pool.query(
|
|
`SELECT preferences_value FROM user_preferences WHERE user_id = $1 AND preferences_key = 'UserEmail'`,
|
|
[agentId]
|
|
);
|
|
if (prefRes.rows.length > 0) {
|
|
agentEmail = prefRes.rows[0].preferences_value || '';
|
|
}
|
|
} catch (_) {}
|
|
|
|
res.json({
|
|
defaultAgentLogin: process.env.OTRS_API_USER || '',
|
|
dailyTargetTime: parseInt(process.env.DAILY_TARGET_TIME, 10) || 480,
|
|
phraseThreshold: parseInt(process.env.PHRASE_THRESHOLD, 10) || 70,
|
|
autoTimeMinHour: process.env.AUTO_TIME_MIN_HOUR || '18:00',
|
|
helpdeskEmail: process.env.OTRS_MAIL_BCC || '',
|
|
agentEmail: agentEmail
|
|
});
|
|
});
|
|
|
|
// 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;
|
|
|
|
// 1. Fetch from local SQLite LDAP cache
|
|
let localRows = [];
|
|
try {
|
|
if (!q) {
|
|
localRows = db.prepare(`
|
|
SELECT DISTINCT customer_id AS customer_id, customer_id AS name
|
|
FROM customer_user_cache
|
|
WHERE customer_id IS NOT NULL AND customer_id != ''
|
|
ORDER BY customer_id
|
|
LIMIT 500
|
|
`).all();
|
|
} else {
|
|
const searchTerm = `%${q}%`;
|
|
localRows = db.prepare(`
|
|
SELECT DISTINCT customer_id AS customer_id, customer_id AS name
|
|
FROM customer_user_cache
|
|
WHERE customer_id IS NOT NULL AND customer_id != '' AND customer_id LIKE ?
|
|
ORDER BY customer_id
|
|
LIMIT 500
|
|
`).all(searchTerm);
|
|
}
|
|
} catch (e) {
|
|
console.warn('Failed to query local customer cache:', e.message);
|
|
}
|
|
|
|
// 2. Fetch from OTRS Postgres DB
|
|
let dbRows = [];
|
|
try {
|
|
if (!q) {
|
|
const result = await pool.query(
|
|
`SELECT customer_id, name
|
|
FROM customer_company
|
|
WHERE valid_id = 1
|
|
ORDER BY name
|
|
LIMIT 500`
|
|
);
|
|
dbRows = result.rows;
|
|
} else {
|
|
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 500`,
|
|
[searchTerm]
|
|
);
|
|
dbRows = result.rows;
|
|
}
|
|
} catch (e) {
|
|
console.warn('Failed to query OTRS customer_company table:', e.message);
|
|
}
|
|
|
|
// 3. Merge results and remove duplicates by customer_id
|
|
const seen = new Set();
|
|
const merged = [];
|
|
|
|
// Prioritize OTRS database rows (which might have better names)
|
|
for (const row of dbRows) {
|
|
const cid = String(row.customer_id).trim();
|
|
if (cid && !seen.has(cid.toLowerCase())) {
|
|
seen.add(cid.toLowerCase());
|
|
merged.push({ customer_id: cid, name: row.name || cid });
|
|
}
|
|
}
|
|
|
|
// Add local LDAP rows
|
|
for (const row of localRows) {
|
|
const cid = String(row.customer_id).trim();
|
|
if (cid && !seen.has(cid.toLowerCase())) {
|
|
seen.add(cid.toLowerCase());
|
|
merged.push({ customer_id: cid, name: row.name || cid });
|
|
}
|
|
}
|
|
|
|
// Sort alphabetically by name
|
|
merged.sort((a, b) => a.name.localeCompare(b.name, 'it', { sensitivity: 'base' }));
|
|
|
|
res.json(merged.slice(0, 500));
|
|
} catch (err) {
|
|
console.error('Error searching customer companies:', err);
|
|
res.status(500).json({ error: err.message });
|
|
}
|
|
});
|
|
|
|
// GET /api/customer-users/search — Search customer users (supports LDAP via REST, fallbacks to DB)
|
|
router.get('/customer-users/search', async (req, res) => {
|
|
const { q = '', customer_company_id } = req.query;
|
|
|
|
// If q is empty, we return a merged list for populating filter dropdowns
|
|
if (!q) {
|
|
let localRows = [];
|
|
try {
|
|
if (customer_company_id) {
|
|
localRows = db.prepare(`
|
|
SELECT login, email, first_name, last_name, customer_id
|
|
FROM customer_user_cache
|
|
WHERE customer_id = ?
|
|
ORDER BY last_name, first_name
|
|
LIMIT 1000
|
|
`).all(customer_company_id);
|
|
} else {
|
|
localRows = db.prepare(`
|
|
SELECT login, email, first_name, last_name, customer_id
|
|
FROM customer_user_cache
|
|
ORDER BY last_name, first_name
|
|
LIMIT 1000
|
|
`).all();
|
|
}
|
|
} catch (e) {
|
|
console.warn('Failed to query local customer user cache:', e.message);
|
|
}
|
|
|
|
let dbRows = [];
|
|
try {
|
|
let queryText = `
|
|
SELECT login, email, first_name, last_name, customer_id
|
|
FROM customer_user
|
|
WHERE valid_id = 1
|
|
`;
|
|
let queryParams = [];
|
|
if (customer_company_id) {
|
|
queryText += ` AND customer_id = $1`;
|
|
queryParams.push(customer_company_id);
|
|
}
|
|
queryText += ` ORDER BY last_name, first_name LIMIT 1000`;
|
|
|
|
const result = await pool.query(queryText, queryParams);
|
|
dbRows = result.rows;
|
|
} catch (e) {
|
|
console.warn('Failed to query OTRS customer_user table:', e.message);
|
|
}
|
|
|
|
// Merge and deduplicate by login
|
|
const seen = new Set();
|
|
const merged = [];
|
|
|
|
for (const row of dbRows) {
|
|
const login = String(row.login).trim();
|
|
if (login && !seen.has(login.toLowerCase())) {
|
|
seen.add(login.toLowerCase());
|
|
merged.push({
|
|
login,
|
|
email: row.email || '',
|
|
first_name: row.first_name || '',
|
|
last_name: row.last_name || '',
|
|
customer_id: row.customer_id || ''
|
|
});
|
|
}
|
|
}
|
|
|
|
for (const row of localRows) {
|
|
const login = String(row.login).trim();
|
|
if (login && !seen.has(login.toLowerCase())) {
|
|
seen.add(login.toLowerCase());
|
|
merged.push({
|
|
login,
|
|
email: row.email || '',
|
|
first_name: row.first_name || '',
|
|
last_name: row.last_name || '',
|
|
customer_id: row.customer_id || ''
|
|
});
|
|
}
|
|
}
|
|
|
|
// Sort alphabetically by last name, first name
|
|
merged.sort((a, b) => {
|
|
const nameA = `${a.last_name} ${a.first_name}`.trim();
|
|
const nameB = `${b.last_name} ${b.first_name}`.trim();
|
|
return nameA.localeCompare(nameB, 'it', { sensitivity: 'base' });
|
|
});
|
|
|
|
return res.json(merged.slice(0, 1000));
|
|
}
|
|
|
|
// 1. Try to search via OTRS GenericInterface REST API if configured
|
|
if (process.env.OTRS_API_URL && process.env.OTRS_API_USER) {
|
|
try {
|
|
// Query the local SQLite cache table first
|
|
const searchTerm = q ? `%${q}%` : '%';
|
|
let rows;
|
|
if (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);
|
|
}
|
|
|
|
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
|
|
try {
|
|
const searchPattern = q ? `*${q}*` : '*';
|
|
const searchPayload = {
|
|
Search: searchPattern,
|
|
Valid: 1
|
|
};
|
|
if (customer_company_id) {
|
|
searchPayload.CustomerID = customer_company_id;
|
|
}
|
|
|
|
const searchResult = await otrsRequest('POST', '/CustomerUserSearch', searchPayload);
|
|
|
|
const searchData = (searchResult && searchResult.Data) || searchResult;
|
|
if (searchData && searchData.CustomerUserID) {
|
|
let logins = searchData.CustomerUserID;
|
|
if (!Array.isArray(logins)) {
|
|
logins = [logins];
|
|
}
|
|
logins = logins.slice(0, 20); // limit to 20 results
|
|
|
|
// Fetch details for each login in parallel
|
|
const detailsPromises = logins.map(async (login) => {
|
|
try {
|
|
const getResult = await otrsRequest('POST', '/CustomerUserGet', { UserLogin: login });
|
|
const getData = (getResult && getResult.Data) || getResult;
|
|
if (getData && getData.CustomerUser) {
|
|
const u = getData.CustomerUser;
|
|
return {
|
|
login: u.UserLogin,
|
|
email: u.UserEmail || '',
|
|
first_name: u.UserFirstname || '',
|
|
last_name: u.UserLastname || '',
|
|
customer_id: u.UserCustomerID || ''
|
|
};
|
|
}
|
|
} catch (getErr) {
|
|
console.warn(`Failed to fetch details for customer user ${login}:`, getErr.message);
|
|
}
|
|
return null;
|
|
});
|
|
|
|
const users = (await Promise.all(detailsPromises)).filter(u => u !== null);
|
|
return res.json(users);
|
|
}
|
|
} catch (restErr) {
|
|
console.warn('Failed to search customer users via REST API, falling back to database query:', restErr.message);
|
|
}
|
|
}
|
|
|
|
// 2. Fallback to local DB query
|
|
try {
|
|
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 });
|
|
}
|
|
});
|
|
|
|
// 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.');
|
|
return 0;
|
|
}
|
|
|
|
console.log('[LDAP Sync] Starting customer user synchronization...');
|
|
try {
|
|
const searchResult = await otrsRequest('POST', '/CustomerUserSearch', { Search: '*', Valid: 1 });
|
|
const searchData = (searchResult && searchResult.Data) || searchResult;
|
|
if (!searchData || !searchData.CustomerUserID) {
|
|
console.log('[LDAP Sync] No customer users found to sync.');
|
|
return 0;
|
|
}
|
|
|
|
let logins = searchData.CustomerUserID;
|
|
if (!Array.isArray(logins)) {
|
|
logins = [logins];
|
|
}
|
|
|
|
console.log(`[LDAP Sync] Found ${logins.length} customer users. Fetching details...`);
|
|
|
|
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
|
|
|
|
// 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) {
|
|
const batchLogins = logins.slice(i, i + batchSize);
|
|
await Promise.all(batchLogins.map(async (login) => {
|
|
let retries = 2;
|
|
while (retries >= 0) {
|
|
try {
|
|
const getResult = await otrsRequest('POST', '/CustomerUserGet', { UserLogin: login });
|
|
const getData = (getResult && getResult.Data) || getResult;
|
|
if (getData && getData.CustomerUser) {
|
|
const u = getData.CustomerUser;
|
|
|
|
// 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) {
|
|
if (retries === 0) {
|
|
console.warn(`[LDAP Sync] Failed to sync customer user ${login} after retries:`, getErr.message);
|
|
} else {
|
|
await delay(200); // Wait 200ms before retrying
|
|
}
|
|
retries--;
|
|
}
|
|
}
|
|
}));
|
|
// Add a small delay between batches
|
|
await delay(50);
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
// POST /api/customer-users/sync — Sync customer users from LDAP
|
|
router.post('/customer-users/sync', async (req, res) => {
|
|
try {
|
|
const count = await syncCustomerUsers();
|
|
res.json({ message: `Sincronizzazione completata! ${count} utenti sincronizzati.`, count });
|
|
} catch (err) {
|
|
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 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;
|