1 | var Bluebird = require('bluebird');
|
2 | var CronJob = require('./cronJob');
|
3 | var Decode = require('jwt-decode');
|
4 | var LogStream = require('webtask-log-stream');
|
5 | var RandExp = require('randexp');
|
6 | var Request = require('./issueRequest');
|
7 | var Superagent = require('superagent');
|
8 | var Webtask = require('./webtask');
|
9 |
|
10 | var defaults = require('lodash.defaults');
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 | module.exports = Sandbox;
|
19 | module.exports.PARSE_NEVER = 0;
|
20 | module.exports.PARSE_ALWAYS = 1;
|
21 | module.exports.PARSE_ON_ARITY = 2;
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 | function Sandbox (options) {
|
36 | var securityVersion = 'v1';
|
37 |
|
38 | this.url = options.url;
|
39 | this.container = options.container;
|
40 | this.token = options.token;
|
41 | this.onBeforeRequest = []
|
42 | .concat(options.onBeforeRequest)
|
43 | .filter(hook => typeof hook === 'function');
|
44 |
|
45 | try {
|
46 | var typ = Decode(options.token, { header: true }).typ;
|
47 |
|
48 | if (typ && typ.toLowerCase() === 'jwt') {
|
49 | securityVersion = 'v2';
|
50 | }
|
51 | } catch (_) {
|
52 |
|
53 | }
|
54 |
|
55 | this.securityVersion = securityVersion;
|
56 | }
|
57 |
|
58 |
|
59 |
|
60 |
|
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 | Sandbox.prototype.clone = function (options) {
|
68 | return new Sandbox({
|
69 | url: options.url || this.url,
|
70 | container: options.container || this.container,
|
71 | token: options.token || this.token,
|
72 | onBeforeRequest: options.onBeforeRequest || this.onBeforeRequest,
|
73 | });
|
74 | };
|
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
83 |
|
84 | Sandbox.prototype.create = function (codeOrUrl, options, cb) {
|
85 | if (typeof codeOrUrl !== 'string') {
|
86 | cb = options;
|
87 | options = codeOrUrl;
|
88 | codeOrUrl = typeof options.code === 'string'
|
89 | ? options.code
|
90 | : options.code_url;
|
91 | }
|
92 |
|
93 | if (typeof options === 'function') {
|
94 | cb = options;
|
95 | options = {};
|
96 | }
|
97 |
|
98 | if (!options) options = {};
|
99 |
|
100 | var fol = codeOrUrl.toLowerCase();
|
101 |
|
102 | if (fol.indexOf('http://') === 0 || fol.indexOf('https://') === 0) {
|
103 | options.code_url = codeOrUrl;
|
104 | } else {
|
105 | options.code = codeOrUrl;
|
106 | }
|
107 |
|
108 | var self = this;
|
109 | var token_options = defaults({}, options, { include_webtask_url: true });
|
110 | var promise = this.createToken(token_options)
|
111 | .then(function (result) {
|
112 | return token_options.include_webtask_url
|
113 | ? new Webtask(self, result.token, { meta: options.meta, webtask_url: result.webtask_url })
|
114 | : new Webtask(self, result, { meta: options.meta });
|
115 | });
|
116 |
|
117 | return cb ? promise.nodeify(cb) : promise;
|
118 | };
|
119 |
|
120 |
|
121 |
|
122 |
|
123 |
|
124 |
|
125 |
|
126 |
|
127 | Sandbox.prototype.createRaw = function (claims, cb) {
|
128 | var self = this;
|
129 |
|
130 | var promise = this.createTokenRaw(claims)
|
131 | .then(function (token) {
|
132 | return new Webtask(self, token, { meta: claims.meta });
|
133 | });
|
134 |
|
135 | return cb ? promise.nodeify(cb) : promise;
|
136 | };
|
137 |
|
138 |
|
139 |
|
140 |
|
141 |
|
142 |
|
143 |
|
144 |
|
145 | Sandbox.prototype.createUrl = function (options, cb) {
|
146 | var promise = this.create(options)
|
147 | .get('url');
|
148 |
|
149 | return cb ? promise.nodeify(cb) : promise;
|
150 | };
|
151 |
|
152 |
|
153 |
|
154 |
|
155 |
|
156 |
|
157 |
|
158 |
|
159 |
|
160 | Sandbox.prototype.run = function (codeOrUrl, options, cb) {
|
161 | if (typeof options === 'function') {
|
162 | cb = options;
|
163 | options = {};
|
164 | }
|
165 |
|
166 | if (!options) options = {};
|
167 |
|
168 | var promise = this.create(codeOrUrl, options)
|
169 | .call('run', options);
|
170 |
|
171 | return cb ? promise.nodeify(cb, {spread: true}) : promise;
|
172 | };
|
173 |
|
174 |
|
175 |
|
176 |
|
177 |
|
178 |
|
179 |
|
180 |
|
181 | Sandbox.prototype.createToken = function (options, cb) {
|
182 | if (!options) options = {};
|
183 |
|
184 | var self = this;
|
185 | var promise = new Bluebird(function (resolve, reject) {
|
186 | var params = {
|
187 | ten: options.container || self.container,
|
188 | dd: options.issuanceDepth || 0,
|
189 | };
|
190 |
|
191 | if (options.exp !== undefined && options.nbf !== undefined
|
192 | && options.exp <= options.nbf) {
|
193 | return reject('The `nbf` parameter cannot be set to a later time than `exp`.');
|
194 | }
|
195 |
|
196 | if (options.host)
|
197 | params.host = options.host;
|
198 | if (options.code_url)
|
199 | params.url = options.code_url;
|
200 | if (typeof options.code === 'string')
|
201 | params.code = options.code;
|
202 | if (options.secrets && Object.keys(options.secrets).length > 0)
|
203 | params.ectx = options.secrets;
|
204 | if (options.secret && Object.keys(options.secret).length > 0)
|
205 | params.ectx = options.secret;
|
206 | if (options.params && Object.keys(options.params).length > 0)
|
207 | params.pctx = options.params;
|
208 | if (options.param && Object.keys(options.param).length > 0)
|
209 | params.pctx = options.param;
|
210 | if (options.meta && Object.keys(options.meta).length > 0)
|
211 | params.meta = options.meta;
|
212 | if (options.nbf !== undefined)
|
213 | params.nbf = options.nbf;
|
214 | if (options.exp !== undefined)
|
215 | params.exp = options.exp;
|
216 | if (options.merge || options.mergeBody)
|
217 | params.mb = 1;
|
218 | if ((options.parse || options.parseBody) !== undefined)
|
219 |
|
220 |
|
221 | params.pb = +(options.parse || options.parseBody);
|
222 | if (!options.selfRevoke)
|
223 | params.dr = 1;
|
224 | if (options.name)
|
225 | params.jtn = options.name;
|
226 |
|
227 | try {
|
228 | if (options.tokenLimit)
|
229 | addLimits(options.tokenLimit, Sandbox.limits.token);
|
230 | if (options.containerLimit)
|
231 | addLimits(options.containerLimit, Sandbox.limits.container);
|
232 | } catch (err) {
|
233 | return reject(err);
|
234 | }
|
235 |
|
236 | return resolve(self.createTokenRaw(params, { include_webtask_url: options.include_webtask_url }));
|
237 |
|
238 | function addLimits(limits, spec) {
|
239 | for (var l in limits) {
|
240 | var limit = parseInt(limits[l], 10);
|
241 |
|
242 | if (!spec[l]) {
|
243 | throw new Error('Unsupported limit type `' + l
|
244 | + '`. Supported limits are: '
|
245 | + Object.keys(spec).join(', ') + '.');
|
246 | }
|
247 |
|
248 | if (isNaN(limits[l]) || Math.floor(+limits[l]) !== limit
|
249 | || limit < 1) {
|
250 | throw new Error('Unsupported limit value for `' + l
|
251 | + '` limit. All limits must be positive integers.');
|
252 | }
|
253 |
|
254 | params[spec[l]] = limit;
|
255 | }
|
256 | }
|
257 | });
|
258 |
|
259 | return cb ? promise.nodeify(cb) : promise;
|
260 | };
|
261 |
|
262 |
|
263 |
|
264 |
|
265 |
|
266 |
|
267 |
|
268 |
|
269 |
|
270 |
|
271 |
|
272 |
|
273 | Sandbox.prototype.issueRequest = function (request, cb) {
|
274 | const transformedRequest = this.onBeforeRequest
|
275 | .reduce((request, hook) => {
|
276 | return hook(request, this);
|
277 | }, request);
|
278 | const promise = Request(transformedRequest);
|
279 |
|
280 | return cb ? promise.nodeify(cb) : promise;
|
281 | };
|
282 |
|
283 |
|
284 |
|
285 |
|
286 |
|
287 |
|
288 |
|
289 |
|
290 |
|
291 |
|
292 | Sandbox.prototype.createTokenRaw = function (claims, options, cb) {
|
293 | if (typeof options === 'function') {
|
294 | cb = options;
|
295 | options = undefined;
|
296 | }
|
297 | var request = Superagent
|
298 | .post(this.url + '/api/tokens/issue')
|
299 | .set('Authorization', 'Bearer ' + this.token)
|
300 | .send(claims);
|
301 |
|
302 | var promise = this.issueRequest(request)
|
303 | .then(function (res) {
|
304 | return (options && options.include_webtask_url)
|
305 | ? { token: res.text, webtask_url: res.header['location'] }
|
306 | : res.text;
|
307 | });
|
308 |
|
309 | return cb ? promise.nodeify(cb) : promise;
|
310 | };
|
311 |
|
312 |
|
313 |
|
314 |
|
315 |
|
316 |
|
317 |
|
318 |
|
319 |
|
320 |
|
321 | Sandbox.prototype.createLogStream = function (options) {
|
322 | if (!options) options = {};
|
323 |
|
324 | var url = this.url + '/api/logs/tenant/'
|
325 | + (options.container || this.container)
|
326 | + '?key=' + encodeURIComponent(this.token);
|
327 |
|
328 | return LogStream(url);
|
329 | };
|
330 |
|
331 | Sandbox.prototype._createCronJob = function (job) {
|
332 | return new CronJob(this, job);
|
333 | };
|
334 |
|
335 |
|
336 |
|
337 |
|
338 |
|
339 |
|
340 |
|
341 |
|
342 |
|
343 |
|
344 |
|
345 |
|
346 | Sandbox.prototype.getWebtask = function (options, cb) {
|
347 | if (!options) options = {};
|
348 |
|
349 | var promise;
|
350 |
|
351 | if (!options.name) {
|
352 | var err = new Error('Missing required option: `options.name`');
|
353 | err.statusCode = 400;
|
354 |
|
355 | promise = Bluebird.reject(err);
|
356 | } else {
|
357 | var url = this.url + '/api/webtask/'
|
358 | + (options.container || this.container) + '/' + options.name;
|
359 | var request = Superagent
|
360 | .get(url)
|
361 | .set('Authorization', 'Bearer ' + this.token)
|
362 | .accept('json');
|
363 | var self = this;
|
364 |
|
365 | if (options.decrypt) request.query({ decrypt: options.decrypt });
|
366 | if (options.fetch_code) request.query({ fetch_code: options.fetch_code });
|
367 |
|
368 | promise = this.issueRequest(request)
|
369 | .get('body')
|
370 | .then(function (data) {
|
371 | return new Webtask(self, data.token, { name: data.name, secrets: data.secrets, code: data.code, meta: data.meta, webtask_url: data.webtask_url });
|
372 | });
|
373 | }
|
374 |
|
375 | return cb ? promise.nodeify(cb) : promise;
|
376 | };
|
377 |
|
378 |
|
379 |
|
380 |
|
381 |
|
382 |
|
383 |
|
384 |
|
385 |
|
386 |
|
387 |
|
388 |
|
389 |
|
390 | Sandbox.prototype.createWebtask = function (options, cb) {
|
391 | if (!options) options = {};
|
392 |
|
393 | var promise;
|
394 | var err;
|
395 |
|
396 | if (typeof options.name !== 'string') {
|
397 | err = new Error('The `name` option is required and must be a string');
|
398 | err.statusCode = 400;
|
399 |
|
400 | promise = Bluebird.reject(err);
|
401 | }
|
402 |
|
403 | if (options.code && typeof options.code !== 'string') {
|
404 | err = new Error('The `code` option must be a string');
|
405 | err.statusCode = 400;
|
406 |
|
407 | promise = Bluebird.reject(err);
|
408 | }
|
409 |
|
410 | if (options.url && typeof options.url !== 'string') {
|
411 | err = new Error('The `url` option must be a string');
|
412 | err.statusCode = 400;
|
413 |
|
414 | promise = Bluebird.reject(err);
|
415 | }
|
416 |
|
417 | if (options.code && options.url) {
|
418 | err = new Error('Either the `code` or `url` option can be specified, but not both');
|
419 | err.statusCode = 400;
|
420 |
|
421 | promise = Bluebird.reject(err);
|
422 | }
|
423 |
|
424 | if (!err) {
|
425 | var url = this.url + '/api/webtask/'
|
426 | + (options.container || this.container) + '/' + options.name;
|
427 | var payload = {};
|
428 | if (options.secrets) payload.secrets = options.secrets;
|
429 | if (options.code) payload.code = options.code;
|
430 | if (options.url) payload.url = options.url;
|
431 | if (options.meta) payload.meta = options.meta;
|
432 | if (options.host) payload.host = options.host;
|
433 | var request = Superagent
|
434 | .put(url)
|
435 | .set('Authorization', 'Bearer ' + this.token)
|
436 | .send(payload)
|
437 |
|
438 | var self = this;
|
439 | promise = this.issueRequest(request)
|
440 | .then(function (res) {
|
441 | return new Webtask(self, res.body.token, { name: res.body.name, container: options.container || self.container, meta: res.body.meta, webtask_url: res.body.webtask_url });
|
442 | });
|
443 | }
|
444 |
|
445 | return cb ? promise.nodeify(cb) : promise;
|
446 | };
|
447 |
|
448 |
|
449 |
|
450 |
|
451 |
|
452 |
|
453 |
|
454 |
|
455 |
|
456 |
|
457 |
|
458 | Sandbox.prototype.removeWebtask = function (options, cb) {
|
459 | if (!options) options = {};
|
460 |
|
461 | var promise;
|
462 |
|
463 | if (!options.name) {
|
464 | var err = new Error('Missing required option: `options.name`');
|
465 | err.statusCode = 400;
|
466 |
|
467 | promise = Bluebird.reject(err);
|
468 | } else {
|
469 | var url = this.url + '/api/webtask/'
|
470 | + (options.container || this.container) + '/' + options.name;
|
471 | var request = Superagent
|
472 | .del(url)
|
473 | .set('Authorization', 'Bearer ' + this.token);
|
474 |
|
475 | promise = this.issueRequest(request)
|
476 | .return(true);
|
477 | }
|
478 |
|
479 | return cb ? promise.nodeify(cb) : promise;
|
480 | };
|
481 |
|
482 |
|
483 |
|
484 |
|
485 |
|
486 |
|
487 |
|
488 |
|
489 |
|
490 |
|
491 |
|
492 |
|
493 |
|
494 |
|
495 |
|
496 |
|
497 |
|
498 |
|
499 |
|
500 | Sandbox.prototype.updateWebtask = function (options, cb) {
|
501 | if (!options) options = {};
|
502 |
|
503 | var self = this;
|
504 | var err;
|
505 | var promise;
|
506 |
|
507 | if (typeof options.name !== 'string') {
|
508 | err = new Error('The `name` option is required and must be a string');
|
509 | err.statusCode = 400;
|
510 |
|
511 | promise = Bluebird.reject(err);
|
512 | }
|
513 |
|
514 | if (options.code && typeof options.code !== 'string') {
|
515 | err = new Error('The `code` option must be a string');
|
516 | err.statusCode = 400;
|
517 |
|
518 | promise = Bluebird.reject(err);
|
519 | }
|
520 |
|
521 | if (options.url && typeof options.url !== 'string') {
|
522 | err = new Error('The `url` option must be a string');
|
523 | err.statusCode = 400;
|
524 |
|
525 | promise = Bluebird.reject(err);
|
526 | }
|
527 |
|
528 | if (options.code && options.url) {
|
529 | err = new Error('Either the `code` or `url` option can be specified, but not both');
|
530 | err.statusCode = 400;
|
531 |
|
532 | promise = Bluebird.reject(err);
|
533 | }
|
534 |
|
535 | if (!err) {
|
536 | promise = this.getWebtask({ name: options.name })
|
537 | .then(onWebtask);
|
538 |
|
539 | }
|
540 |
|
541 | return cb ? promise.nodeify(cb) : promise;
|
542 |
|
543 |
|
544 | function onWebtask(webtask) {
|
545 | return webtask.inspect({ decrypt: options.secrets !== false, fetch_code: options.code !== false, meta: options.meta !== false })
|
546 | .then(onInspection);
|
547 |
|
548 |
|
549 | function onInspection(claims) {
|
550 | var newClaims = {
|
551 | ten: claims.ten,
|
552 | jtn: claims.jtn,
|
553 | };
|
554 |
|
555 | ['nbf', 'exp', 'dd', 'dr', 'ls', 'lm', 'lh', 'ld', 'lw', 'lo', 'lts', 'ltm', 'lth', 'ltd', 'ltw', 'lto']
|
556 | .forEach(function (claim) {
|
557 | if (claims[claim]) {
|
558 | newClaims[claim] = claims[claim];
|
559 | }
|
560 | });
|
561 |
|
562 | if (options.host !== false && (options.host || claims.host)) {
|
563 | newClaims.host = options.host || claims.host;
|
564 | }
|
565 | if (typeof options.parseBody !== 'undefined' || typeof options.parse !== 'undefined' || claims.pb) {
|
566 | newClaims.pb = +(options.parseBody || options.parse || claims.pb);
|
567 | }
|
568 | if (typeof options.mergeBody !== 'undefined' || typeof options.merge !== 'undefined' || claims.mb) {
|
569 | newClaims.mb = +(options.mergeBody || options.merge || claims.mb);
|
570 | }
|
571 | if (options.secrets !== false && (options.secrets || claims.ectx)) {
|
572 | newClaims.ectx = options.secrets || claims.ectx;
|
573 | }
|
574 | if (options.params !== false && (options.params || claims.pctx)) {
|
575 | newClaims.pctx = options.params || claims.pctx;
|
576 | }
|
577 | if (options.meta !== false && (options.meta || claims.meta)) {
|
578 | newClaims.meta = options.meta || claims.meta;
|
579 | }
|
580 | if (options.url) {
|
581 | newClaims.url = options.url;
|
582 | }
|
583 | if (options.code) {
|
584 | newClaims.code = options.code;
|
585 | }
|
586 |
|
587 | return self.createRaw(newClaims, { include_webtask_url: options.include_webtask_url });
|
588 | }
|
589 | }
|
590 | };
|
591 |
|
592 |
|
593 |
|
594 |
|
595 |
|
596 |
|
597 |
|
598 |
|
599 |
|
600 | Sandbox.prototype.listWebtasks = function (options, cb) {
|
601 | if (!options) options = {};
|
602 |
|
603 | var url = this.url + '/api/webtask/'
|
604 | + (options.container || this.container);
|
605 | var request = Superagent
|
606 | .get(url)
|
607 | .set('Authorization', 'Bearer ' + this.token)
|
608 | .accept('json');
|
609 |
|
610 | if (options.offset) request.query({ offset: options.offset });
|
611 | if (options.limit) request.query({ limit: options.limit });
|
612 | if (options.meta) {
|
613 | for (var m in options.meta) {
|
614 | request.query({ meta: m + ':' + options.meta[m] });
|
615 | }
|
616 | }
|
617 |
|
618 | var self = this;
|
619 | var promise = this.issueRequest(request)
|
620 | .get('body')
|
621 | .map(function (webtask) {
|
622 | return new Webtask(self, webtask.token, { name: webtask.name, meta: webtask.meta, webtask_url: webtask.webtask_url });
|
623 | });
|
624 |
|
625 | return cb ? promise.nodeify(cb) : promise;
|
626 | };
|
627 |
|
628 |
|
629 |
|
630 |
|
631 |
|
632 |
|
633 |
|
634 |
|
635 |
|
636 |
|
637 |
|
638 |
|
639 |
|
640 |
|
641 | Sandbox.prototype.createCronJob = function (options, cb) {
|
642 | options = defaults(options, { container: this.container });
|
643 |
|
644 | var payload = {
|
645 | schedule: options.schedule,
|
646 | };
|
647 | if (options.token) payload.token = options.token;
|
648 |
|
649 | if (options.state) {
|
650 | payload.state = options.state;
|
651 | }
|
652 | if (options.meta) {
|
653 | payload.meta = options.meta;
|
654 | }
|
655 | if (options.tz) {
|
656 | payload.tz = options.tz;
|
657 | }
|
658 |
|
659 | var request = Superagent
|
660 | .put(this.url + '/api/cron/' + options.container + '/' + options.name)
|
661 | .set('Authorization', 'Bearer ' + this.token)
|
662 | .send(payload)
|
663 | .accept('json');
|
664 |
|
665 | var promise = this.issueRequest(request)
|
666 | .get('body')
|
667 | .then(this._createCronJob.bind(this));
|
668 |
|
669 | return cb ? promise.nodeify(cb) : promise;
|
670 | };
|
671 |
|
672 |
|
673 |
|
674 |
|
675 |
|
676 |
|
677 |
|
678 |
|
679 |
|
680 |
|
681 | Sandbox.prototype.removeCronJob = function (options, cb) {
|
682 | options = defaults(options, { container: this.container });
|
683 |
|
684 | var request = Superagent
|
685 | .del(this.url + '/api/cron/' + options.container + '/' + options.name)
|
686 | .set('Authorization', 'Bearer ' + this.token)
|
687 | .accept('json');
|
688 |
|
689 | var promise = this.issueRequest(request)
|
690 | .get('body');
|
691 |
|
692 | return cb ? promise.nodeify(cb) : promise;
|
693 | };
|
694 |
|
695 |
|
696 |
|
697 |
|
698 |
|
699 |
|
700 |
|
701 |
|
702 |
|
703 |
|
704 |
|
705 | Sandbox.prototype.setCronJobState = function (options, cb) {
|
706 | options = defaults(options, { container: this.container });
|
707 |
|
708 | var request = Superagent
|
709 | .put(this.url + '/api/cron/' + options.container + '/' + options.name + '/state')
|
710 | .set('Authorization', 'Bearer ' + this.token)
|
711 | .send({
|
712 | state: options.state,
|
713 | })
|
714 | .accept('json');
|
715 |
|
716 | var promise = this.issueRequest(request)
|
717 | .get('body');
|
718 |
|
719 | return cb ? promise.nodeify(cb) : promise;
|
720 | };
|
721 |
|
722 |
|
723 |
|
724 |
|
725 |
|
726 |
|
727 |
|
728 |
|
729 |
|
730 | Sandbox.prototype.listCronJobs = function (options, cb) {
|
731 | if (typeof options === 'function') {
|
732 | cb = options;
|
733 | options = null;
|
734 | }
|
735 |
|
736 | if (!options) options = {};
|
737 |
|
738 | options = defaults(options, { container: this.container });
|
739 |
|
740 | var request = Superagent
|
741 | .get(this.url + '/api/cron/' + options.container)
|
742 | .set('Authorization', 'Bearer ' + this.token)
|
743 | .accept('json');
|
744 |
|
745 | if (options.offset) request.query({ offset: options.offset });
|
746 | if (options.limit) request.query({ limit: options.limit });
|
747 | if (options.meta) {
|
748 | for (var m in options.meta) {
|
749 | request.query({ meta: m + ':' + options.meta[m] });
|
750 | }
|
751 | }
|
752 |
|
753 | var promise = this.issueRequest(request)
|
754 | .get('body')
|
755 | .map(this._createCronJob.bind(this));
|
756 |
|
757 | return cb ? promise.nodeify(cb) : promise;
|
758 | };
|
759 |
|
760 |
|
761 |
|
762 |
|
763 |
|
764 |
|
765 |
|
766 |
|
767 |
|
768 |
|
769 | Sandbox.prototype.getCronJob = function (options, cb) {
|
770 | options = defaults(options, { container: this.container });
|
771 |
|
772 | var request = Superagent
|
773 | .get(this.url + '/api/cron/' + options.container + '/' + options.name)
|
774 | .set('Authorization', 'Bearer ' + this.token)
|
775 | .accept('json');
|
776 |
|
777 | var promise = this.issueRequest(request)
|
778 | .get('body')
|
779 | .then(this._createCronJob.bind(this));
|
780 |
|
781 | return cb ? promise.nodeify(cb) : promise;
|
782 | };
|
783 |
|
784 |
|
785 |
|
786 |
|
787 |
|
788 |
|
789 |
|
790 |
|
791 |
|
792 |
|
793 |
|
794 |
|
795 |
|
796 | Sandbox.prototype.getCronJobHistory = function (options, cb) {
|
797 | options = defaults(options, { container: this.container });
|
798 |
|
799 | var request = Superagent
|
800 | .get(this.url + '/api/cron/' + options.container + '/' + options.name + '/history')
|
801 | .set('Authorization', 'Bearer ' + this.token)
|
802 | .accept('json');
|
803 |
|
804 | if (options.offset) request.query({offset: options.offset});
|
805 | if (options.limit) request.query({limit: options.limit});
|
806 |
|
807 | var promise = this.issueRequest(request)
|
808 | .get('body')
|
809 | .map(function (result) {
|
810 | var auth0HeaderRx = /^x-auth0/;
|
811 |
|
812 | result.scheduled_at = new Date(result.scheduled_at);
|
813 | result.started_at = new Date(result.started_at);
|
814 | result.completed_at = new Date(result.completed_at);
|
815 |
|
816 | for (var header in result.headers) {
|
817 | if (auth0HeaderRx.test(header)) {
|
818 | try {
|
819 | result.headers[header] = JSON.parse(result.headers[header]);
|
820 | } catch (__) {
|
821 |
|
822 | }
|
823 | }
|
824 | }
|
825 |
|
826 | return result;
|
827 | });
|
828 |
|
829 | return cb ? promise.nodeify(cb) : promise;
|
830 | };
|
831 |
|
832 |
|
833 |
|
834 |
|
835 |
|
836 |
|
837 |
|
838 |
|
839 |
|
840 |
|
841 |
|
842 | Sandbox.prototype.inspectToken = function (options, cb) {
|
843 | options = defaults(options, { container: this.container });
|
844 |
|
845 | var request = Superagent
|
846 | .get(this.url + '/api/tokens/inspect')
|
847 | .query({ token: options.token })
|
848 | .set('Authorization', 'Bearer ' + this.token)
|
849 | .accept('json');
|
850 |
|
851 | if (options.decrypt) request.query({ decrypt: options.decrypt });
|
852 | if (options.fetch_code) request.query({ fetch_code: options.fetch_code });
|
853 | if (options.meta) request.query({ meta: options.meta });
|
854 |
|
855 | var promise = this.issueRequest(request)
|
856 | .get('body');
|
857 |
|
858 | return cb ? promise.nodeify(cb) : promise;
|
859 | };
|
860 |
|
861 |
|
862 |
|
863 |
|
864 |
|
865 |
|
866 |
|
867 |
|
868 |
|
869 |
|
870 |
|
871 | Sandbox.prototype.inspectWebtask = function (options, cb) {
|
872 | options = defaults(options, { container: this.container });
|
873 |
|
874 | var promise = this.securityVersion === 'v2'
|
875 | ? this.getWebtask({ name: options.name, decrypt: options.decrypt, fetch_code: options.fetch_code })
|
876 | : this.getWebtask({ name: options.name })
|
877 | .call('inspect', { decrypt: options.decrypt, fetch_code: options.fetch_code, meta: options.meta });
|
878 |
|
879 | return cb ? promise.nodeify(cb) : promise;
|
880 | };
|
881 |
|
882 |
|
883 |
|
884 |
|
885 |
|
886 |
|
887 |
|
888 |
|
889 |
|
890 |
|
891 | Sandbox.prototype.revokeToken = function (token, cb) {
|
892 | var request = Superagent
|
893 | .post(this.url + '/api/tokens/revoke')
|
894 | .set('Authorization', 'Bearer ' + this.token)
|
895 | .query({ token: token });
|
896 |
|
897 | var promise = this.issueRequest(request);
|
898 |
|
899 | return cb ? promise.nodeify(cb) : promise;
|
900 | };
|
901 |
|
902 |
|
903 |
|
904 |
|
905 |
|
906 |
|
907 |
|
908 |
|
909 |
|
910 | Sandbox.prototype.listNodeModuleVersions = function (options, cb) {
|
911 | var request = Superagent
|
912 | .get(this.url + `/api/env/node/modules/${encodeURIComponent(options.name)}`);
|
913 |
|
914 | var promise = this.issueRequest(request)
|
915 | .get('body');
|
916 |
|
917 | return cb ? promise.nodeify(cb) : promise;
|
918 | };
|
919 |
|
920 |
|
921 |
|
922 |
|
923 |
|
924 |
|
925 |
|
926 |
|
927 |
|
928 |
|
929 | Sandbox.prototype.ensureNodeModules = function (options, cb) {
|
930 | var request = Superagent
|
931 | .post(this.url + '/api/env/node/modules')
|
932 | .send({ modules: options.modules })
|
933 | .set('Authorization', 'Bearer ' + this.token);
|
934 |
|
935 | if (options.reset) {
|
936 | request.query({ reset: 1 });
|
937 | }
|
938 |
|
939 | var promise = this.issueRequest(request)
|
940 | .get('body');
|
941 |
|
942 | return cb ? promise.nodeify(cb) : promise;
|
943 | };
|
944 |
|
945 |
|
946 |
|
947 |
|
948 |
|
949 |
|
950 |
|
951 |
|
952 |
|
953 |
|
954 |
|
955 |
|
956 |
|
957 | Sandbox.prototype.updateStorage = function (storage, options, cb) {
|
958 | var promise;
|
959 |
|
960 | if (!options || !options.name) {
|
961 | var err = new Error('Missing required option: `options.name`');
|
962 | err.statusCode = 400;
|
963 |
|
964 | promise = Bluebird.reject(err);
|
965 | }
|
966 | else {
|
967 | var obj = {
|
968 | data: JSON.stringify(storage.data)
|
969 | }
|
970 |
|
971 | if (storage.etag) {
|
972 | obj.etag = storage.etag.toString();
|
973 | }
|
974 |
|
975 | var request = Superagent
|
976 | .put(this.url + '/api/webtask/' + (options.container || this.container) + '/' + options.name + '/data')
|
977 | .send(obj)
|
978 | .set('Authorization', 'Bearer ' + this.token)
|
979 | .accept('json');
|
980 |
|
981 | promise = this.issueRequest(request)
|
982 | .get('body');
|
983 | }
|
984 |
|
985 | return cb ? promise.nodeify(cb) : promise;
|
986 | };
|
987 |
|
988 |
|
989 |
|
990 |
|
991 |
|
992 |
|
993 |
|
994 |
|
995 |
|
996 |
|
997 | Sandbox.prototype.getStorage = function (options, cb) {
|
998 | var promise;
|
999 |
|
1000 | if (!options || !options.name) {
|
1001 | var err = new Error('Missing required option: `options.name`');
|
1002 | err.statusCode = 400;
|
1003 |
|
1004 | promise = Bluebird.reject(err);
|
1005 | }
|
1006 | else {
|
1007 | var request = Superagent
|
1008 | .get(this.url + '/api/webtask/' + (options.container || this.container) + '/' + options.name + '/data')
|
1009 | .set('Authorization', 'Bearer ' + this.token)
|
1010 | .accept('json');
|
1011 |
|
1012 | promise = this.issueRequest(request)
|
1013 | .get('body')
|
1014 | .then(function (result) {
|
1015 | var storage = result;
|
1016 |
|
1017 | try {
|
1018 | storage.data = JSON.parse(result.data);
|
1019 | } catch(e) {
|
1020 |
|
1021 | }
|
1022 |
|
1023 | return storage;
|
1024 | });
|
1025 | }
|
1026 |
|
1027 | return cb ? promise.nodeify(cb) : promise;
|
1028 | }
|
1029 |
|
1030 | Sandbox.limits = {
|
1031 | container: {
|
1032 | second: 'ls',
|
1033 | minute: 'lm',
|
1034 | hour: 'lh',
|
1035 | day: 'ld',
|
1036 | week: 'lw',
|
1037 | month: 'lo'
|
1038 | },
|
1039 | token: {
|
1040 | second: 'lts',
|
1041 | minute: 'ltm',
|
1042 | hour: 'lth',
|
1043 | day: 'ltd',
|
1044 | week: 'ltw',
|
1045 | month: 'lto',
|
1046 | },
|
1047 | };
|
1048 |
|
1049 |
|
1050 |
|
1051 |
|
1052 |
|
1053 |
|
1054 |
|
1055 |
|
1056 |
|
1057 |
|
1058 |
|
1059 |
|
1060 |
|
1061 | Sandbox.fromToken = function (token, options) {
|
1062 | var config = defaults({}, options, Sandbox.optionsFromJwt(token));
|
1063 |
|
1064 | return Sandbox.init(config);
|
1065 | };
|
1066 |
|
1067 |
|
1068 |
|
1069 |
|
1070 |
|
1071 |
|
1072 |
|
1073 |
|
1074 |
|
1075 |
|
1076 |
|
1077 |
|
1078 | Sandbox.init = function (options) {
|
1079 | if (typeof options !== 'object') throw new Error('Expecting an options Object, got `' + typeof options + '`.');
|
1080 | if (!options.container) throw new Error('A Sandbox instance cannot be created without a container.');
|
1081 | if (typeof options.container !== 'string') throw new Error('Only String containers are supported, got `' + typeof options.container + '`.');
|
1082 | if (typeof options.token !== 'string') throw new Error('A Sandbox instance cannot be created without a token.');
|
1083 |
|
1084 | defaults(options, {
|
1085 | url: 'https://webtask.it.auth0.com',
|
1086 | });
|
1087 |
|
1088 | return new Sandbox(options);
|
1089 | };
|
1090 |
|
1091 | Sandbox.optionsFromJwt = function (jwt) {
|
1092 | var claims = Decode(jwt);
|
1093 |
|
1094 | if (!claims) throw new Error('Unable to decode token `' + jwt + '` (https://jwt.io/#id_token=' + jwt + ').');
|
1095 |
|
1096 |
|
1097 | var ten = claims.ten;
|
1098 |
|
1099 | if (!ten) throw new Error('Invalid token, missing `ten` claim `' + jwt + '` (https://jwt.io/#id_token=' + jwt + ').');
|
1100 |
|
1101 | if (Array.isArray(ten)) {
|
1102 | ten = ten[0];
|
1103 | } else {
|
1104 |
|
1105 | var matches = ten.match(/\/(.+)\//);
|
1106 | if (matches) {
|
1107 | try {
|
1108 | var regex = new RegExp(matches[1]);
|
1109 | var gen = new RandExp(regex);
|
1110 |
|
1111 |
|
1112 | gen.randInt = function (l) { return l; };
|
1113 |
|
1114 | ten = gen.gen();
|
1115 | } catch (err) {
|
1116 | throw new Error('Unable to derive containtainer name from `ten` claim `' + claims.ten + '`: ' + err.message + '.');
|
1117 | }
|
1118 | }
|
1119 | }
|
1120 |
|
1121 | if (typeof ten !== 'string' || !ten) throw new Error('Expecting `ten` claim to be a non-blank string, got `' + typeof ten + '`, with value `' + ten + '`.');
|
1122 |
|
1123 | return {
|
1124 | container: ten,
|
1125 | token: jwt,
|
1126 | };
|
1127 | };
|