kequapp
Version:
DEPRECATED: renamed to @kequtech/arbor
57 lines (56 loc) • 2.43 kB
JavaScript
import fs from 'node:fs';
import path from 'node:path';
import { Ex } from "../ex.js";
import { createAction } from "../router/modules.js";
import { guessContentType } from "../utils/guess-content-type.js";
import { validateArray, validateExists, validateObject, validatePathname, } from "../utils/validate.js";
import { sendFile } from "./send-file.js";
export function staticDirectory(options) {
validateOptions(options);
const root = getAbsolute(process.cwd(), options.location);
return createAction(async ({ req, res, params }) => {
const wild = params.wild ?? '';
const location = await getLocation(root, options.location, wild, options.index);
const contentType = guessContentType(location, options.contentTypes);
await sendFile(req, res, location, contentType);
});
}
function validateOptions(options) {
validateExists(options, 'Static directory options');
validateObject(options, 'Static directory options');
validateExists(options.location, 'Static directory options.location');
validatePathname(options.location, 'Static directory options.location');
validateArray(options.index, 'Static directory options.index', 'string');
validateObject(options.contentTypes, 'Static directory options.contentTypes', 'string');
}
async function getLocation(root, location, wild, index = []) {
// Prevent traversal outside the static root
const absolute = getAbsolute(root, wild);
const relative = path.relative(root, absolute);
if (relative.startsWith('..') || relative.includes('../')) {
throw Ex.Forbidden('invalid path', { root, absolute });
}
try {
const stats = await fs.promises.stat(absolute);
if (stats.isDirectory() && index.length > 0) {
const files = await fs.promises.readdir(absolute);
for (const file of index) {
if (files.includes(file)) {
const indexPath = path.join(location, relative, file);
return indexPath;
}
}
}
if (stats.isFile()) {
const relFromCwd = path.relative(process.cwd(), absolute);
return relFromCwd;
}
}
catch {
// fall through to NotFound
}
throw Ex.NotFound(undefined, { absolute, index });
}
function getAbsolute(root, location) {
return path.resolve(root, location.replace(/^\/+/, ''));
}