ipfs-gateway-emulator
Version:
A server emulating ipfs gateway behavior forked from local-web-server
117 lines (105 loc) • 3.33 kB
JavaScript
const fs = require("fs");
const path = require("path");
const EventEmitter = require('events')
const slash = require('slash');
function replyWithError(ctx, code, message = 'An Error Happened') {
ctx.body = message;
ctx.status= code;
ctx.message = message ;
}
function replyWith404(ctx, message) {
ctx.body = message || "Not Found";
ctx.status= 404;
ctx.message = message || "Not Found";
}
class TrailingSlash extends EventEmitter {
description () {
return 'Mimic ipfs gateway behavior'
}
optionDefinitions () {
return [
{
name: 'only',
type: String,
description: 'to only enable "root" or "hash" ("/ipfs/<hash>/") base path'
},
{
name: 'fail',
type: String,
description: 'fails with status code specified this way: `<Status code>:<folder1>[,<folder2>]*`'
}
]
}
middleware (config) {
return async function (ctx, next) {
const url = new URL(ctx.request.href);
let pathname = url.pathname;
let logicalPathName = pathname; // that will pass along to fetch the resource (via next())
if (logicalPathName.startsWith('/ipfs/')) {
if (!config.only || config.only === "hash") {
const hashSlashIndex = logicalPathName.substr(6).indexOf("/");
if (hashSlashIndex === -1) {
logicalPathName = "/"
} else {
logicalPathName = logicalPathName.slice(hashSlashIndex+6);
}
} else {
return replyWith404(ctx);
}
} else {
if (ctx.request.header) {
const referer = ctx.request.header.referer;
if (referer) {
const refererURL = new URL(referer);
if (refererURL.pathname.startsWith("/ipfs/")) {
return replyWith404(ctx, "Not Found (referer)");
}
}
}
if (config.only && config.only === "hash") {
return replyWith404(ctx);
}
}
let filepath = logicalPathName.length > 0 ? logicalPathName.slice(1) : logicalPathName;
if (config.directory) {
filepath = path.join(config.directory, filepath);
}
if (fs.existsSync(filepath)) {
// console.log({filepath});
if (config.fail) {
const [codeStr, folders] = config.fail.split(':');
const code = parseInt(codeStr);
const splitted = folders.split(',');
for (const split of splitted) {
if (filepath.startsWith(config.directory ? path.join(config.directory, split): split)) {
return replyWithError(ctx, isNaN(code) ? 500 : code);
}
}
}
const stats = fs.statSync(filepath);
if (stats.isDirectory()) {
const length = pathname.length;
if (length == 0) {
pathname = "/"
} else if (pathname[length - 1] != "/") {
pathname += "/";
}
if (pathname !== url.pathname) {
console.log('redirecting to ' + pathname);
ctx.redirect(pathname);
} else {
ctx.request.url = logicalPathName;
}
} else {
ctx.request.url = logicalPathName;
}
}
await next()
}
}
}
module.exports = [
TrailingSlash,
'static',
'index'
]