Last active
January 9, 2018 07:10
-
-
Save Gozala/e52439ba42fcad635667cfacee5d8271 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const path = require("path") | |
const url = require("url") | |
const IPFS = require("ipfs") | |
const mime = require("mime-types") | |
const fileType = require("file-type") | |
const unixfs = require("ipfs-unixfs-engine") | |
const pull = require("pull-stream") | |
const toStream = require("pull-stream-to-stream") | |
const peek = require("pull-peek") | |
class FileSystem { | |
constructor(node) { | |
this.node = node | |
this.ipldResolver = node._ipldResolver | |
} | |
static normalizePath(path) { | |
const path2 = path.startsWith("/ipfs/") ? path.substr(6) : path | |
const path3 = path2.endsWith("/") | |
? path2.substr(0, path2.length - 1) | |
: path2 | |
return path3 | |
} | |
entry(ipfsPath) { | |
return new Promise((resolve, reject) => { | |
const entryPath = FileSystem.normalizePath(ipfsPath) | |
const maxDepth = entryPath.split("/").length | |
return pull( | |
unixfs.exporter(entryPath, this.ipldResolver, { maxDepth }), | |
pull.collect((error, entries) => { | |
if (error) { | |
if (error.message.startsWith("multihash length inconsistent")) { | |
reject(new InvalidCID(error)) | |
} else if (error.message.startsWith("Non-base58 character")) { | |
reject(new InvalidCID(error)) | |
} else { | |
reject(new IOError(error)) | |
} | |
} else { | |
switch (entries.length) { | |
case 0: | |
return reject(new EntryNotFound(entryPath)) | |
case 1: { | |
const [node] = entries | |
return resolve(new FileEntry(path.dirname(entryPath), node)) | |
} | |
default: { | |
const [node, ...nodes] = entries | |
return resolve( | |
new DirectoryEntry(this, path.dirname(entryPath), node, nodes) | |
) | |
} | |
} | |
} | |
}) | |
) | |
}) | |
} | |
} | |
class Entry { | |
static from(fs, parentPath, node) { | |
if (node.type === "file") { | |
return new FileEntry(parentPath, node) | |
} else { | |
return new DirectoryEntry(fs, parentPath, node, null) | |
} | |
} | |
} | |
class FileEntry { | |
constructor(parentPath, node) { | |
this.node = node | |
this.name = node.name | |
this.path = `/ipfs/${parentPath}/${this.name}` | |
this.hash = node.hash | |
this.type = "file" | |
} | |
peek() { | |
if (this._nodeContent == null) { | |
this._fileType = new Promise((resolve, reject) => { | |
this._nodeContent = pull( | |
this.node.content, | |
peek((end, data) => { | |
resolve(fileType(data)) | |
}) | |
) | |
}) | |
} | |
return this | |
} | |
fileType() { | |
return this.peek()._fileType | |
} | |
contentStream() { | |
return toStream.source(this.peek()._nodeContent) | |
} | |
contentBuffer() { | |
return new Promise((resolve, reject) => { | |
pull( | |
this.peek()._nodeContent, | |
pull.collect((error, chunks) => { | |
if (error) reject(error) | |
else resolve(Buffer.concat(chunks)) | |
}) | |
) | |
}) | |
} | |
} | |
class DirectoryEntry { | |
constructor(fs, parentPath, node, nodes) { | |
this.fs = fs | |
this.nodes = nodes | |
this.node = node | |
this.name = node.name | |
this.path = path.normalize(`/ipfs/${parentPath}/${this.name}/`) | |
this.hash = node.hash | |
this.type = "directory" | |
} | |
async entries() { | |
if (this._entries) { | |
return this._entries | |
} else if (this.nodes) { | |
this._entries = this.nodes.map(node => | |
Entry.from(this.fs, this.path, node) | |
) | |
return this._entries | |
} else { | |
const directory = await this.fs.entry(this.path) | |
this._entries = await directory.entries() | |
return this._entries | |
} | |
} | |
} | |
class IOError { | |
constructor(error) { | |
this.code = 500 | |
this.reason = error | |
this.stack = error.stack | |
this.message = error.message | |
} | |
} | |
class InvalidCID { | |
constructor(error) { | |
this.code = 404 | |
this.reason = error | |
this.stack = error.stack | |
this.message = error.message | |
} | |
} | |
class EntryNotFound { | |
constructor(path) { | |
this.code = 404 | |
this.path = path | |
this.stack = new Error().stack | |
this.message = this.toString() | |
} | |
toString() { | |
return `Entry \`/ipfs/${this.path}\` not found` | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment