Files: 83e13c780bb0d57b34ae589acedd7c977b24cb88 / old.js
7445 bytesRaw
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 | } |
266 |
Built with git-ssb-web