ipfs-gateway-emulator
Version:
A server emulating IPFS gateway behaviour, for local preview and end-to-end tests
78 lines (65 loc) • 2.28 kB
JavaScript
import {Hono} from 'hono';
import {serve} from '@hono/node-server';
import {serveStatic} from '@hono/node-server/serve-static';
import fs from 'node:fs';
import path from 'node:path';
import {
routeRequest,
stripIpfsPrefix,
parseFailSpec,
matchesFailFolder,
needsTrailingSlash,
} from './emulation.js';
/**
* Build the Hono app that emulates an IPFS path gateway over `directory`.
*
* Exported separately from `startServer` so tests can drive it with
* `app.request(...)` without binding a port.
*/
export function createApp({directory = '.', only, fail} = {}) {
const app = new Hono();
const failSpec = parseFailSpec(fail);
app.use('*', async (c, next) => {
const pathname = decodeURIComponent(new URL(c.req.url).pathname);
const decision = routeRequest({
pathname,
referer: c.req.header('referer'),
only,
});
if (decision.type === 'notFound') {
return c.text(decision.message, 404);
}
const {logicalPath} = decision;
if (failSpec && matchesFailFolder(logicalPath, failSpec.folders)) {
return c.text('An Error Happened', failSpec.status);
}
// Resolve against the served directory to see whether this is a
// directory, which a gateway answers with a trailing-slash redirect so
// that relative URLs inside the page resolve correctly.
const relative = logicalPath.startsWith('/') ? logicalPath.slice(1) : logicalPath;
const resolved = path.join(directory, relative);
if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory() && needsTrailingSlash(pathname)) {
return c.redirect(`${pathname}/`, 302);
}
await next();
});
app.use(
'*',
serveStatic({
root: directory,
rewriteRequestPath: stripIpfsPrefix,
})
);
app.notFound((c) => c.text('Not Found', 404));
return app;
}
/** Start the emulator. Resolves with the underlying Node server. */
export function startServer({port = 8080, directory = '.', only, fail, log = console.log} = {}) {
const app = createApp({directory, only, fail});
const server = serve({fetch: app.fetch, port}, (info) => {
log(`Serving ${path.resolve(directory)} as an IPFS gateway on http://127.0.0.1:${info.port}`);
log(` root: http://127.0.0.1:${info.port}/`);
log(` gateway: http://127.0.0.1:${info.port}/ipfs/<cid>/`);
});
return server;
}