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

Commit b86978a

Browse files
daviddiasdryajov
authored andcommitted
feat: add gateway to ipfs daemon
* feat: add HTTP Gateway to the js-ipfs daemon License: MIT Signed-off-by: Yahya <[email protected]> * apply remaining CR * chore: update deps * test: fix failing gateway tests with this one simple trick! (#1006) * fix failing tests with this one simple trick! * adding files to make tests self-contained * changes requested by VictorBjelkholm #968 * chore: fix circle CI chrome build (#1008) (#1009) Thanks to mkg20001
1 parent 025e428 commit b86978a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+738
-23
lines changed

gulpfile.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const gulp = require('gulp')
44
const parallel = require('async/parallel')
55
const series = require('async/series')
66
const createTempRepo = require('./test/utils/create-repo-nodejs.js')
7-
const HTTPAPI = require('./src/http-api')
7+
const HTTPAPI = require('./src/http')
88
const leftPad = require('left-pad')
99

1010
let nodes = []

package.json

+14-10
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"test:unit:node": "gulp test:node",
2828
"test:unit:node:core": "TEST=core npm run test:unit:node",
2929
"test:unit:node:http": "TEST=http npm run test:unit:node",
30+
"test:unit:node:gateway": "TEST=gateway npm run test:unit:node",
3031
"test:unit:node:cli": "TEST=cli npm run test:unit:node",
3132
"test:unit:browser": "gulp test:browser",
3233
"test:interop": "npm run test:interop:node",
@@ -92,31 +93,33 @@
9293
"async": "^2.5.0",
9394
"bl": "^1.2.1",
9495
"boom": "^5.2.0",
95-
"cids": "~0.5.1",
9696
"debug": "^3.0.1",
97+
"cids": "^0.5.1",
98+
"file-type": "^6.1.0",
99+
"filesize": "^3.5.10",
97100
"fsm-event": "^2.1.0",
98101
"glob": "^7.1.2",
99102
"hapi": "^16.5.2",
100103
"hapi-set-header": "^1.0.2",
101104
"hoek": "^4.2.0",
102-
"ipfs-api": "^14.3.3",
105+
"ipfs-api": "^14.3.4",
103106
"ipfs-bitswap": "~0.17.2",
104107
"ipfs-block": "~0.6.0",
105108
"ipfs-block-service": "~0.12.0",
106109
"ipfs-multipart": "~0.1.0",
107110
"ipfs-repo": "~0.17.0",
108111
"ipfs-unixfs": "~0.1.13",
109-
"ipfs-unixfs-engine": "~0.22.3",
112+
"ipfs-unixfs-engine": "~0.22.4",
110113
"ipld-resolver": "~0.13.2",
111114
"is-ipfs": "^0.3.0",
112115
"is-stream": "^1.1.0",
113116
"joi": "^10.6.0",
114-
"libp2p": "~0.12.3",
115-
"libp2p-floodsub": "~0.11.0",
116-
"libp2p-kad-dht": "~0.5.0",
117-
"libp2p-mdns": "~0.9.0",
117+
"libp2p": "~0.12.4",
118+
"libp2p-floodsub": "~0.11.1",
119+
"libp2p-kad-dht": "~0.5.1",
120+
"libp2p-mdns": "~0.9.1",
118121
"libp2p-multiplex": "~0.5.0",
119-
"libp2p-railing": "~0.7.0",
122+
"libp2p-railing": "~0.7.1",
120123
"libp2p-secio": "~0.8.1",
121124
"libp2p-tcp": "~0.11.0",
122125
"libp2p-webrtc-star": "~0.13.1",
@@ -126,13 +129,14 @@
126129
"lodash.sortby": "^4.7.0",
127130
"lodash.values": "^4.3.0",
128131
"mafmt": "^3.0.1",
132+
"mime-types": "^2.1.17",
129133
"mkdirp": "~0.5.1",
130134
"multiaddr": "^3.0.1",
131135
"multihashes": "~0.4.9",
132136
"once": "^1.4.0",
133137
"path-exists": "^3.0.0",
134-
"peer-book": "~0.5.0",
135-
"peer-id": "~0.10.0",
138+
"peer-book": "~0.5.1",
139+
"peer-id": "~0.10.1",
136140
"peer-info": "~0.11.0",
137141
"promisify-es6": "^1.0.3",
138142
"pull-file": "^1.0.0",

src/cli/commands/daemon.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict'
22

3-
const HttpAPI = require('../../http-api')
3+
const HttpAPI = require('../../http')
44
const utils = require('../utils')
55
const print = utils.print
66

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

src/http/gateway/dir-view/index.js

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
'use strict'
2+
3+
const filesize = require('filesize')
4+
5+
const mainStyle = require('./style')
6+
const pathUtil = require('../utils/path')
7+
8+
function getParentDirectoryURL (originalParts) {
9+
const parts = originalParts.slice()
10+
11+
if (parts.length > 1) {
12+
parts.pop()
13+
}
14+
15+
return [ '', 'ipfs' ].concat(parts).join('/')
16+
}
17+
18+
function buildFilesList (path, links) {
19+
const rows = links.map((link) => {
20+
let row = [
21+
`<div class="ipfs-icon ipfs-_blank">&nbsp;</div>`,
22+
`<a href="${pathUtil.joinURLParts(path, link.name)}">${link.name}</a>`,
23+
filesize(link.size)
24+
]
25+
26+
row = row.map((cell) => `<td>${cell}</td>`).join('')
27+
28+
return `<tr>${row}</tr>`
29+
})
30+
31+
return rows.join('')
32+
}
33+
34+
function buildTable (path, links) {
35+
const parts = pathUtil.splitPath(path)
36+
const parentDirectoryURL = getParentDirectoryURL(parts)
37+
38+
return `
39+
<table class="table table-striped">
40+
<tbody>
41+
<tr>
42+
<td class="narrow">
43+
<div class="ipfs-icon ipfs-_blank">&nbsp;</div>
44+
</td>
45+
<td class="padding">
46+
<a href="${parentDirectoryURL}">..</a>
47+
</td>
48+
<td></td>
49+
</tr>
50+
${buildFilesList(path, links)}
51+
</tbody>
52+
</table>
53+
`
54+
}
55+
56+
function render (path, links) {
57+
return `
58+
<!DOCTYPE html>
59+
<html>
60+
<head>
61+
<meta charset="utf-8">
62+
<title>${path}</title>
63+
<style>${mainStyle}</style>
64+
</head>
65+
<body>
66+
<div id="header" class="row">
67+
<div class="col-xs-2">
68+
<div id="logo" class="ipfs-logo"></div>
69+
</div>
70+
</div>
71+
<br>
72+
<div class="col-xs-12">
73+
<div class="panel panel-default">
74+
<div class="panel-heading">
75+
<strong>Index of ${path}</strong>
76+
</div>
77+
${buildTable(path, links)}
78+
</div>
79+
</div>
80+
</body>
81+
</html>
82+
`
83+
}
84+
85+
exports = module.exports
86+
exports.render = render

src/http/gateway/dir-view/style.js

+16
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/http/gateway/resolver.js

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
'use strict'
2+
3+
const mh = require('multihashes')
4+
const promisify = require('promisify-es6')
5+
const reduce = require('async/reduce')
6+
const CID = require('cids')
7+
const Unixfs = require('ipfs-unixfs')
8+
const debug = require('debug')
9+
const log = debug('jsipfs:http-gateway:resolver')
10+
log.error = debug('jsipfs:http-gateway:resolver:error')
11+
12+
const dirView = require('./dir-view')
13+
const pathUtil = require('./utils/path')
14+
15+
function getIndexFiles (links) {
16+
const INDEX_HTML_FILES = [
17+
'index.html',
18+
'index.htm',
19+
'index.shtml'
20+
]
21+
22+
return links.filter((link) => INDEX_HTML_FILES.indexOf(link.name) !== -1)
23+
}
24+
25+
const resolveDirectory = promisify((ipfs, path, multihash, callback) => {
26+
mh.validate(mh.fromB58String(multihash))
27+
28+
ipfs.object.get(multihash, { enc: 'base58' }, (err, dagNode) => {
29+
if (err) { return callback(err) }
30+
31+
const indexFiles = getIndexFiles(dagNode.links)
32+
33+
if (indexFiles.length > 0) {
34+
return callback(null, indexFiles)
35+
}
36+
37+
return callback(null, dirView.render(path, dagNode.links))
38+
})
39+
})
40+
41+
const resolveMultihash = promisify((ipfs, path, callback) => {
42+
const parts = pathUtil.splitPath(path)
43+
let firstMultihash = parts.shift()
44+
let currentCid
45+
46+
reduce(parts, firstMultihash, (memo, item, next) => {
47+
try {
48+
currentCid = new CID(mh.fromB58String(memo))
49+
} catch (err) {
50+
return next(err)
51+
}
52+
53+
log('memo: ', memo)
54+
log('item: ', item)
55+
56+
ipfs.dag.get(currentCid, (err, result) => {
57+
if (err) { return next(err) }
58+
59+
let dagNode = result.value
60+
// find multihash of requested named-file in current dagNode's links
61+
let multihashOfNextFile
62+
let nextFileName = item
63+
64+
const links = dagNode.links
65+
66+
for (let link of links) {
67+
if (link.name === nextFileName) {
68+
// found multihash of requested named-file
69+
multihashOfNextFile = mh.toB58String(link.multihash)
70+
log('found multihash: ', multihashOfNextFile)
71+
break
72+
}
73+
}
74+
75+
if (!multihashOfNextFile) {
76+
return next(new Error(`no link named "${nextFileName}" under ${memo}`))
77+
}
78+
79+
next(null, multihashOfNextFile)
80+
})
81+
}, (err, result) => {
82+
if (err) { return callback(err) }
83+
84+
let cid
85+
try {
86+
cid = new CID(mh.fromB58String(result))
87+
} catch (err) {
88+
return callback(err)
89+
}
90+
91+
ipfs.dag.get(cid, (err, dagResult) => {
92+
if (err) return callback(err)
93+
94+
let dagDataObj = Unixfs.unmarshal(dagResult.value.data)
95+
if (dagDataObj.type === 'directory') {
96+
let isDirErr = new Error('This dag node is a directory')
97+
// add memo (last multihash) as a fileName so it can be used by resolveDirectory
98+
isDirErr.fileName = result
99+
return callback(isDirErr)
100+
}
101+
102+
callback(null, { multihash: result })
103+
})
104+
})
105+
})
106+
107+
module.exports = {
108+
resolveDirectory: resolveDirectory,
109+
resolveMultihash: resolveMultihash
110+
}

0 commit comments

Comments
 (0)