Skip to content
This repository has been archived by the owner on Sep 28, 2021. It is now read-only.

Commit

Permalink
feat: load files/dirs from hamt shards (#19)
Browse files Browse the repository at this point in the history
Use the HAMT support and IPFS overlay build in to the mfs related
files commands to enable loading files from HAMT shards.
  • Loading branch information
achingbrain authored and vasco-santos committed Jun 6, 2019
1 parent 9a88d61 commit 25edfbc
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 116 deletions.
2 changes: 1 addition & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const response = (ipfsNode, ipfsPath) => {
return new Promise((resolve, reject) => {
// switch case with true feels so wrong.
switch (true) {
case (errorString === 'Error: This dag node is a directory'):
case (errorString.includes('dag node is a directory')):
resolver.directory(node, path, error.cid)
.then((content) => {
// dir render
Expand Down
167 changes: 52 additions & 115 deletions src/resolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,142 +2,79 @@

const mh = require('multihashes')
const promisify = require('promisify-es6')
const reduce = require('async/reduce')
const CID = require('cids')
const Unixfs = require('ipfs-unixfs')
const debug = require('debug')
const log = debug('ipfs:http:response:resolver')

const tryEach = require('async/tryEach')
const waterfall = require('async/waterfall')
const log = debug('jsipfs:http:response:resolver')
log.error = debug('jsipfs:http:response:resolver:error')
const dirView = require('./dir-view')
const pathUtil = require('./utils/path')

function getIndexFiles (links) {
const INDEX_HTML_FILES = [
'index.html',
'index.htm',
'index.shtml'
]
// directory
let indexes = links.filter((link) => INDEX_HTML_FILES.indexOf(link.Name) !== -1)
if (indexes.length) {
return indexes
}
// hamt-sharded-directory uses a 2 char prefix
return links.filter((link) => {
return link.Name.length > 2 && INDEX_HTML_FILES.indexOf(link.Name.substring(2)) !== -1
})
const INDEX_HTML_FILES = [
'index.html',
'index.htm',
'index.shtml'
]

const findIndexFile = (ipfs, path, callback) => {
return tryEach(INDEX_HTML_FILES.map(file => {
return (cb) => {
waterfall([
(cb) => ipfs.files.stat(`${path}/${file}`, cb),
(stats, cb) => cb(null, {
name: file,
cid: new CID(stats.hash)
})
], cb)
}
}), callback)
}

const directory = promisify((ipfs, path, cid, callback) => {
cid = new CID(cid)

ipfs.object.get(cid.buffer, (err, dagNode) => {
// Test if it is a Website
findIndexFile(ipfs, path, (err, res) => {
if (err) {
return callback(err)
}
if (err.message.includes('does not exist')) {
// not a website, just show a directory listing
return ipfs.dag.get(cid, (err, result) => {
if (err) {
return callback(err)
}

// Test if it is a Website
const indexFiles = getIndexFiles(dagNode.Links)
return callback(null, dirView.render(path, result.value.Links))
})
}

if (indexFiles.length) {
return callback(null, indexFiles)
return callback(err)
}

return callback(null, dirView.render(path, dagNode.Links))
callback(err, [{
Name: res.name
}])
})
})

const cid = promisify((ipfs, path, callback) => {
const parts = pathUtil.cidArray(path)
let firstCid = parts.shift()
let currentCid

// TODO: replace below with ipfs.resolve(path, {recursive: true})
// (requires changes to js-ipfs/js-ipfs-api)

reduce(
parts,
firstCid,
(memo, item, next) => {
try {
currentCid = new CID(memo)
} catch (err) {
return next(err)
}

log('memo: ', memo)
log('item: ', item)

ipfs.dag.get(currentCid, (err, result) => {
if (err) {
return next(err)
}

const dagNode = result.value
// find multihash/cid of requested named-file in current dagNode's links
let cidOfNextFile
const nextFileName = item

try {
for (let link of dagNode.Links) {
if (link.Name === nextFileName) {
cidOfNextFile = link.Hash
break
}
}
} catch (err) {
return next(err)
}

if (!cidOfNextFile) {
const missingLinkErr = new Error(`no link named "${nextFileName}" under ${memo}`)
missingLinkErr.parentDagNode = memo
missingLinkErr.missingLinkName = nextFileName
return next(missingLinkErr)
}

next(null, cidOfNextFile)
})
}, (err, cid) => {
if (err) {
return callback(err)
}

try {
cid = new CID(cid)
} catch (err) {
return callback(err)
}
ipfs.files.stat(path, (err, stats) => {
if (err) {
return callback(err)
}

if (cid.codec === 'raw') {
// no need for additional lookup, its raw data
callback(null, { cid })
}
const cid = new CID(stats.hash)

ipfs.dag.get(cid, (err, dagResult) => {
if (err) {
return callback(err)
}
if (stats.type.includes('directory')) {
const err = new Error('This dag node is a directory')
err.cid = cid
err.fileName = stats.name
err.dagDirType = stats.type

try {
let dagDataObj = Unixfs.unmarshal(dagResult.value.Data)
// There are at least two types of directories:
// - "directory"
// - "hamt-sharded-directory" (example: QmT5NvUtoM5nWFfrQdVrFtvGfKFmG7AHE8P34isapyhCxX)
if (dagDataObj.type === 'directory' || dagDataObj.type === 'hamt-sharded-directory') {
let isDirErr = new Error('This dag node is a directory')
// store memo of last multihash so it can be used by directory
isDirErr.cid = isDirErr.fileName = cid
isDirErr.dagDirType = dagDataObj.type
return callback(isDirErr)
}
} catch (err) {
return callback(err)
}
return callback(err)
}

callback(null, { cid })
})
callback(err, {
cid
})
})
})

const multihash = promisify((ipfs, path, callback) => {
Expand Down

0 comments on commit 25edfbc

Please sign in to comment.