galleria_photo_face_rknn_op.../watcher/watcher_logic.mjs
2026-10-03 12:16:58 +02:00

413 lines
7.7 KiB
JavaScript

import readline from "readline";
import dotenv from "dotenv";
dotenv.config();
// ===============================
// CONFIG
// ===============================
const BASE_URL = process.env.BASE_URL;
if (!BASE_URL) {
console.error("ERRORE: BASE_URL non definita nel .env");
process.exit(1);
}
const WATCHER_TOKEN = process.env.WATCHER_TOKEN;
if (!WATCHER_TOKEN) {
console.error("ERRORE: WATCHER_TOKEN non definito nel .env");
process.exit(1);
}
console.log("Watcher globale avviato");
// ===============================
// STDIN
// ===============================
const rl = readline.createInterface({
input: process.stdin,
crlfDelay: Infinity
});
// ===============================
// ESTENSIONI FOTO / VIDEO
// ===============================
const photoExt = [
".jpg",
".jpeg",
".png",
".gif",
".bmp",
".webp",
".tiff",
".heic",
".heif",
".mp4",
".mov",
".avi",
".mkv",
".webm",
".m4v"
];
// ===============================
// ESTENSIONI ATTIVITÀ
// ===============================
const activityExt = [
".fit",
".gpx",
".tcx"
];
// ===============================
// PARSING PATH
// ===============================
function parsePath(fullPath) {
const normalized = fullPath.replace(/\\/g, "/");
/*
* Supporta sia:
*
* /app/photos/Fabio/original/...
* /app/activities/Fabio/original/...
*
* sia:
*
* ../public/photos/Fabio/original/...
* ../public/activities/Fabio/original/...
*
*/
const match = normalized.match(
/(?:^|\/)(photos|activities)\/([^/]+)\/original(?:\/|$)/
);
if (!match) {
return null;
}
return {
area: match[1],
user: match[2]
};
}
// ===============================
// NORMALIZZAZIONE PATH
// ===============================
function normalizePath(fullPath) {
const normalized = fullPath.replace(/\\/g, "/");
/*
* Trasforma:
*
* ../public/photos/Fabio/original/traptour/
*
* in:
*
* /photos/Fabio/original/traptour/
*
*
* e anche:
*
* /app/photos/Fabio/original/traptour/
*
* in:
*
* /photos/Fabio/original/traptour/
*/
const match = normalized.match(
/(?:^|\/)(photos|activities)\/(.+)$/
);
if (!match) {
return null;
}
return `/${match[1]}/${match[2]}`;
}
// ===============================
// FILTRO FILE
// ===============================
function isAllowedFile(area, filename) {
const lower = filename.toLowerCase();
if (area === "photos") {
return photoExt.some(ext => lower.endsWith(ext));
}
if (area === "activities") {
return activityExt.some(ext => lower.endsWith(ext));
}
return false;
}
// ===============================
// CHIAMATA API
// ===============================
async function callAutoScan(type, file, path, user) {
const relPath = normalizePath(path);
if (!relPath) {
console.error(
"Impossibile normalizzare path:",
path
);
return false;
}
const headers = {
"Content-Type": "application/json",
Authorization: `Bearer ${WATCHER_TOKEN}`
};
const payload = {
type,
file,
path: relPath,
user
};
console.log("AUTO_SCAN:", payload);
try {
const res = await fetch(
`${BASE_URL}/api_v1/auto_scan`,
{
method: "POST",
headers,
body: JSON.stringify(payload)
}
);
if (!res.ok) {
console.error(
`Errore auto_scan ${res.status}`
);
try {
const text = await res.text();
if (text) {
console.error(
"Risposta API:",
text
);
}
}
catch (_) {
// niente
}
return false;
}
console.log(
`AUTO_SCAN OK ${res.status}`
);
return true;
}
catch (err) {
console.error(
"Errore chiamata auto_scan:",
err
);
return false;
}
}
// ===============================
// WATCHER
// ===============================
function startWatcher() {
rl.on("line", async line => {
console.log("RAW:", line);
try {
const parts = line.trim().split(/\s+/);
if (parts.length < 3) {
console.log(
"EVENTO SCARTATO: formato non valido"
);
return;
}
const path = parts[0];
const action = parts[1];
const file = parts.slice(2).join(" ");
console.log({
path,
action,
file
});
// ===============================
// PARSE PATH
// ===============================
const info = parsePath(path);
console.log(
"PARSE_PATH:",
info
);
if (!info) {
console.log(
"PATH IGNORATO"
);
return;
}
const {
user,
area
} = info;
// ===============================
// DIRECTORY?
// ===============================
const isDir = action.includes("ISDIR");
// ===============================
// FILTRO ESTENSIONE
// ===============================
if (
!isDir &&
!isAllowedFile(area, file)
) {
console.log(
`FILE IGNORATO area=${area} file=${file}`
);
return;
}
// ===============================
// TIPO EVENTO
// ===============================
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 {
console.log(
`AZIONE IGNORATA: ${action}`
);
return;
}
// ===============================
// EVENTO
// ===============================
console.log(
`[${area}] ${user} -> ${type} ${file}`
);
// ===============================
// API
// ===============================
await callAutoScan(
type,
file,
path,
user
);
}
catch (err) {
console.error(
"Errore watcher:",
err
);
}
});
}
// ===============================
// START
// ===============================
startWatcher();