Skip to content
This repository has been archived by the owner on Feb 12, 2024. It is now read-only.

Commit

Permalink
fix: update module (#104)
Browse files Browse the repository at this point in the history
- Adds types
- Converts to ESM
- Uses [ejs](https://www.npmjs.com/package/ejs) for html templating
  • Loading branch information
achingbrain committed Sep 7, 2021
1 parent b9a998a commit 319e2b4
Show file tree
Hide file tree
Showing 12 changed files with 301 additions and 216 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,4 @@ typings/
# while testing npm5
package-lock.json
yarn.lock
types
30 changes: 24 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,32 @@
"description": "Creates an HTTP response from an IPFS Hash",
"leadMaintainer": "Vasco Santos <vasco.santos@moxy.studio>",
"main": "src/index.js",
"types": "./types/src/index.d.ts",
"type": "module",
"eslintConfig": {
"extends": "ipfs",
"parserOptions": {
"sourceType": "module"
}
},
"exports": {
".": {
"import": "./src/index.js"
}
},
"scripts": {
"lint": "aegir lint",
"prepare": "aegir build --no-bundle && cp -R types dist",
"lint": "aegir ts -p check && aegir lint",
"build": "aegir build",
"release": "aegir release --target node",
"release-minor": "aegir release --type minor --target node",
"pretest": "aegir build --esm-tests",
"test": "aegir test -t node",
"test:node": "aegir test -t node"
},
"browser": {
"file-type": "file-type/browser"
"file-type": "file-type/browser",
"fs": false
},
"repository": {
"type": "git",
Expand All @@ -32,8 +48,9 @@
"homepage": "https://github.com/ipfs/js-ipfs-http-response#readme",
"dependencies": {
"debug": "^4.3.1",
"ejs": "^3.1.6",
"file-type": "^16.0.0",
"filesize": "^7.0.0",
"filesize": "^8.0.0",
"it-buffer": "^0.1.1",
"it-concat": "^2.0.0",
"it-reader": "^3.0.0",
Expand All @@ -43,13 +60,14 @@
"p-try-each": "^1.0.1"
},
"devDependencies": {
"aegir": "^34.0.2",
"@types/ejs": "^3.1.0",
"aegir": "^35.0.3",
"get-stream": "^6.0.0",
"ipfs-core": "^0.9.1",
"ipfs-core": "^0.10.5",
"ipfsd-ctl": "^10.0.1",
"it-all": "^1.0.5",
"path": "^0.12.7",
"uint8arrays": "^2.0.5",
"uint8arrays": "^3.0.0",
"util": "^0.12.3"
},
"contributors": [
Expand Down
135 changes: 65 additions & 70 deletions src/dir-view/index.js
Original file line number Diff line number Diff line change
@@ -1,81 +1,76 @@
'use strict'

const filesize = require('filesize')

const mainStyle = require('./style')
const pathUtil = require('../utils/path')
import filesize from 'filesize'
import style from './style.js'
import { cidArray } from '../utils/path.js'
import ejs from 'ejs'

/**
* @param {string} path
*/
function getParentHref (path) {
const parts = pathUtil.cidArray(path).slice()
const parts = cidArray(path).slice()
if (parts.length > 1) {
// drop the last segment in a safe way that works for both paths and urls
return path.replace(`/${parts.pop()}`, '')
}
return path
}

function buildFilesList (path, links) {
const rows = links.map((link) => {
let row = [
'<div class="ipfs-icon ipfs-_blank">&nbsp;</div>',
`<a href="${path}${path.endsWith('/') ? '' : '/'}${link.Name}">${link.Name}</a>`,
filesize(link.Tsize)
]

row = row.map((cell) => `<td>${cell}</td>`).join('')

return `<tr>${row}</tr>`
})

return rows.join('')
}

function buildTable (path, links) {
return `
<table class="table table-striped">
<tbody>
<tr>
<td class="narrow">
<div class="ipfs-icon ipfs-_blank">&nbsp;</div>
</td>
<td class="padding">
<a href="${getParentHref(path)}">..</a>
</td>
<td></td>
</tr>
${buildFilesList(path, links)}
</tbody>
</table>
`
}

function render (path, links) {
return `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>${path}</title>
<style>${mainStyle}</style>
</head>
<body>
<div id="header" class="row">
<div class="col-xs-2">
<div id="logo" class="ipfs-logo"></div>
</div>
/**
* @param {string} path
* @param {({ Name: string, Tsize: number })[]} links
*/
export function render (path, links) {
return ejs.render(`<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title><%= path %></title>
<style>${style}</style>
</head>
<body>
<div id="header" class="row">
<div class="col-xs-2">
<div id="logo" class="ipfs-logo"></div>
</div>
</div>
<br>
<div class="col-xs-12">
<div class="panel panel-default">
<div class="panel-heading">
<strong>Index of <%= path %></strong>
</div>
<br>
<div class="col-xs-12">
<div class="panel panel-default">
<div class="panel-heading">
<strong>Index of ${path}</strong>
</div>
${buildTable(path, links)}
</div>
</div>
</body>
</html>
`
<table class="table table-striped">
<tbody>
<tr>
<td class="narrow">
<div class="ipfs-icon ipfs-_blank">&nbsp;</div>
</td>
<td class="padding">
<a href="<%= parentHref %>">..</a>
</td>
<td></td>
</tr>
<% links.forEach(function (link) { %>
<tr>
<td><div class="ipfs-icon ipfs-_blank">&nbsp;</div></td>
<td><a href="<%= link.link %>"><%= link.name %></a></t>
<td><%= link.size %></td>
</td>
</tr>
<% }) %>
</tbody>
</table>
</div>
</div>
</body>
</html>
`, {
path,
links: links.map((link) => ({
name: link.Name,
size: filesize(link.Tsize),
link: `${path}${path.endsWith('/') ? '' : '/'}${link.Name}`
})),
parentHref: getParentHref(path)
})
}

exports.render = render
4 changes: 1 addition & 3 deletions src/dir-view/style.js

Large diffs are not rendered by default.

54 changes: 35 additions & 19 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
/* global Response, Blob */

'use strict'

const toStream = require('it-to-stream')
const concat = require('it-concat')
const toBuffer = require('it-buffer')
const log = require('debug')('ipfs:http:response')

const resolver = require('./resolver')
const pathUtils = require('./utils/path')
const detectContentType = require('./utils/content-type')
// @ts-ignore no types
import toStream from 'it-to-stream'
import concat from 'it-concat'
// @ts-ignore no types
import toBuffer from 'it-buffer'
import debug from 'debug'
import * as ipfsResolver from './resolver.js'
import * as pathUtils from './utils/path.js'
import { detectContentType } from './utils/content-type.js'

const log = debug('ipfs:http:response')

// TODO: pass path and add Etag and X-Ipfs-Path + tests
const getHeader = (status = 200, statusText = 'OK', headers = {}) => ({
Expand All @@ -18,21 +19,27 @@ const getHeader = (status = 200, statusText = 'OK', headers = {}) => ({
headers
})

// handle hash resolve error (simple hash, test for directory now)
/**
* handle hash resolve error (simple hash, test for directory now)
*
* @param {*} node
* @param {string} path
* @param {*} error
*/
const handleResolveError = async (node, path, error) => {
const errorString = error.toString()

if (errorString.includes('dag node is a directory')) {
try {
const content = await resolver.directory(node, path, error.cid)
const content = await ipfsResolver.directory(node, path, error.cid)
// dir render
if (typeof content === 'string') {
return new Response(content, getHeader(200, 'OK', { 'Content-Type': 'text/html' }))
}

// redirect to dir entry point (index)
return Response.redirect(pathUtils.joinURLParts(path, content[0].Name))
} catch (error) {
} catch (/** @type {any} */ error) {
log(error)
return new Response(errorString, getHeader(500, error.toString()))
}
Expand All @@ -49,14 +56,20 @@ const handleResolveError = async (node, path, error) => {
return new Response(errorString, getHeader(500, errorString))
}

const getResponse = async (ipfsNode, ipfsPath) => {
/**
*
* @param {*} ipfsNode
* @param {*} ipfsPath
* @returns
*/
export async function getResponse (ipfsNode, ipfsPath) {
// remove trailing slash for files if needed
if (ipfsPath.endsWith('/')) {
return Response.redirect(pathUtils.removeTrailingSlash(ipfsPath))
}

try {
const resolvedData = await resolver.cid(ipfsNode, ipfsPath)
const resolvedData = await ipfsResolver.cid(ipfsNode, ipfsPath)
const { source, contentType } = await detectContentType(ipfsPath, ipfsNode.cat(resolvedData.cid))

if (typeof Blob === 'undefined') {
Expand All @@ -74,7 +87,7 @@ const getResponse = async (ipfsNode, ipfsPath) => {
return contentType
? new Response(blob, getHeader(200, 'OK', { 'Content-Type': contentType }))
: new Response(blob, getHeader())
} catch (err) {
} catch (/** @type {any} */ err) {
return new Response(err.toString(), getHeader(500, 'Error fetching the file'))
}
} catch (error) {
Expand All @@ -83,7 +96,10 @@ const getResponse = async (ipfsNode, ipfsPath) => {
}
}

module.exports = {
getResponse,
resolver
export const resolver = {
...ipfsResolver
}

export const utils = {
detectContentType
}
Loading

0 comments on commit 319e2b4

Please sign in to comment.