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

chore: move gateway response into gateway #3856

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions packages/ipfs-http-gateway/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,22 +45,34 @@
"@hapi/boom": "^9.1.0",
"@hapi/hapi": "^20.0.0",
"debug": "^4.1.1",
"ejs": "^3.1.6",
"file-type": "^16.5.3",
"filesize": "^8.0.0",
"hapi-pino": "^8.3.0",
"ipfs-core-types": "^0.7.1",
"ipfs-http-response": "^0.7.0",
"is-ipfs": "^6.0.1",
"it-buffer": "^0.1.3",
"it-concat": "^2.0.0",
"it-last": "^1.0.4",
"it-reader": "^3.0.0",
"it-to-stream": "^1.0.0",
"joi": "^17.2.1",
"mime-types": "^2.1.32",
"multiformats": "^9.4.1",
"p-try-each": "^1.0.1",
"uint8arrays": "^3.0.0",
"uri-to-multiaddr": "^6.0.0"
},
"devDependencies": {
"@types/hapi-pino": "^8.0.1",
"@types/ejs": "^3.1.0",
"@types/hapi__hapi": "^20.0.5",
"@types/hapi-pino": "^8.0.1",
"aegir": "^35.0.3",
"file-type": "^16.0.0",
"get-stream": "^6.0.1",
"ipfs-core": "^0.10.5",
"ipfsd-ctl": "^10.0.3",
"it-all": "^1.0.5",
"rimraf": "^3.0.2",
"sinon": "^11.1.1"
}
Expand Down
6 changes: 2 additions & 4 deletions packages/ipfs-http-gateway/src/resources/gateway.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,8 @@ const Ammo = require('@hapi/ammo') // HTTP Range processing utilities
const last = require('it-last')
const { CID } = require('multiformats/cid')
const { base32 } = require('multiformats/bases/base32')
// @ts-ignore no types
const { resolver } = require('ipfs-http-response')
// @ts-ignore no types
const detectContentType = require('ipfs-http-response/src/utils/content-type')
const resolver = require('../utils/resolver')
const { detectContentType } = require('../utils/content-type')
const isIPFS = require('is-ipfs')
// @ts-ignore no types
const toStream = require('it-to-stream')
Expand Down
52 changes: 52 additions & 0 deletions packages/ipfs-http-gateway/src/utils/content-type.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
'use strict'

const fileType = require('file-type')
// @ts-ignore no types
const mime = require('mime-types')
// @ts-ignore no types
const Reader = require('it-reader')

const minimumBytes = 4100

/**
* @param {string} path
* @param {AsyncIterable<Uint8Array>} source
* @returns {Promise<{ source: AsyncIterable<Uint8Array>, contentType?: string }>}
*/
const detectContentType = async (path, source) => {
let fileSignature

// try to guess the filetype based on the first bytes
// note that `file-type` doesn't support svgs, therefore we assume it's a svg if path looks like it
if (!path.endsWith('.svg')) {
try {
const reader = Reader(source)
const { value, done } = await reader.next(minimumBytes)

if (done) return { source: reader }

fileSignature = await fileType.fromBuffer(value.slice())

source = (async function * () { // eslint-disable-line require-await
yield value
yield * reader
})()
} catch (/** @type {any} */ err) {
if (err.code !== 'ERR_UNDER_READ') throw err

// not enough bytes for sniffing, just yield the data
source = (async function * () { // eslint-disable-line require-await
yield err.buffer // these are the bytes that were read (if any)
})()
}
}

// if we were unable to, fallback to the `path` which might contain the extension
const mimeType = mime.lookup(fileSignature ? fileSignature.ext : path)

return { source, contentType: mime.contentType(mimeType) }
}

module.exports = {
detectContentType
}
82 changes: 82 additions & 0 deletions packages/ipfs-http-gateway/src/utils/dir-view/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
'use strict'

const filesize = require('filesize')
const style = require('./style.js')
const { cidArray } = require('../path')
const ejs = require('ejs')

/**
* @param {string} path
*/
function getParentHref (path) {
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
}

/**
* @param {string} path
* @param {({ Name: string, Tsize: number })[]} links
*/
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>
<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)
})
}

module.exports = {
render
}
16 changes: 16 additions & 0 deletions packages/ipfs-http-gateway/src/utils/dir-view/style.js

Large diffs are not rendered by default.

50 changes: 47 additions & 3 deletions packages/ipfs-http-gateway/src/utils/path.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,52 @@ function joinURLParts (...urls) {
return urls.join('/')
}

/**
* Converts path or url to an array starting at CID
*
* @param {string} path
*/
function cidArray (path) {
if (path[path.length - 1] === '/') {
path = path.substring(0, path.length - 1)
}
// skip /ipxs/ prefix
if (path.match(/^\/ip[fn]s\//)) {
path = path.substring(6)
}
// skip ipxs:// protocol
if (path.match(/^ip[fn]s:\/\//)) {
path = path.substring(7)
}
return path.split('/')
}

/**
* @param {string} url
*/
function removeLeadingSlash (url) {
if (url[0] === '/') {
url = url.substring(1)
}

return url
}

/**
* @param {string} url
*/
function removeSlashFromBothEnds (url) {
url = removeLeadingSlash(url)
url = removeTrailingSlash(url)

return url
}

module.exports = {
splitPath: splitPath,
removeTrailingSlash: removeTrailingSlash,
joinURLParts: joinURLParts
splitPath,
removeLeadingSlash,
removeTrailingSlash,
joinURLParts,
removeSlashFromBothEnds,
cidArray
}
75 changes: 75 additions & 0 deletions packages/ipfs-http-gateway/src/utils/resolver.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
'use strict'

const pTryEach = require('p-try-each')
const { render } = require('./dir-view')

const INDEX_HTML_FILES = [
'index.html',
'index.htm',
'index.shtml'
]

/**
* @param {*} ipfs
* @param {*} path
*/
const findIndexFile = (ipfs, path) => {
return pTryEach(INDEX_HTML_FILES.map(file => {
return async () => {
const stats = await ipfs.files.stat(`${path}/${file}`)

return {
name: file,
cid: stats.cid
}
}
}))
}

/**
* @param {*} ipfs
* @param {string} path
* @param {*} cid
*/
const directory = async (ipfs, path, cid) => {
// Test if it is a Website
try {
const res = await findIndexFile(ipfs, path)

return [{ Name: res.name }]
} catch (/** @type {any} */ err) {
if (err.message.includes('does not exist')) {
// not a website, just show a directory listing
const result = await ipfs.dag.get(cid)

return render(path, result.value.Links)
}

throw err
}
}

/**
* @param {*} ipfs
* @param {string} path
*/
const cid = async (ipfs, path) => {
const stats = await ipfs.files.stat(path)

if (stats.type.includes('directory')) {
const err = Object.assign(new Error('This dag node is a directory'), {
cid: stats.cid,
fileName: stats.fileName,
dagDirType: stats.type
})

throw err
}

return { cid: stats.cid }
}

module.exports = {
directory,
cid
}
Loading