90 lines
2.4 KiB
JavaScript
90 lines
2.4 KiB
JavaScript
/**
|
|
* 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 '';
|
|
}
|