Files
pokemon-inventory/apps/api/src/infrastructure/images/local-image-storage.js
T
fragginwagon c728016dec feat: establish auth listing and image vertical slices
Includes MFA-capable authentication, session and CSRF security, password recovery, listing lifecycle and ownership rules, inventory image metadata, local ImageStorage, migrations, tests, and implementation status documentation. Verification: npm test (62 passing) and integration suite (14 passing).
2026-09-08 17:32:03 -04:00

45 lines
1.3 KiB
JavaScript

import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
import path from 'node:path';
import { createImageStorageContract } from './image-storage.js';
function safePath(rootDirectory, key) {
if (typeof key !== 'string' || key.length === 0 || path.isAbsolute(key)) {
throw new Error('Image storage key must be a relative non-empty path.');
}
const root = path.resolve(rootDirectory);
const target = path.resolve(root, key);
if (target !== root && !target.startsWith(`${root}${path.sep}`)) {
throw new Error('Image storage key escapes the storage root.');
}
return target;
}
export function createLocalImageStorage(rootDirectory) {
if (typeof rootDirectory !== 'string' || rootDirectory.length === 0) {
throw new TypeError('Image storage root directory is required.');
}
return createImageStorageContract({
async put({ key, data }) {
if (!Buffer.isBuffer(data)) {
throw new TypeError('Image storage data must be a Buffer.');
}
const target = safePath(rootDirectory, key);
await mkdir(path.dirname(target), { recursive: true });
await writeFile(target, data, { flag: 'wx' });
return { key };
},
async get(key) {
return readFile(safePath(rootDirectory, key));
},
async delete(key) {
await rm(safePath(rootDirectory, key), { force: true });
},
});
}