331 lines
9.7 KiB
JavaScript
331 lines
9.7 KiB
JavaScript
let photoScanInterval = null;
|
|
let activityScanInterval = null;
|
|
|
|
/* ============================================================
|
|
UTILS
|
|
============================================================ */
|
|
function stopPhotoScanPolling() {
|
|
if (photoScanInterval) {
|
|
clearInterval(photoScanInterval);
|
|
photoScanInterval = null;
|
|
}
|
|
}
|
|
|
|
function stopActivityScanPolling() {
|
|
if (activityScanInterval) {
|
|
clearInterval(activityScanInterval);
|
|
activityScanInterval = null;
|
|
}
|
|
}
|
|
|
|
function updateScanUi(data) {
|
|
const current = Number(data?.current || 0);
|
|
const total = Number(data?.total || 0);
|
|
|
|
const percent = Number.isFinite(Number(data?.percent))
|
|
? Number(data.percent)
|
|
: total > 0
|
|
? Math.round((current / total) * 100)
|
|
: 0;
|
|
|
|
return { current, total, percent };
|
|
}
|
|
|
|
/* Aggiornamento indipendente dei banner di scansione. */
|
|
function showPhotoBanner(message, options = {}) {
|
|
if (window.operationBanner) {
|
|
window.operationBanner.show(message, { ...options, id: "photos" });
|
|
}
|
|
}
|
|
|
|
function showActivityBanner(message, options = {}) {
|
|
if (window.operationBanner) {
|
|
window.operationBanner.show(message, { ...options, id: "activities" });
|
|
}
|
|
}
|
|
|
|
/* ============================================================
|
|
FOTO — SCANSIONE
|
|
============================================================ */
|
|
let photoScanRequestDone = false;
|
|
let photoCacheSynced = false;
|
|
let photoPollingBusy = false;
|
|
|
|
async function scan() {
|
|
photoScanRequestDone = false;
|
|
photoCacheSynced = false;
|
|
|
|
startScanStatusPolling();
|
|
|
|
if (window.operationBanner) {
|
|
showPhotoBanner(
|
|
"Avvio scansione foto…",
|
|
{ type: "info", busy: true, sticky: true }
|
|
);
|
|
}
|
|
|
|
try {
|
|
await apiGet("/scan");
|
|
photoScanRequestDone = true;
|
|
} catch (err) {
|
|
photoScanRequestDone = true;
|
|
console.error("Errore avvio scan foto:", err);
|
|
out?.({ error: err.message });
|
|
|
|
if (window.operationBanner) {
|
|
showPhotoBanner(
|
|
`Errore nella scansione foto: ${err.message}`,
|
|
{ type: "error", sticky: true }
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
async function startScanStatusPolling() {
|
|
stopPhotoScanPolling();
|
|
|
|
photoScanInterval = setInterval(async () => {
|
|
if (photoPollingBusy) return;
|
|
photoPollingBusy = true;
|
|
|
|
try {
|
|
const data = await apiGet(`/scanStatus/photos?ts=${Date.now()}`);
|
|
const remaining = Number(data?.remaining ?? 0);
|
|
const pendingCount = Number(data?.pending || 0);
|
|
const processingCount = Number(data?.processing || 0);
|
|
const errorsFromDb = Number(data?.errors || 0);
|
|
const current = Number(data?.current || 0);
|
|
const total = Number(data?.total || 0);
|
|
const percent = Math.max(0, Math.min(100, Number(data?.percent || 0)));
|
|
if (window.operationBanner) {
|
|
window.operationBanner.setProgress("photos", percent);
|
|
}
|
|
|
|
let queue = null;
|
|
try {
|
|
queue = await apiGet(`/scanStatus/worker-queues?ts=${Date.now()}`);
|
|
if (!queue?.ok) queue = null;
|
|
} catch (err) {
|
|
console.warn("[scan foto] lettura coda fallita:", err.message);
|
|
}
|
|
|
|
const pending = Number(queue?.photos || 0);
|
|
const errors = Number(queue?.photosErrors || 0);
|
|
const scanFinished = photoScanRequestDone;
|
|
|
|
if (window.operationBanner) {
|
|
let message;
|
|
|
|
if (!scanFinished) {
|
|
message =
|
|
`Scansione foto in corso · ${pending} da elaborare · ${errors} errori`;
|
|
showPhotoBanner(message, {
|
|
type: errors > 0 ? "error" : "info",
|
|
busy: true,
|
|
sticky: true
|
|
});
|
|
} else if (!photoCacheSynced) {
|
|
message =
|
|
`Accodamento completato · ${pending} foto da elaborare · ${errors} errori`;
|
|
showPhotoBanner(message, {
|
|
type: errors > 0 ? "error" : "info",
|
|
busy: pending > 0,
|
|
sticky: true
|
|
});
|
|
} else if (pending > 0) {
|
|
showPhotoBanner(
|
|
`Elaborazione foto in corso: ${pending} da elaborare · ${errors} errori`,
|
|
{ type: errors > 0 ? "error" : "info", busy: true, sticky: true }
|
|
);
|
|
} else {
|
|
stopPhotoScanPolling();
|
|
|
|
showPhotoBanner(
|
|
errors > 0
|
|
? `Elaborazione terminata con ${errors} errori nella coda foto`
|
|
: "Scansione ed elaborazione foto completate",
|
|
{
|
|
type: errors > 0 ? "error" : "success",
|
|
busy: false,
|
|
sticky: errors > 0,
|
|
duration: 5000
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
if (scanFinished && !photoCacheSynced) {
|
|
if (window.AdminCacheSync?.progressiveSyncPhotosCacheFromAdmin) {
|
|
const result =
|
|
await AdminCacheSync.progressiveSyncPhotosCacheFromAdmin();
|
|
console.log("[admin scan foto] cache aggiornata:", result);
|
|
} else {
|
|
console.warn("[admin scan foto] AdminCacheSync non disponibile");
|
|
}
|
|
|
|
await readDBUser();
|
|
photoCacheSynced = true;
|
|
}
|
|
|
|
if (scanFinished && !queue) {
|
|
if (window.operationBanner) {
|
|
showPhotoBanner(
|
|
"Scansione terminata; impossibile leggere lo stato della coda foto",
|
|
{ type: "error", sticky: true }
|
|
);
|
|
}
|
|
}
|
|
} catch (err) {
|
|
console.warn("[scan foto] aggiornamento stato fallito:", err.message);
|
|
} finally {
|
|
photoPollingBusy = false;
|
|
}
|
|
}, 1000);
|
|
}
|
|
|
|
/* ============================================================
|
|
ATTIVITÀ — SCANSIONE ED ELABORAZIONE CODA
|
|
============================================================ */
|
|
let activityScanRequestDone = false;
|
|
let activityCacheSynced = false;
|
|
let activityPollingBusy = false;
|
|
|
|
async function scanActivities() {
|
|
activityScanRequestDone = false;
|
|
activityCacheSynced = false;
|
|
|
|
if (window.operationBanner) {
|
|
showActivityBanner(
|
|
"Avvio scansione attività…",
|
|
{ type: "info", busy: true, sticky: true }
|
|
);
|
|
}
|
|
|
|
startScanActivitiesPolling();
|
|
|
|
try {
|
|
await apiGet("/scanActivities");
|
|
activityScanRequestDone = true;
|
|
} catch (err) {
|
|
activityScanRequestDone = true;
|
|
console.error("Errore avvio scan attività:", err);
|
|
outActivities?.({ error: err.message });
|
|
stopActivityScanPolling();
|
|
|
|
if (window.operationBanner) {
|
|
showActivityBanner(
|
|
`Errore nella scansione attività: ${err.message}`,
|
|
{ type: "error", sticky: true }
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
function startScanActivitiesPolling() {
|
|
stopActivityScanPolling();
|
|
|
|
activityScanInterval = setInterval(async () => {
|
|
if (activityPollingBusy) return;
|
|
activityPollingBusy = true;
|
|
|
|
try {
|
|
const data = await apiGet(
|
|
`/scanStatus/activities?ts=${Date.now()}`
|
|
);
|
|
|
|
const { current, total, percent } = updateScanUi(data);
|
|
if (window.operationBanner) {
|
|
window.operationBanner.setProgress("activities", percent);
|
|
}
|
|
|
|
let queue = null;
|
|
try {
|
|
queue = await apiGet(
|
|
`/scanStatus/worker-queues?ts=${Date.now()}`
|
|
);
|
|
if (!queue?.ok) queue = null;
|
|
} catch (err) {
|
|
console.warn(
|
|
"[scan attività] lettura coda fallita:",
|
|
err.message
|
|
);
|
|
}
|
|
|
|
const pending = Number(queue?.activities || 0);
|
|
const errors = Number(queue?.activitiesErrors || 0);
|
|
|
|
const scanFinished =
|
|
activityScanRequestDone &&
|
|
(total === 0 || current >= total);
|
|
|
|
// Aggiorna la cache una sola volta, quando la scansione è finita.
|
|
if (scanFinished && !activityCacheSynced) {
|
|
if (window.AdminCacheSync?.progressiveSyncActivitiesCacheFromAdmin) {
|
|
const result =
|
|
await AdminCacheSync.progressiveSyncActivitiesCacheFromAdmin();
|
|
console.log("[admin scan attività] cache aggiornata:", result);
|
|
} else {
|
|
console.warn(
|
|
"[admin scan attività] AdminCacheSync non disponibile"
|
|
);
|
|
}
|
|
|
|
await readActivitiesDBUser();
|
|
activityCacheSynced = true;
|
|
}
|
|
|
|
if (!window.operationBanner) {
|
|
// Senza banner non serve aggiornare messaggi, ma il polling
|
|
// continua comunque fino alla fine della coda.
|
|
if (scanFinished && queue && pending === 0) {
|
|
stopActivityScanPolling();
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!scanFinished) {
|
|
showActivityBanner(
|
|
`Scansione attività: ${percent}%` +
|
|
(queue
|
|
? ` · ${pending} in coda/elaborazione · ${errors} errori`
|
|
: ""),
|
|
{ type: errors > 0 ? "error" : "info", busy: true, sticky: true }
|
|
);
|
|
} else if (!queue) {
|
|
// Non nascondere il banner se non possiamo verificare la coda.
|
|
showActivityBanner(
|
|
"Scansione attività terminata; attendo di poter verificare la coda",
|
|
{ type: "error", busy: true, sticky: true }
|
|
);
|
|
} else if (pending > 0) {
|
|
showActivityBanner(
|
|
`Elaborazione attività in corso: ${pending} in coda/elaborazione` +
|
|
(errors > 0 ? ` · ${errors} errori` : ""),
|
|
{ type: errors > 0 ? "error" : "info", busy: true, sticky: true }
|
|
);
|
|
} else {
|
|
// La scansione è terminata e non ci sono job pending/processing.
|
|
stopActivityScanPolling();
|
|
|
|
showActivityBanner(
|
|
errors > 0
|
|
? `Scansione attività completata con ${errors} errori`
|
|
: "Scansione attività completata",
|
|
{
|
|
type: errors > 0 ? "error" : "success",
|
|
busy: false,
|
|
sticky: errors > 0,
|
|
duration: 5000
|
|
}
|
|
);
|
|
}
|
|
} catch (err) {
|
|
console.warn(
|
|
"[scan attività] aggiornamento stato fallito, riprovo:",
|
|
err.message
|
|
);
|
|
} finally {
|
|
activityPollingBusy = false;
|
|
}
|
|
}, 1000);
|
|
}
|