UNPKG

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
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; }