server_photo_s85js/utils/resolveFilePath.js
2026-08-11 08:45:04 +02:00

191 lines
4.8 KiB
JavaScript

// 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 = <project>/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 è "<PROJECT_ROOT>/<WEB_ROOT>/photos/..."
const underWebRoot = existingPath(
path.isAbsolute(webRoot) ? webRoot : path.join(PROJECT_ROOT, webRoot),
c.replace(/^\/+/, ''),
);
if (underWebRoot) return underWebRoot;
// Altro fallback:
// "<PROJECT_ROOT>/photos/..."
const underProject = existingPath(PROJECT_ROOT, c.replace(/^\/+/, ''));
if (underProject) return underProject;
return null;
}
// ===============================
// PATH RELATIVO
// ===============================
// <PROJECT_ROOT>/<WEB_ROOT>/<c>
const p1 = existingPath(
path.isAbsolute(webRoot) ? webRoot : path.join(PROJECT_ROOT, webRoot),
c,
);
if (p1) return p1;
// <PROJECT_ROOT>/<c>
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 };