tool per ticket automatici

This commit is contained in:
2026-07-05 23:33:52 +02:00
parent fb67a277e5
commit e55cccbaa5
7 changed files with 400 additions and 10 deletions
+188
View File
@@ -0,0 +1,188 @@
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 <file-csv> <intervallo-secondi>\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();