git ssb

0+

cel / ssb-exec



Tree: 83e13c780bb0d57b34ae589acedd7c977b24cb88

Files: 83e13c780bb0d57b34ae589acedd7c977b24cb88 / old.js

7445 bytesRaw
1var path = require('path')
2var url = require('url')
3var pull = require('pull-stream')
4var fs = require('fs')
5var memo = require('asyncmemo')
6var toPull = require('stream-to-pull-stream')
7
8function redirect(res, dest) {
9 res.writeHead(302, {
10 Location: dest
11 })
12 res.end()
13}
14
15function 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
29function 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
60function ifFunction(fn) {
61 return typeof fn === 'function' ? fn : null
62}
63
64function 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
79exports.name = 'exec.js'
80exports.version = '1.0.0'
81exports.manifest = {
82 callAsync: 'async',
83 callDuplex: 'duplex',
84 callSource: 'source',
85 callSink: 'sink',
86 clearCache: 'async'
87}
88exports.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}
266

Built with git-ssb-web