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).
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
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 });
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user