'use strict' // Journal d'audit d'assignation des Dispatch Jobs : CHAQUE changement horodaté (assigné / réassigné / reflété de F / // retour au pool / complété / annulé), avec l'ACTEUR (courriel Authentik, ou « F » pour le legacy) et la SOURCE. // Stockage append-only JSONL dans /app/data (bind-monté → persiste, comme user_prefs.json). Zéro schéma ERPNext. const fs = require('fs') const path = require('path') const { log } = require('./helpers') const DIR = '/app/data' const FILE = path.join(DIR, 'dispatch_audit.jsonl') // Enregistre un événement d'assignation. Fire-and-forget : ne bloque JAMAIS la synchro même si l'écriture échoue. function record (entry) { try { if (!entry || !entry.job) return const line = JSON.stringify({ ts: new Date().toISOString(), job: String(entry.job), ticket: String(entry.ticket || ''), event: entry.event || 'change', // assigned | reassigned | mirror-assigned | returned-to-pool | completed | cancelled | backfill from: entry.from || '', to: entry.to || '', actor: entry.actor || 'system', // courriel Authentik · 'F' (legacy) · 'system' source: entry.source || '', // ops-push | F-watch | ops-return | backfill }) + '\n' try { fs.mkdirSync(DIR, { recursive: true }) } catch (e) {} fs.appendFile(FILE, line, () => {}) } catch (e) { try { log('dispatch-audit record error: ' + e.message) } catch (x) {} } } // Lecture filtrée (par job et/ou tech et/ou plage ISO), du plus RÉCENT au plus ancien, plafonnée à `limit`. function read ({ job, tech, from, to, limit = 500 } = {}) { let raw = '' try { raw = fs.readFileSync(FILE, 'utf8') } catch (e) { return [] } const lines = raw.split('\n'); const out = [] for (let i = lines.length - 1; i >= 0 && out.length < limit; i--) { const l = lines[i].trim(); if (!l) continue let e; try { e = JSON.parse(l) } catch (x) { continue } if (job && e.job !== job) continue if (tech && e.to !== tech && e.from !== tech) continue if (from && e.ts < from) continue if (to && e.ts > to) continue out.push(e) } return out } module.exports = { record, read }