191 lines
4.8 KiB
JavaScript
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 };
|