import readline from "readline"; import fs from "fs"; import dotenv from "dotenv"; dotenv.config(); const user = process.argv[2]; // =============================== // BASE_URL DAL .env // =============================== const BASE_URL = process.env.BASE_URL; // es: https://prova.patachina.it if (!BASE_URL) { console.error("ERRORE: BASE_URL non definita in .env"); process.exit(1); } // =============================== // CREDENZIALI ADMIN // =============================== const adminSecret = JSON.parse(fs.readFileSync("./api_v1/admin_secret.json", "utf8")); let adminToken = null; let isRefreshing = false; // =============================== // LOGIN ADMIN (fetch come frontend) // =============================== async function loginAdmin() { const res = await fetch(`${BASE_URL}/auth/login`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email: adminSecret.email, password: adminSecret.password }) }); if (!res.ok) { console.error("Errore login Admin:", await res.text()); throw new Error("Login fallito"); } const data = await res.json(); adminToken = data.token; console.log("Watcher autenticato come Admin"); } // =============================== // GARANTISCE TOKEN VALIDO // =============================== async function ensureAdminToken() { if (adminToken) return; if (isRefreshing) { return new Promise(resolve => { const interval = setInterval(() => { if (!isRefreshing) { clearInterval(interval); resolve(); } }, 100); }); } isRefreshing = true; await loginAdmin(); isRefreshing = false; } // =============================== // CHIAMATA A /auto_scan (fetch come frontend) // =============================== async function callAutoScan(type, file, path, user) { await ensureAdminToken(); const res = await fetch(`${BASE_URL}/api_v1/auto_scan`, { method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Bearer " + adminToken }, body: JSON.stringify({ type, file, path, user }) }); if (res.status === 401) { console.log("Token scaduto, rinnovo…"); adminToken = null; return callAutoScan(type, file, path, user); } return res.ok; } // =============================== // ESTENSIONI MEDIA // =============================== const photoExt = [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".tiff", ".heic", ".heif"]; const videoExt = [".mp4", ".mov", ".avi", ".mkv", ".webm", ".m4v"]; function isMediaFile(filename) { const lower = filename.toLowerCase(); return photoExt.some(ext => lower.endsWith(ext)) || videoExt.some(ext => lower.endsWith(ext)); } console.log(`Node attivo per utente: ${user}`); const rl = readline.createInterface({ input: process.stdin, crlfDelay: Infinity }); // =============================== // LOGICA EVENTI // =============================== function startWatcher() { rl.on("line", line => { const parts = line.trim().split(/\s+/); const path = parts[0]; const action = parts[1]; const file = parts[2]; if (!file) return; const isDir = action.includes("ISDIR"); if (!isDir && !isMediaFile(file)) { console.log(`Ignorato (non media): ${file}`); return; } let type = null; if (action === "MOVED_TO,ISDIR") type = "ADD_DIR"; else if (action === "MOVED_FROM,ISDIR") type = "DEL_DIR"; else if (/^CLOSE_WRITE(,CLOSE)?$/.test(action)) type = "ADD"; else if (action === "MOVED_TO") type = "ADD"; else if (action === "MOVED_FROM") type = "DEL"; else if (action === "DELETE") type = "DEL"; else return; console.log(`${type} ${file} ${path} ${user}`); callAutoScan(type, file, path, user); }); } // =============================== // AVVIO: LOGIN + WATCHER // =============================== loginAdmin().then(() => { startWatcher(); });