134 lines
No EOL
4.6 KiB
JavaScript
134 lines
No EOL
4.6 KiB
JavaScript
// scanner/scanPhoto.js
|
|
const path = require('path');
|
|
const fsp = require('fs/promises');
|
|
const { loadPreviousIndex, saveIndex } = require('./indexStore');
|
|
const scanUserRoot = require('./scanUser');
|
|
const postWithAuth = require('./postWithAuth');
|
|
const {
|
|
WEB_ROOT,
|
|
SEND_PHOTOS,
|
|
BASE_URL,
|
|
WRITE_INDEX,
|
|
} = require('../config');
|
|
|
|
const COMMON = 'Common'; // Nome canonico e case-sensitive per la shared folder
|
|
|
|
/**
|
|
* Restituisce la prima directory esistente tra le candidate
|
|
* @param {string[]} candidates
|
|
* @returns {Promise<string|null>}
|
|
*/
|
|
async function firstExistingDir(candidates) {
|
|
for (const dir of candidates) {
|
|
try {
|
|
const st = await fsp.stat(dir);
|
|
if (st.isDirectory()) return dir;
|
|
} catch { /* ignore */ }
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Scansione foto con indice separato:
|
|
* - Carica indice precedente (mappa { id: meta })
|
|
* - Produce SOLO i cambiamenti (nuovi/modificati)
|
|
* - Merge e salvataggio atomico dell'indice
|
|
* - POST verso /photos solo del delta (se abilitato)
|
|
* - Admin: include la cartella "Common"
|
|
*
|
|
* Nota path: usiamo un path ASSOLUTO per <root>/public/photos
|
|
* partendo da .../api_v1/scanner -> '..', '..' per salire alla root del progetto.
|
|
*/
|
|
async function scanPhoto(dir, userName) {
|
|
try {
|
|
const previousIndexMap = await loadPreviousIndex();
|
|
const nextIndexMap = { ...previousIndexMap };
|
|
|
|
// Path assoluto alla radice photos (es: <root>/public/photos)
|
|
const photosRoot = path.resolve(__dirname, '..', '..', WEB_ROOT, 'photos');
|
|
|
|
let changes = [];
|
|
|
|
if (userName === 'Admin') {
|
|
// 1) Scansiona TUTTI gli utenti tranne la/e cartella/e common
|
|
let entries = [];
|
|
try {
|
|
entries = await fsp.readdir(photosRoot, { withFileTypes: true });
|
|
} catch (e) {
|
|
console.error(`[SCAN] photosRoot non accessibile: ${photosRoot}`, e.message);
|
|
entries = [];
|
|
}
|
|
|
|
for (const e of entries) {
|
|
if (!e.isDirectory()) continue;
|
|
// salta qualunque "common" (qualunque casing) per non doppiare
|
|
if (e.name.toLowerCase() === COMMON.toLowerCase()) continue;
|
|
|
|
const userDir = path.join(photosRoot, e.name, 'original');
|
|
try {
|
|
const st = await fsp.stat(userDir);
|
|
if (!st.isDirectory()) continue;
|
|
} catch {
|
|
continue;
|
|
}
|
|
|
|
const userChanges = await scanUserRoot(e.name, userDir, previousIndexMap);
|
|
for (const m of userChanges) nextIndexMap[m.id] = m;
|
|
changes.push(...userChanges);
|
|
}
|
|
|
|
// 2) Scansiona la cartella COMMON (case strict), con fallback legacy (solo lettura) a 'common'
|
|
const commonPreferred = path.join(photosRoot, COMMON, 'original'); // .../photos/Common/original
|
|
const commonLegacy = path.join(photosRoot, 'common', 'original'); // .../photos/common/original (legacy)
|
|
|
|
const commonDir = await firstExistingDir([commonPreferred, commonLegacy]);
|
|
if (commonDir) {
|
|
// Forziamo SEMPRE userName = 'Common' per ID/thumbnails coerenti in 'Common'
|
|
const commonChanges = await scanUserRoot(COMMON, commonDir, previousIndexMap);
|
|
for (const m of commonChanges) nextIndexMap[m.id] = m;
|
|
changes.push(...commonChanges);
|
|
console.log(`[SCAN] Common indicizzati da ${commonDir}: +${commonChanges.length}`);
|
|
} else {
|
|
console.log(`[SCAN] Nessuna cartella "${COMMON}" trovata sotto ${photosRoot} (atteso: ${commonPreferred})`);
|
|
}
|
|
|
|
} else {
|
|
// Non-Admin (per completezza; in server.js /scan è Admin-only)
|
|
const userDir = path.join(photosRoot, userName, 'original');
|
|
try {
|
|
const st = await fsp.stat(userDir);
|
|
if (st.isDirectory()) {
|
|
const userChanges = await scanUserRoot(userName, userDir, previousIndexMap);
|
|
for (const m of userChanges) nextIndexMap[m.id] = m;
|
|
changes.push(...userChanges);
|
|
}
|
|
} catch {
|
|
// utente senza dir 'original' -> nessuna modifica
|
|
}
|
|
}
|
|
|
|
// Salva indice (mappa) in modo atomico
|
|
if (WRITE_INDEX) {
|
|
await saveIndex(nextIndexMap);
|
|
}
|
|
|
|
// POST solo dei cambiamenti (delta)
|
|
if (SEND_PHOTOS && BASE_URL && changes.length) {
|
|
for (const p of changes) {
|
|
try {
|
|
await postWithAuth(`${BASE_URL}/photos`, p);
|
|
} catch (err) {
|
|
console.error('Errore invio:', err.message);
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log(`SCAN COMPLETATA per utente ${userName}: ${changes.length} file aggiornati`);
|
|
return changes;
|
|
} catch (e) {
|
|
console.error('Errore generale scanPhoto:', e);
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
module.exports = scanPhoto; |