const fs = require('fs'); const path = require('path'); // Load environment variables if .env exists try { const dotenvPath = path.resolve(__dirname, '../.env'); if (fs.existsSync(dotenvPath)) { require('dotenv').config({ path: dotenvPath }); } } catch (err) {} const port = process.env.PORT || 3000; const baseUrl = `http://localhost:${port}`; // Parse command line arguments const args = process.argv.slice(2); if (args.length < 2) { console.log('\nUso: node scripts/bulk-injector.js \n'); console.log('Esempio: node scripts/bulk-injector.js tickets-sample.csv 5\n'); process.exit(1); } const csvPath = path.resolve(args[0]); const intervalSeconds = parseFloat(args[1]); if (!fs.existsSync(csvPath)) { console.error(`Errore: il file CSV non esiste al percorso: ${csvPath}`); process.exit(1); } if (isNaN(intervalSeconds) || intervalSeconds <= 0) { console.error('Errore: specificare un intervallo in secondi valido (maggiore di 0)'); process.exit(1); } // Read and parse CSV const content = fs.readFileSync(csvPath, 'utf-8'); const lines = content.split(/\r?\n/).map(line => line.trim()).filter(line => line.length > 0); if (lines.length < 2) { console.error('Errore: il file CSV deve contenere almeno l\'intestazione e una riga di dati.'); process.exit(1); } const headerLine = lines[0]; const delimiter = headerLine.includes(';') ? ';' : ','; const headers = headerLine.split(delimiter).map(h => h.trim().replace(/^["']|["']$/g, '')); const rows = []; for (let i = 1; i < lines.length; i++) { const cols = lines[i].split(delimiter).map(c => c.trim().replace(/^["']|["']$/g, '')); const row = {}; headers.forEach((header, idx) => { row[header] = cols[idx] || ''; }); rows.push(row); } // Start execution flow async function start() { console.log(`\n============================================================`); console.log(`⚙️ OTRS Turbo - CSV Bulk Injector`); console.log(`============================================================`); console.log(`📂 File CSV caricato: ${path.basename(csvPath)} (${rows.length} righe)`); console.log(`⏱️ Intervallo: ${intervalSeconds} secondi`); console.log(`🌐 Server target: ${baseUrl}`); // Fetch ticket types lookup let ticketTypes = []; try { const typesRes = await fetch(`${baseUrl}/api/types`); if (typesRes.ok) { ticketTypes = await typesRes.json(); console.log(`📊 Tipi ticket caricati dal server: ${ticketTypes.length}`); } } catch (err) { console.warn(`⚠️ Impossibile pre-caricare i tipi di ticket (verrà usato l'ID diretto): ${err.message}`); } console.log(`ℹ️ Premi Ctrl+C per arrestare il processo.`); console.log(`============================================================\n`); let currentIndex = 0; async function injectNext() { const row = rows[currentIndex]; console.log(`[${new Date().toLocaleTimeString()}] Invio riga ${currentIndex + 1}/${rows.length}: "${row.title || 'Senza titolo'}"...`); // Determine type_id from CSV 'type' or 'type_id' let type_id = undefined; if (row.type) { const parsedId = parseInt(row.type, 10); if (!isNaN(parsedId)) { type_id = parsedId; } else { const found = ticketTypes.find(t => t.name.toLowerCase() === row.type.toLowerCase()); if (found) { type_id = found.id; } } } else if (row.type_id) { type_id = parseInt(row.type_id, 10); } // Build payload const payload = { title: row.title || 'Ticket da CSV', queue_id: row.queue_id ? parseInt(row.queue_id, 10) : 1, state_id: row.state_id ? parseInt(row.state_id, 10) : 1, priority_id: row.priority_id ? parseInt(row.priority_id, 10) : 3, type_id, user_id: row.user_id ? parseInt(row.user_id, 10) : undefined, responsible_user_id: row.responsible_user_id ? parseInt(row.responsible_user_id, 10) : undefined, customer_id: row.customer_id || undefined, customer_user_id: row.customer_user_id || undefined, subject: row.subject || undefined, body: row.body || undefined }; try { const response = await fetch(`${baseUrl}/api/tickets`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-agent-id': '1' }, body: JSON.stringify(payload) }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${await response.text()}`); } const resData = await response.json(); const ticketId = resData.id; const tn = resData.tn; console.log(` -> Successo: Creato Ticket #${tn} (ID: ${ticketId})`); // Check for create_time retrodating if (row.create_time) { console.log(` -> Richiesta retrodatazione a: ${row.create_time}`); // 1. Retrodate ticket const ticketRetRes = await fetch(`${baseUrl}/api/tickets/${ticketId}/retrodata-ticket`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ create_time: row.create_time }) }); if (ticketRetRes.ok) { console.log(` -> Ticket retrodatato.`); } else { console.error(` -> Errore retrodatazione ticket: ${await ticketRetRes.text()}`); } // 2. Fetch articles to retrodate const detailRes = await fetch(`${baseUrl}/api/tickets/${ticketId}`); if (detailRes.ok) { const detail = await detailRes.json(); const articles = detail.articles || []; for (const art of articles) { const artRetRes = await fetch(`${baseUrl}/api/tickets/articles/${art.article_id}/retrodata-article`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ create_time: row.create_time }) }); if (artRetRes.ok) { console.log(` -> Articolo ID ${art.article_id} retrodatato.`); } else { console.error(` -> Errore retrodatazione articolo ID ${art.article_id}: ${await artRetRes.text()}`); } } } } } catch (err) { console.error(` ❌ Errore iniezione riga: ${err.message}`); } // Cycle to next row currentIndex = (currentIndex + 1) % rows.length; } // Start immediately and schedule next executions injectNext(); setInterval(injectNext, intervalSeconds * 1000); } start();