server_photo_s85js/api_v1/scanner/scanActivitiesUser.js
2026-08-11 08:45:04 +02:00

160 lines
4.3 KiB
JavaScript

// api_v1/scanner/scanActivitiesUser.js
const path = require('path');
const fsp = require('fs/promises');
const scanActivity = require('./scanActivity');
const { log } = require('./logger');
const { WEB_ROOT, SUPPORTED_ACTIVITY_EXTS } = require('../config');
const writeJsonAtomic = require('./writeJsonAtomic');
// ---------------------------------------------------------
// ETA CALCULATOR
// ---------------------------------------------------------
function computeETA(startTime, current, total) {
if (current === 0) return 'calcolo...';
const elapsed = (Date.now() - startTime) / 1000;
const rate = current / elapsed;
const remaining = (total - current) / rate;
const m = Math.floor(remaining / 60);
const s = Math.floor(remaining % 60);
return `${m}m ${s}s`;
}
// ---------------------------------------------------------
// PRIMA PASSATA: conta TUTTI i file reali ricorsivamente
// ---------------------------------------------------------
async function countActivityFilesUser(rootDir) {
let count = 0;
async function walk(dir) {
let entries = [];
try {
entries = await fsp.readdir(dir, { withFileTypes: true });
} catch {
return;
}
for (const e of entries) {
const abs = path.join(dir, e.name);
if (e.isDirectory()) {
await walk(abs);
} else {
const ext = path.extname(e.name).toLowerCase();
if (SUPPORTED_ACTIVITY_EXTS.has(ext)) {
count++;
}
}
}
}
await walk(rootDir);
return count;
}
// ---------------------------------------------------------
// SECONDA PASSATA: scansiona SOLO le cartelle vere
// ---------------------------------------------------------
async function scanActivitiesUser(userName, db) {
log(`🔵 Inizio scan ATTIVITÀ per user=${userName}`);
const activitiesRoot = path.resolve(__dirname, '..', '..', WEB_ROOT, 'activities');
const userDir = path.join(activitiesRoot, userName, 'original');
const statusPath = path.join(activitiesRoot, 'scan_status.json');
let entries = [];
try {
entries = await fsp.readdir(userDir, { withFileTypes: true });
} catch {
log(`❌ Nessuna directory attività per utente ${userName}`);
return [];
}
// Filtra SOLO cartelle vere dentro "original"
const folders = entries
.filter(e => e.isDirectory())
.map(e => e.name);
// ---------------------------------------------------------
// RIMOZIONE CARTELLE CANCELLATE DAL FILESYSTEM
// ---------------------------------------------------------
const createDeleteFolderFunctions = require('./deleteFolder');
const { deleteFolderForUser } = createDeleteFolderFunctions(db);
const dbFolders = await db('activities')
.where({ user: userName })
.distinct('cartella')
.pluck('cartella');
for (const dbFolder of dbFolders) {
if (!folders.includes(dbFolder)) {
log(`🗑️ Cartella attività rimossa dal filesystem: ${dbFolder}`);
await deleteFolderForUser(userName, dbFolder);
}
}
// PRIMA PASSATA: conta i file reali
const TOTAL_FILES = await countActivityFilesUser(userDir);
const CURRENT = { value: 0 };
const start = Date.now();
log(`📊 Attività totali da scansionare: ${TOTAL_FILES}`);
// Stato iniziale
await writeJsonAtomic(statusPath, {
current: 0,
total: TOTAL_FILES,
percent: 0,
eta: 'calcolo...'
});
const allNewActivities = [];
// SECONDA PASSATA: UNA SOLA SCANSIONE PER CARTELLA
for (const cartella of folders) {
log(`📁 Scan cartella attività: ${cartella}`);
const newActivities = await scanActivity(
cartella,
userName,
db,
CURRENT,
TOTAL_FILES,
start,
async () => {
await writeJsonAtomic(statusPath, {
current: CURRENT.value,
total: TOTAL_FILES,
percent: TOTAL_FILES > 0
? Math.round((CURRENT.value / TOTAL_FILES) * 100)
: 100,
eta: computeETA(start, CURRENT.value, TOTAL_FILES)
});
}
);
if (newActivities?.length) {
allNewActivities.push(...newActivities);
}
}
// Stato finale
await writeJsonAtomic(statusPath, {
current: TOTAL_FILES,
total: TOTAL_FILES,
percent: 100,
eta: '0m 0s'
});
log(`🟣 Scan ATTIVITÀ COMPLETATO per user=${userName}. Nuove attività: ${allNewActivities.length}`);
return allNewActivities;
}
module.exports = scanActivitiesUser;