git ssb

0+

cel / ssb-exec



Commit 93078b1b9a4a2049ae2a376b7285913e90c0da43

Init

cel committed on 2/5/2019, 5:54:37 AM

Files changed

index.jsadded
loader.jsadded
old.jsadded
package.jsonadded
index.jsView
@@ -1,0 +1,74 @@
1 +var url = require('url')
2 +var path = require('path')
3 +var fs = require('fs')
4 +
5 +exports.name = 'exec'
6 +exports.version = '1.0.0'
7 +exports.manifest = {
8 +}
9 +exports.init = function (sbot, config) {
10 + if (!sbot.ws || !sbot.ws.use) return console.trace('missing ssb-ws')
11 + var conf = config.exec || {}
12 + var prefix = conf.prefix || '/exec'
13 + var dir = path.join(config.path, conf.dir || 'blobs/exec/scripts')
14 + var mtimes = {}
15 + console.log('[exec] init. dir:', dir, 'prefix:', prefix)
16 +
17 + function getModule(resolved, tried, cb) {
18 + try {
19 + var module = require(resolved)
20 + return cb(null, module)
21 + } catch(e) {
22 + if (!tried && e && (e.code === 'ENOENT' || e.code === 'MODULE_NOT_FOUND')) {
23 + var m = /(sha256)\/([0-9a-f]{2})\/([0-9a-f]{62})/.exec(e.path || e.message)
24 + if (!m) throw e
25 + var blobId = '&' + new Buffer(m[2] + m[3], 'hex').toString('base64') + '.' + m[1]
26 + if (!sbot.blobs) return cb(new Error('Missing ssb-blobs plugin'))
27 + if (typeof sbot.blobs.want !== 'function') return cb(new Error('missing blobs.want method'))
28 + return sbot.blobs.want(blobId, function (err, has) {
29 + if (err) return cb(new Error('Unable to get blob: ' + (err.stack || err)))
30 + if (!has) return cb(new Error('Unable to get blob'))
31 + getModule(resolved, true, cb)
32 + })
33 + }
34 + return cb(e)
35 + }
36 + }
37 +
38 + sbot.ws.use(function (req, res, next) {
39 + if (prefix !== req.url.substr(0, prefix.length)) return next()
40 + if (prefix === req.url) {
41 + res.writeHead(302, {Location: path.basename(prefix) + '/'})
42 + return res.end()
43 + }
44 + try {
45 + req.uri = url.parse(req.url.substr(prefix.length), true)
46 + var resolved = require.resolve(path.join(dir, req.uri.pathname))
47 + var prevMtime = mtimes[resolved]
48 + var mtime = fs.statSync(resolved).mtime.getTime()
49 + if (mtime !== prevMtime) {
50 + delete require.cache[resolved]
51 + }
52 + getModule(resolved, false, gotModule)
53 + } catch(e) {
54 + return gotError(e)
55 + }
56 +
57 + function gotError(err) {
58 + try {
59 + res.writeHead(err.code === 'ENOENT' || err.code === 'MODULE_NOT_FOUND' ? 404 : 500)
60 + } finally {
61 + res.end(err.stack || err)
62 + }
63 + }
64 +
65 + function gotModule(err, module) {
66 + if (err) return gotError(err)
67 + try {
68 + module(req, res, sbot, config)
69 + } catch(e) {
70 + gotError(e)
71 + }
72 + }
73 + })
74 +}
loader.jsView
@@ -1,0 +1,60 @@
1 +var fs = require('fs')
2 +var path = require('path')
3 +
4 +var mainPath = path.resolve(__dirname, 'index.js')
5 +var mtime
6 +var main
7 +function loadMain() {
8 + var stats = fs.statSync(mainPath)
9 + var prevMtime = mtime
10 + mtime = stats.mtime.getTime()
11 + if (mtime !== prevMtime) {
12 + delete require.cache[mainPath]
13 + main = require(mainPath)
14 + var k
15 + if (main.manifest && exports.manifest) {
16 + for (k in main.manifest) exports.manifest[k] = main.manifest[k]
17 + }
18 + for (k in main) exports[k] = main[k]
19 + return true
20 + }
21 +}
22 +loadMain()
23 +
24 +exports.init = function (sbot, config) {
25 + var api
26 + var wsUseHandler
27 + var wsUse = sbot.ws && sbot.ws.use
28 + if (wsUse) sbot.ws.use = function (handler) {
29 + if (!wsUseHandler) wsUseHandler = handler
30 + else wsUse.call(sbot.ws, handler)
31 + }
32 +
33 + function loadApi() {
34 + if (loadMain() || !api) {
35 + var prevApi = api
36 + var init = typeof main === 'function' ? main : main.init
37 + wsUseHandler = null
38 + api = init.call(main, sbot, config)
39 + }
40 + }
41 +
42 + if (wsUse) wsUse(function (req, res, next) {
43 + loadApi()
44 + if (wsUseHandler) wsUseHandler(req, res, next)
45 + else if (next) next()
46 + })
47 +
48 + loadApi()
49 + var wrappedApi = {}
50 + function wrapMethod(method) {
51 + return function () {
52 + loadApi()
53 + return api[method].apply(api, arguments)
54 + }
55 + }
56 + for (var method in main.manifest || api) {
57 + wrappedApi[method] = wrapMethod(method)
58 + }
59 + return wrappedApi
60 +}
old.jsView
@@ -1,0 +1,265 @@
1 +var path = require('path')
2 +var url = require('url')
3 +var pull = require('pull-stream')
4 +var fs = require('fs')
5 +var memo = require('asyncmemo')
6 +var toPull = require('stream-to-pull-stream')
7 +
8 +function redirect(res, dest) {
9 + res.writeHead(302, {
10 + Location: dest
11 + })
12 + res.end()
13 +}
14 +
15 +function asyncSource(fn) {
16 + var next
17 + return function (end, cb) {
18 + if (next) return next(end, cb)
19 + if (end) return cb(end)
20 + fn(function (err, _next) {
21 + if (err) return cb(err)
22 + next = _next
23 + if (typeof next !== 'function') return cb(new TypeError('bad function'))
24 + next(null, cb)
25 + })
26 + }
27 +}
28 +
29 +function asyncDuplex(fn) {
30 + var next
31 + var waiting = 3
32 + var sourceCb, sinkRead, fnErr, fnStream
33 + fn(function (err, stream) {
34 + fnErr = err
35 + fnStream = stream
36 + if (!--waiting) next()
37 + })
38 + function onError(err) {
39 + sourceCb(err)
40 + sinkRead(err, function (err) {
41 + if (err && err !== true) console.trace(err)
42 + })
43 + }
44 + function next() {
45 + if (fnErr) return onError(fnErr)
46 + sourceCb(null, pull(sinkRead, fnStream))
47 + }
48 + return {
49 + source: asyncSource(function (cb) {
50 + sourceCb = cb
51 + if (!--waiting) next()
52 + }),
53 + sink: function (read) {
54 + sinkRead = read
55 + if (!--waiting) next()
56 + }
57 + }
58 +}
59 +
60 +function ifFunction(fn) {
61 + return typeof fn === 'function' ? fn : null
62 +}
63 +
64 +function getWantBlob(blobs, id, cb) {
65 + blobs.size(id, function (err, size) {
66 + if (size == null) blobs.want(id, gotBlobWant)
67 + else gotBlobWant(null, true)
68 + })
69 + function gotBlobWant(err, has) {
70 + if (err) return cb(err)
71 + if (!has) return cb(new Error('missing blob'))
72 + pull(blobs.get(id), pull.collect(function (err, bufs) {
73 + if (err) return cb(err)
74 + cb(null, Buffer.concat(bufs))
75 + }))
76 + }
77 +}
78 +
79 +exports.name = 'exec.js'
80 +exports.version = '1.0.0'
81 +exports.manifest = {
82 + callAsync: 'async',
83 + callDuplex: 'duplex',
84 + callSource: 'source',
85 + callSink: 'sink',
86 + clearCache: 'async'
87 +}
88 +exports.init = function (sbot, config) {
89 + console.log('[exec.js] init')
90 +
91 + var conf = config.exec || {}
92 + var prefix = '/' + (conf.prefix || 'exec')
93 + var dir = path.join(config.path, conf.dir || 'exec')
94 + var blobsDir = path.join(config.path, 'blobs', 'sha256')
95 + var mtimes = {}
96 +
97 + function getBlobModule(id, cb) {
98 + var hexId = new Buffer(id.substr(1, 44), 'base64').toString('hex')
99 + var filename = path.join(hexId.substr(0, 2), hexId.substr(2))
100 + sbot.blobs.size(id, function (err, size) {
101 + if (size == null) sbot.blobs.want(id, gotBlobWant)
102 + else gotBlobWant(null, true)
103 + function gotBlobWant(err, has) {
104 + if (err) return cb(err)
105 + if (!has) return cb(new Error('missing blob'))
106 + var module
107 + try { module = require(filename) }
108 + catch(e) { return cb(e) }
109 + cb(null, module)
110 + }
111 + })
112 + }
113 +
114 + function getFileModule(name, cb) {
115 + var filename = path.join(dir, name || 'index.js')
116 + var self = this
117 + fs.stat(filename, function (err, stat) {
118 + if (err) return cb(err)
119 + var prevMtime = mtimes[filename]
120 + var mtime = stat.mtime.getTime()
121 + if (mtime !== prevMtime) {
122 + if (prevMtime) delete require.cache[filename]
123 + mtimes[filename] = mtime
124 + }
125 + var module
126 + try { module = require(filename) }
127 + catch(e) { return cb(e) }
128 + cb(null, module)
129 + })
130 + }
131 +
132 + function getModule(id, cb) {
133 + if (typeof id !== 'string') return cb(new TypeError('id should be string'))
134 + if (id[0] === '&') getBlobModule(id, cb)
135 + else getFileModule(id, cb)
136 + }
137 +
138 + if (!sbot.ws || !sbot.ws.use) {
139 + console.error('[exec.js] missing sbot.ws.use')
140 + } else sbot.ws.use(function (req, res, next) {
141 + req.uri = url.parse(req.url, true)
142 + if (req.uri.pathname === prefix) {
143 + return redirect(res, path.basename(prefix) + '/')
144 + }
145 + if (req.uri.pathname.substr(0, prefix.length + 1) !== prefix + '/') {
146 + return next && next()
147 + }
148 + var id = req.uri.pathname.substr(prefix.length + 1)
149 + if (id[0] === '&') {
150 + // canonicalize with URL-encoding so that relative URLs
151 + // work (since blob ids may contain "/" )
152 + var dest = prefix + '/' + encodeURIComponent(id)
153 + + (req.uri.search || '')
154 + return redirect(res, dest)
155 + }
156 + try { id = decodeURIComponent(id) }
157 + catch (e) {
158 + res.writeHead(400, {'Content-Type': 'text/plain'})
159 + return res.end('Bad id "' + id + '"')
160 + }
161 + getPlugin(id, function (err, plugin) {
162 + try {
163 + if (err) throw err
164 + if (!plugin) return next()
165 + var opts = {
166 + method: 'GET',
167 + headers: req.headers,
168 + uri: req.uri
169 + }
170 + if (req.method === 'GET' && typeof plugin.get === 'function') {
171 + var source = plugin.get(opts)
172 + if (!source) return res.writeHead(201), res.end()
173 + pull(
174 + source,
175 + toPull.sink(res)
176 + )
177 + } else if (req.method === 'POST' && typeof plugin.post === 'function') {
178 + var through = plugin.post(opts)
179 + if (!through) return req.end(), res.writeHead(201), res.end()
180 + pull(
181 + toPull.source(req),
182 + through,
183 + toPull.sink(res)
184 + )
185 + } else if (typeof plugin.request === 'function') {
186 + var stream = plugin.request(opts)
187 + if (!stream) return req.end(), res.writeHead(201), res.end()
188 + pull(
189 + toPull.source(req),
190 + stream,
191 + toPull.sink(res)
192 + )
193 + } else {
194 + res.writeHead(405, {'Content-Type': 'text/plain'})
195 + return res.end('Method not allowed')
196 + }
197 + } catch(err) {
198 + res.writeHead(500, {'Content-Type': 'text/plain'})
199 + res.end(err.stack || err)
200 + }
201 + })
202 + })
203 +
204 + function callAt(api, method, args) {
205 + var self, fn = api
206 + var parts = method.split('.')
207 + for (var i = 0; i < parts.length; i++) {
208 + self = fn
209 + fn = fn && fn[parts[i]]
210 + }
211 + if (!fn) throw new Error('Missing method')
212 + return fn.apply(self, args)
213 + }
214 +
215 + return {
216 + callAsync: function (id, method/*, args..., cb*/) {
217 + var args = [].slice.call(arguments, 2)
218 + var cb = ifFunction(args[args.length-1]) || console.trace
219 + getPlugin(id, function (err, api) {
220 + if (err) return cb(err)
221 + try {
222 + callAt(api, method, args)
223 + } catch(e) {
224 + cb(e)
225 + }
226 + })
227 + },
228 + callDuplex: function (id, method/*, args...*/) {
229 + var args = [].slice.call(arguments, 2)
230 + return asyncDuplex(function (cb) {
231 + getPlugin(id, function (err, api) {
232 + if (err) return cb(err)
233 + cb(null, callAt(api, method, args))
234 + })
235 + })
236 + },
237 + callSource: function (id, method/*, args...*/) {
238 + var args = [].slice.call(arguments, 2)
239 + return asyncSource(function (cb) {
240 + getPlugin(id, function (err, api) {
241 + if (err) return cb(err)
242 + cb(null, callAt(api, method, args))
243 + })
244 + })
245 + },
246 + callSink: function (id, method/*, args...*/) {
247 + var args = [].slice.call(arguments, 2)
248 + function onEnd(err) {
249 + if (err) console.trace(id, method, err)
250 + }
251 + return function (read) {
252 + getPlugin(id, function (err, api) {
253 + if (err) return read(err, onEnd)
254 + callAt(api, method, args)(read)
255 + })
256 + }
257 + },
258 + clearCache: function (cb) {
259 + getScript.cache.clear()
260 + getBlobModule.cache.clear()
261 + getPluginCached.cache.clear()
262 + cb(null)
263 + }
264 + }
265 +}
package.jsonView
@@ -1,0 +1,3 @@
1 +{
2 + "main": "loader.js"
3 +}

Built with git-ssb-web