/** * Shared filesystem storage utilities for database providers. */ import { join } from 'node:path'; import { promises as fsPromises } from 'node:fs'; export async function safeWrite(filePath, buffer) { if (buffer == null) return; const buf = Buffer.isBuffer(buffer) ? buffer : Buffer.from(buffer); await fsPromises.writeFile(filePath, buf); } export async function findFileByHash(dir, hash, ext = '') { if (!dir || !hash) return null; if (ext) { const candidate = join(dir, hash + ext); try { await fsPromises.access(candidate); return candidate; } catch { // fallback } } const candidateNoExt = join(dir, hash); try { await fsPromises.access(candidateNoExt); return candidateNoExt; } catch { // fallback } try { const files = await fsPromises.readdir(dir); const match = files.find((f) => f === hash || f.startsWith(hash + '.')); if (match) return join(dir, match); } catch { // ignore } return null; } export async function safeReadByHash(dir, hash, ext = '') { const filePath = await findFileByHash(dir, hash, ext); if (!filePath) return null; try { return await fsPromises.readFile(filePath); } catch (err) { if (err.code === 'ENOENT') return null; throw err; } } export async function safeDeleteByHash(dir, hash) { if (!dir || !hash) return; try { const files = await fsPromises.readdir(dir); const matches = files.filter((f) => f === hash || f.startsWith(hash + '.')); await Promise.all(matches.map((f) => fsPromises.unlink(join(dir, f)))); } catch (err) { if (err.code !== 'ENOENT') throw err; } } export function extFromMimetype(mimetype) { if (!mimetype) return ''; const type = mimetype.trim().toLowerCase().split(';')[0]; const map = { 'application/pdf': '.pdf', 'text/plain': '.txt', 'text/csv': '.csv', 'text/html': '.html', 'text/markdown': '.md', 'image/avif': '.avif', 'image/png': '.png', 'image/jpeg': '.jpg', 'image/jpg': '.jpg', 'image/webp': '.webp', 'image/gif': '.gif', 'image/svg+xml': '.svg', 'application/json': '.json', 'application/zip': '.zip', 'application/xml': '.xml', 'application/octet-stream': '.bin', }; if (map[type]) return map[type]; if (type.startsWith('image/')) return '.' + type.slice(6).replace('+xml', ''); if (type.startsWith('text/')) return '.' + type.slice(5); return ''; }