// utils/resolveFilePath.js const fs = require('fs'); const path = require('path'); const db = require('../db/knex'); const { WEB_ROOT } = require('../api_v1/config'); // In questo file __dirname = /utils. // Quindi la root reale del progetto è un livello sopra. const PROJECT_ROOT = path.resolve(__dirname, '..'); function existingPath(...parts) { const p = path.resolve(...parts); return fs.existsSync(p) ? p : null; } function normalizeWebRoot() { if (!WEB_ROOT || typeof WEB_ROOT !== 'string') return 'public'; // Se WEB_ROOT è assoluto, lo usiamo come root assoluta. // Se è relativo, lo risolviamo dalla root progetto. return WEB_ROOT; } function resolveCandidatePath(candidate) { if (!candidate || typeof candidate !== 'string') return null; const c = candidate.trim(); if (!c) return null; const webRoot = normalizeWebRoot(); // =============================== // PATH ASSOLUTO // =============================== if (path.isAbsolute(c)) { const direct = existingPath(c); if (direct) return direct; // Caso comune: // DB salva "/photos/utente/original/..." // ma il vero path è "//photos/..." const underWebRoot = existingPath( path.isAbsolute(webRoot) ? webRoot : path.join(PROJECT_ROOT, webRoot), c.replace(/^\/+/, ''), ); if (underWebRoot) return underWebRoot; // Altro fallback: // "/photos/..." const underProject = existingPath(PROJECT_ROOT, c.replace(/^\/+/, '')); if (underProject) return underProject; return null; } // =============================== // PATH RELATIVO // =============================== // // const p1 = existingPath( path.isAbsolute(webRoot) ? webRoot : path.join(PROJECT_ROOT, webRoot), c, ); if (p1) return p1; // / const p2 = existingPath(PROJECT_ROOT, c); if (p2) return p2; return null; } async function getPhotoRowByAnyKnownId(entryId) { const cleanId = String(entryId || '').trim(); if (!cleanId) return null; // Prima prova id, che è il caso originale. let row = await db('photos').where({ id: cleanId }).first(); if (row) return row; // Poi prova colonne alternative, ma solo se esistono davvero. // Così evitiamo errori tipo "no such column". let columns = {}; try { columns = await db('photos').columnInfo(); } catch (err) { console.warn('[resolveFilePathForEntryId] columnInfo error:', err.message); return null; } const candidateColumns = [ 'remoteId', 'remote_id', 'hash', 'sha1', 'checksum', 'content_hash', 'contentHash', ]; for (const col of candidateColumns) { if (!columns[col]) continue; row = await db('photos').where(col, cleanId).first(); if (row) { console.log(`[resolveFilePathForEntryId] found row by ${col}=${cleanId}`); return row; } } console.warn('[resolveFilePathForEntryId] no row found for id/hash:', cleanId); return null; } async function resolveFilePathForEntryId(entryId) { try { const cleanId = String(entryId || '').trim(); if (!cleanId) return null; const row = await getPhotoRowByAnyKnownId(cleanId); if (!row) return null; const candidates = [ row.path, row.file, row.file_path, row.original, row.uri, row.fullpath, row.storage_path, row.local_path, row.remotePath, row.remote_path, ]; for (const candidate of candidates) { const resolved = resolveCandidatePath(candidate); if (resolved) { console.log('[resolveFilePathForEntryId] resolved from candidate:', { entryId: cleanId, candidate, resolved, }); return resolved; } } // =============================== // FALLBACK: user/cartella/name // =============================== if (row.user && row.cartella && (row.name || row.file)) { const filename = row.name || row.file; const webRoot = normalizeWebRoot(); const resolved = existingPath( path.isAbsolute(webRoot) ? webRoot : path.join(PROJECT_ROOT, webRoot), 'photos', row.user, 'original', row.cartella, filename, ); if (resolved) { console.log('[resolveFilePathForEntryId] resolved from user/cartella/name:', { entryId: cleanId, resolved, }); return resolved; } } console.warn('[resolveFilePathForEntryId] file path not resolved:', { entryId: cleanId, rowId: row.id, user: row.user, cartella: row.cartella, name: row.name, path: row.path, file: row.file, }); return null; } catch (err) { console.error('resolveFilePathForEntryId error:', err); return null; } } module.exports = { resolveFilePathForEntryId };