total5 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/builders.js ADDED
@@ -0,0 +1,1770 @@
1
+ // Total.js Builders
2
+ // The MIT License
3
+ // Copyright 2023 (c) Peter Širka <petersirka@gmail.com>
4
+
5
+ 'use strict';
6
+
7
+ const REG_ARGS = /\{{1,2}[a-z0-9_.-\s]+\}{1,2}/gi;
8
+ const SESSIONSEPARATOR = '\0';
9
+
10
+ var transforms = { error: {}, restbuilder: {} };
11
+ var restbuilderupgrades = [];
12
+
13
+ function Options(ctrl, error) {
14
+ var t = this;
15
+ t.controller = ctrl;
16
+ t.error = error || new ErrorBuilder();
17
+ t.response = {};
18
+ }
19
+
20
+ Options.prototype = {
21
+
22
+ get client() {
23
+ return this.controller;
24
+ },
25
+
26
+ get websocket() {
27
+ return this.controller && this.controller.iswebsocket ? this.controller : null;
28
+ },
29
+
30
+ get sessionid() {
31
+ return this.controller ? this.controller.sessionid : null;
32
+ },
33
+
34
+ get value() {
35
+ return this.payload;
36
+ },
37
+
38
+ get model() {
39
+ return this.payload;
40
+ },
41
+
42
+ set value(value) {
43
+ this.payload = value;
44
+ },
45
+
46
+ set model(value) {
47
+ this.payload = value;
48
+ },
49
+
50
+ get url() {
51
+ return (this.controller ? this.controller.url : '') || '';
52
+ },
53
+
54
+ get uri() {
55
+ return this.controller ? this.controller.uri : null;
56
+ },
57
+
58
+ get path() {
59
+ return this.controller ? this.controller.pathname : EMPTYARRAY;
60
+ },
61
+
62
+ get split() {
63
+ return this.controller ? this.controller.split : EMPTYARRAY;
64
+ },
65
+
66
+ get split2() {
67
+ return this.controller ? this.controller.split2 : EMPTYARRAY;
68
+ },
69
+
70
+ get language() {
71
+ return (this.controller ? this.controller.language : '') || '';
72
+ },
73
+
74
+ get ip() {
75
+ return this.controller ? this.controller.ip : null;
76
+ },
77
+
78
+ get files() {
79
+ return this.controller ? this.controller.files : null;
80
+ },
81
+
82
+ get body() {
83
+ return this.controller ? this.controller.body : null;
84
+ },
85
+
86
+ get mobile() {
87
+ return this.controller ? this.controller.mobile : null;
88
+ },
89
+
90
+ get headers() {
91
+ return this.controller ? this.controller.headers : null;
92
+ },
93
+
94
+ get ua() {
95
+ return this.controller ? this.controller.ua : null;
96
+ }
97
+ };
98
+
99
+ Options.prototype.unauthorized = function() {
100
+ var args = [this];
101
+ for (let i = 0; i < arguments.length; i++)
102
+ args.push(arguments[i]);
103
+ return F.unauthorized.apply(global, args);
104
+ };
105
+
106
+ Options.prototype.transform = function(name, value, callback) {
107
+ return F.transform(name, value, callback, this.controller);
108
+ };
109
+
110
+ Options.prototype.action = function(schema, payload) {
111
+ return F.action(schema, payload, this.controller);
112
+ };
113
+
114
+ Options.prototype.publish = function(value) {
115
+ var self = this;
116
+ var name = self.id;
117
+ if (F.TTMS.cache.socket && F.TTMS.cache.pcache[name]) {
118
+
119
+ var tmp = {};
120
+ if (tmp) {
121
+ for (var key in value) {
122
+ if (!self.$publish || self.$publish[key])
123
+ tmp[key] = value[key];
124
+ }
125
+ }
126
+
127
+ F.stats.performance.publish++;
128
+ F.TTMS.cache.socket.send({ type: 'publish', id: name, data: tmp }, client => client.tmsready && client.$subscribers[name]);
129
+ }
130
+ return self;
131
+ };
132
+
133
+ Options.prototype.on = function(name, fn) {
134
+ var self = this;
135
+ if (!self.events)
136
+ self.events = {};
137
+ if (!self.events[name])
138
+ self.events[name] = [];
139
+ self.events[name].push(fn);
140
+ return self;
141
+ };
142
+
143
+ Options.prototype.emit = function(name, a, b, c, d) {
144
+
145
+ var self = this;
146
+
147
+ if (!self.events || !self.events[name])
148
+ return false;
149
+
150
+ for (var evt of self.events[name])
151
+ evt.call(self, a, b, c, d);
152
+
153
+ return true;
154
+ };
155
+
156
+ Options.prototype.cancel = function() {
157
+ var self = this;
158
+ self.callback = self.next = null;
159
+ self.error = null;
160
+ self.controller = null;
161
+ self.model = null;
162
+ self.options = null;
163
+ return self;
164
+ };
165
+
166
+ Options.prototype.redirect = function(url) {
167
+ var self = this;
168
+ self.$callback = null;
169
+ if (self.controller) {
170
+ self.controller.redirect(url);
171
+ self.controller.destroyed = true;
172
+ }
173
+ self.cancel();
174
+ return self;
175
+ };
176
+
177
+ Options.prototype.audit = function(message, type) {
178
+ F.audit(this, message ? this.variables(message) : '', type);
179
+ };
180
+
181
+ Options.prototype.success = function(value) {
182
+ var self = this;
183
+ if (self.TYPE === 'auth')
184
+ self.callback(value || EMPTYOBJECT);
185
+ else
186
+ self.callback(DEF.onSuccess(value));
187
+ };
188
+
189
+ Options.prototype.successful = function(callback) {
190
+ var self = this;
191
+ return function(err, a, b, c) {
192
+ if (err)
193
+ self.invalid(err);
194
+ else
195
+ callback.call(self, a, b, c);
196
+ };
197
+ };
198
+
199
+ Options.prototype.callback = function(value) {
200
+
201
+ var self = this;
202
+
203
+ if (arguments.length == 0) {
204
+ return function(err, response) {
205
+ err && self.error.push(err);
206
+ self.callback(response);
207
+ };
208
+ }
209
+
210
+ self.$callback(self.error.items.length ? self.error : null, value);
211
+ };
212
+
213
+ Options.prototype.done = function(arg) {
214
+ var self = this;
215
+ return function(err, response) {
216
+ if (err) {
217
+ self.error.push(err);
218
+ self.callback(null);
219
+ } else {
220
+ if (self.TYPE === 'auth')
221
+ self.callback(arg === true ? response : arg);
222
+ else
223
+ self.callback(DEF.onSuccess(arg === true ? response : arg));
224
+ }
225
+ };
226
+ };
227
+
228
+ Options.prototype.output = function(name) {
229
+ // @name {String} json, html, xml, text, redirect, binary, jsonstring, empty, file
230
+ var self = this;
231
+ if (self.controller)
232
+ self.controller.response.output = name;
233
+ return self;
234
+ };
235
+
236
+ Options.prototype.invalid = function(error, path, index) {
237
+ var self = this;
238
+ self.error.push(error, path, index);
239
+ self.$callback(true);
240
+ return self;
241
+ };
242
+
243
+ Options.prototype.cookie = function(name, value, expire, options) {
244
+ var self = this;
245
+ if (value === undefined)
246
+ return self.controller.cookie(name);
247
+ if (value === null)
248
+ expire = '-1 day';
249
+ self.controller.cookie(name, value, expire, options);
250
+ return self;
251
+ };
252
+
253
+ Options.prototype.variables = function(str, data) {
254
+
255
+ if (str.indexOf('{') === -1)
256
+ return str;
257
+
258
+ var $ = this;
259
+
260
+ return str.replace(REG_ARGS, function(text) {
261
+ var l = text[1] === '{' ? 2 : 1;
262
+ var key = text.substring(l, text.length - l).trim();
263
+ var val = null;
264
+ var five = key.substring(0, 5);
265
+ if (five === 'user.') {
266
+ if ($.user) {
267
+ key = key.substring(5);
268
+ val = key.indexOf('.') === -1 ? $.user[key] : F.TUtils.get($.user, key);
269
+ }
270
+ } else if (five === 'data.') {
271
+ if (data) {
272
+ key = key.substring(5);
273
+ val = key.indexOf('.') === -1 ? data[key] : F.TUtils.get(data, key);
274
+ }
275
+ } else {
276
+ var six = key.substring(0, 6);
277
+ if (six === 'model.' || six === 'value.') {
278
+ if ($.model) {
279
+ key = key.substring(6);
280
+ val = key.indexOf('.') === -1 ? $.model[key] : F.TUtils.get($.model, key);
281
+ }
282
+ } else if (six === 'query.')
283
+ val = $.query[key.substring(6)];
284
+ else if (key.substring(0, 7) === 'params.')
285
+ val = $.params[key.substring(7)];
286
+ }
287
+ return val == null ? text : val;
288
+ });
289
+
290
+ };
291
+
292
+ function ErrorBuilder() {
293
+ var t = this;
294
+ t.items = [];
295
+ // t.replacer = null;
296
+ t.status = 400;
297
+ t.prefix = '';
298
+ }
299
+
300
+ ErrorBuilder.prototype = {
301
+ get length() {
302
+ return this.items.length;
303
+ }
304
+ };
305
+
306
+ ErrorBuilder.prototype.reject = function(language) {
307
+ var self = this;
308
+ return new Error(self.toString(language, ''));
309
+ };
310
+
311
+ ErrorBuilder.prototype.push = function(err, path, index) {
312
+ var self = this;
313
+
314
+ if (!err)
315
+ err = 401;
316
+
317
+ if (err > 399) {
318
+ self.status = err;
319
+ self.items.push({ error: F.TUtils.httpstatus(err) });
320
+ } else
321
+ self.items.push({ error: err.toString(), path: path, index: index });
322
+ return self;
323
+ };
324
+
325
+ ErrorBuilder.prototype.push2 = function(name, path, index) {
326
+ var self = this;
327
+ self.items.push({ name: self.prefix + name, error: '@', path: path, index: index });
328
+ return self;
329
+ };
330
+
331
+ ErrorBuilder.assign = function(arr) {
332
+ var builder = new ErrorBuilder();
333
+ if (arr instanceof Array) {
334
+ for (var i = 0; i < arr.length; i++) {
335
+ if (arr[i].error)
336
+ builder.items.push(arr[i]);
337
+ }
338
+ } else {
339
+ var type = typeof(arr);
340
+ if (type === 'number' || type === 'string')
341
+ builder.push(arr);
342
+ else if (arr instanceof Error)
343
+ builder.push(arr + '');
344
+ }
345
+ return builder;
346
+ };
347
+
348
+ ErrorBuilder.prototype.replace = function(search, value) {
349
+ var self = this;
350
+ if (!self.replacer)
351
+ self.replacer = {};
352
+ self.replacer[search] = value;
353
+ return self;
354
+ };
355
+
356
+ ErrorBuilder.prototype.output = function(language = 'default') {
357
+
358
+ var self = this;
359
+ var output = [];
360
+
361
+ for (let m of self.items) {
362
+
363
+ let err = m.error;
364
+
365
+ if (err == '@')
366
+ err = F.resource(language, 'T' + (err === '@' ? m.name : err.substring(1)).hash(true).toString(36)) || 'The field "' + m.name + '" is invalid';
367
+ else if (err[0] === '@')
368
+ err = F.translate(language, err);
369
+
370
+ if (self.replacer) {
371
+ for (let key in self.replacer)
372
+ err = err.replaceAll(key, self.replacer[key]);
373
+ }
374
+
375
+ output.push({ name: m.name, error: err, path: m.path, index: m.index });
376
+ }
377
+
378
+ if (ErrorBuilder.$transform)
379
+ output = ErrorBuilder.$transform(output, language);
380
+
381
+ return output;
382
+ };
383
+
384
+ ErrorBuilder.prototype.toString = function(language = 'default', divider = '\n') {
385
+ var self = this;
386
+ var output = self.output(language);
387
+ var str = '';
388
+ for (let err of output)
389
+ str += (str ? divider : '') + err.error;
390
+ return str;
391
+ };
392
+
393
+ ErrorBuilder.transform = function(callback) {
394
+ ErrorBuilder.$transform = callback;
395
+ };
396
+
397
+ function RESTBuilder(url) {
398
+
399
+ this.$length = 0;
400
+ this.$transform = transforms.restbuilder_default;
401
+ this.$persistentcookies = false;
402
+
403
+ this.options = { url: url, timeout: 10000, method: 'GET', resolve: true, headers: { 'user-agent': 'Total.js/v' + F.version_header, accept: 'application/json, text/plain, text/plain, text/xml' }};
404
+
405
+ // this.$data = {};
406
+ // this.$nodnscache = true;
407
+ // this.$expire;
408
+ // this.$redirect
409
+
410
+ // Auto Total.js Error Handling
411
+ this.$errorbuilderhandler = true;
412
+ }
413
+
414
+ RESTBuilder.make = function(fn) {
415
+ var instance = new RESTBuilder();
416
+ fn && fn(instance);
417
+ return instance;
418
+ };
419
+
420
+ RESTBuilder.url = function(url) {
421
+ return new RESTBuilder(url);
422
+ };
423
+
424
+ RESTBuilder.GET = function(url, data) {
425
+ var builder = new RESTBuilder(url);
426
+ builder.options.query = data;
427
+ return builder;
428
+ };
429
+
430
+ RESTBuilder.API = function(url, name, data) {
431
+ var builder = new RESTBuilder(url);
432
+ builder.operation = name;
433
+ builder.options.method = 'POST';
434
+ builder.raw(data, 'raw');
435
+ return builder;
436
+ };
437
+
438
+ RESTBuilder.POST = function(url, data) {
439
+ var builder = new RESTBuilder(url);
440
+ builder.options.method = 'POST';
441
+ data && builder.raw(data, 'json');
442
+ return builder;
443
+ };
444
+
445
+ RESTBuilder.PUT = function(url, data) {
446
+ var builder = new RESTBuilder(url);
447
+ builder.options.method = 'PUT';
448
+ data && builder.raw(data, 'json');
449
+ return builder;
450
+ };
451
+
452
+ RESTBuilder.DELETE = function(url, data) {
453
+ var builder = new RESTBuilder(url);
454
+ builder.$method = 'delete';
455
+ builder.options.method = 'DELETE';
456
+ data && builder.raw(data, 'json');
457
+ return builder;
458
+ };
459
+
460
+ RESTBuilder.PATCH = function(url, data) {
461
+ var builder = new RESTBuilder(url);
462
+ builder.$method = 'patch';
463
+ builder.options.method = 'PATCH';
464
+ data && builder.raw(data, 'json');
465
+ return builder;
466
+ };
467
+
468
+ RESTBuilder.HEAD = function(url) {
469
+ var builder = new RESTBuilder(url);
470
+ builder.options.method = 'HEAD';
471
+ return builder;
472
+ };
473
+
474
+ RESTBuilder.upgrade = function(fn) {
475
+ restbuilderupgrades.push(fn);
476
+ };
477
+
478
+ var RESTP = RESTBuilder.prototype;
479
+
480
+ RESTP.insecure = function() {
481
+ this.options.insecure = true;
482
+ return this;
483
+ };
484
+
485
+ RESTP.error = function(err) {
486
+ this.$errorhandler = err;
487
+ return this;
488
+ };
489
+
490
+ RESTP.noparse = function() {
491
+ this.$noparse = true;
492
+ return this;
493
+ };
494
+
495
+ RESTP.debug = function() {
496
+ this.$debug = true;
497
+ return this;
498
+ };
499
+
500
+ RESTP.unixsocket = function(socket, path) {
501
+ var self = this;
502
+ self.options.unixsocket = { socket: socket, path: path };
503
+ return self;
504
+ };
505
+
506
+ RESTP.promise = function($) {
507
+ var self = this;
508
+ return new Promise(function(resolve, reject) {
509
+ self.exec(function(err, response) {
510
+ if (err) {
511
+ if ($ && $.invalid)
512
+ $.invalid(err);
513
+ else
514
+ reject(F.TUtils.toError(err));
515
+ } else
516
+ resolve(response);
517
+ });
518
+ });
519
+ };
520
+
521
+ RESTP.proxy = function(value) {
522
+ this.options.proxy = value;
523
+ return this;
524
+ };
525
+
526
+ RESTP.url = function(url) {
527
+ if (url === undefined)
528
+ return this.options.url;
529
+ this.options.url = url;
530
+ return this;
531
+ };
532
+
533
+ RESTP.cert = function(key, cert, dhparam) {
534
+ this.options.key = key;
535
+ this.options.cert = cert;
536
+ this.options.dhparam = dhparam;
537
+ return this;
538
+ };
539
+
540
+ RESTP.file = function(name, filename, buffer) {
541
+
542
+ var self = this;
543
+ var obj = { name: name, filename: filename };
544
+
545
+ if (buffer) {
546
+ if (typeof(buffer) === 'string') {
547
+ if (buffer.isURL())
548
+ obj.url = buffer;
549
+ else
550
+ obj.path = buffer;
551
+ } else
552
+ obj.buffer = buffer;
553
+ }
554
+
555
+ if (self.options.files)
556
+ self.options.files.push(obj);
557
+ else
558
+ self.options.files = [obj];
559
+
560
+ return self;
561
+ };
562
+
563
+ RESTP.timeout = function(number) {
564
+ this.options.timeout = number;
565
+ return this;
566
+ };
567
+
568
+ RESTP.maxlength = function(number) {
569
+ this.options.limit = number;
570
+ return this;
571
+ };
572
+
573
+ RESTP.auth = function(user, password) {
574
+ this.options.headers.authorization = password == null ? user : 'Basic ' + Buffer.from(user + ':' + password).toString('base64');
575
+ return this;
576
+ };
577
+
578
+ RESTP.convert = function(convert) {
579
+ this.$convert = convert;
580
+ return this;
581
+ };
582
+
583
+ RESTP.nodnscache = function() {
584
+ this.options.resolve = false;
585
+ return this;
586
+ };
587
+
588
+ RESTP.nocache = function() {
589
+ this.$nocache = true;
590
+ return this;
591
+ };
592
+
593
+ RESTP.make = function(fn) {
594
+ fn.call(this, this);
595
+ return this;
596
+ };
597
+
598
+ RESTP.xhr = function() {
599
+ this.options.xhr = true;
600
+ return this;
601
+ };
602
+
603
+ RESTP.method = function(method, data) {
604
+ this.options.method = method.charCodeAt(0) > 96 ? method.toUpperCase() : method;
605
+ data && this.raw(data, 'json');
606
+ return this;
607
+ };
608
+
609
+ RESTP.referer = RESTP.referrer = function(value) {
610
+ this.options.headers.Referer = value;
611
+ return this;
612
+ };
613
+
614
+ RESTP.origin = function(value) {
615
+ this.options.headers.Origin = value;
616
+ return this;
617
+ };
618
+
619
+ RESTP.robot = function() {
620
+ if (this.options.headers['User-Agent'])
621
+ this.options.headers['User-Agent'] += ' Bot';
622
+ else
623
+ this.options.headers['User-Agent'] = 'Bot';
624
+ return this;
625
+ };
626
+
627
+ RESTP.mobile = function() {
628
+ if (this.options.headers['User-Agent'])
629
+ this.options.headers['User-Agent'] += ' iPhone';
630
+ else
631
+ this.options.headers['User-Agent'] = 'iPhone';
632
+ return this;
633
+ };
634
+
635
+ RESTP.put = RESTP.PUT = function(data) {
636
+ this.options.method = 'PUT';
637
+ data && this.raw(data, this.options.type || 'json');
638
+ return this;
639
+ };
640
+
641
+ RESTP.delete = RESTP.DELETE = function(data) {
642
+ this.options.method = 'DELETE';
643
+ data && this.raw(data, this.options.type || 'json');
644
+ return this;
645
+ };
646
+
647
+ RESTP.get = RESTP.GET = function(data) {
648
+ this.options.method = 'GET';
649
+ this.options.query = data;
650
+ return this;
651
+ };
652
+
653
+ RESTP.post = RESTP.POST = function(data) {
654
+ this.options.method = 'POST';
655
+ data && this.raw(data, this.options.type || 'json');
656
+ return this;
657
+ };
658
+
659
+ RESTP.head = RESTP.HEAD = function() {
660
+ this.options.method = 'HEAD';
661
+ return this;
662
+ };
663
+
664
+ RESTP.patch = RESTP.PATCH = function(data) {
665
+ this.options.method = 'PATCH';
666
+ data && this.raw(data, this.options.type || 'json');
667
+ return this;
668
+ };
669
+
670
+ RESTP.json = function(data) {
671
+ data && this.raw(data, 'json');
672
+ if (this.options.method === 'GET')
673
+ this.options.method = 'POST';
674
+ return this;
675
+ };
676
+
677
+ RESTP.urlencoded = function(data) {
678
+ if (this.options.method === 'GET')
679
+ this.options.method = 'POST';
680
+ this.options.type = 'urlencoded';
681
+ data && this.raw(data, this.options.type);
682
+ return this;
683
+ };
684
+
685
+ RESTP.accept = function(ext) {
686
+ var type;
687
+ if (ext.length > 8)
688
+ type = ext;
689
+ else
690
+ type = F.TUtils.contentTypes[ext];
691
+ this.options.headers.Accept = type;
692
+ return this;
693
+ };
694
+
695
+ RESTP.xml = function(data, replace) {
696
+
697
+ if (this.options.method === 'GET')
698
+ this.options.method = 'POST';
699
+
700
+ if (replace)
701
+ this.$replace = true;
702
+
703
+ this.options.type = 'xml';
704
+ data && this.raw(data, this.options.type);
705
+ return this;
706
+ };
707
+
708
+ RESTP.redirect = function(value) {
709
+ this.options.noredirect = !value;
710
+ return this;
711
+ };
712
+
713
+ RESTP.raw = function(value, type) {
714
+ this.options.type = type;
715
+ this.options.body = value;
716
+ return this;
717
+ };
718
+
719
+ RESTP.text = RESTP.plain = function(val) {
720
+ this.$plain = true;
721
+ this.options.body = val;
722
+ this.options.type = 'plain';
723
+ return this;
724
+ };
725
+
726
+ RESTP.cook = function(value) {
727
+ this.options.cook = value !== false;
728
+ return this;
729
+ };
730
+
731
+ RESTP.cookies = function(obj) {
732
+ this.options.cookies = obj;
733
+ return this;
734
+ };
735
+
736
+ RESTP.cookie = function(name, value) {
737
+ if (!this.options.cookies)
738
+ this.options.cookies = {};
739
+ this.options.cookies[name] = value;
740
+ return this;
741
+ };
742
+
743
+ RESTP.header = function(name, value) {
744
+ this.options.headers[name] = value;
745
+ return this;
746
+ };
747
+
748
+ RESTP.type = function(value) {
749
+ this.options.headers['Content-Type'] = value;
750
+ return this;
751
+ };
752
+
753
+ function execrestbuilder(instance, callback) {
754
+ instance.exec(callback);
755
+ }
756
+
757
+ RESTP.callback = function(fn) {
758
+
759
+ var self = this;
760
+
761
+ if (fn instanceof Options)
762
+ fn = fn.callback();
763
+
764
+ if (typeof(fn) === 'function') {
765
+ setImmediate(execrestbuilder, self, fn);
766
+ return self;
767
+ }
768
+
769
+ self.$ = fn;
770
+ setImmediate(execrestbuilder, self);
771
+ return new Promise(function(resolve, reject) {
772
+ self.$resolve = resolve;
773
+ self.$reject = reject;
774
+ });
775
+ };
776
+
777
+ RESTP.csrf = function(value) {
778
+ this.options.headers['X-Csrf-Token'] = value;
779
+ return this;
780
+ };
781
+
782
+ RESTP.encrypt = function(key) {
783
+ this.options.encrypt = key || DEF.secret_encryption;
784
+ return this;
785
+ };
786
+
787
+ RESTP.compress = function(val) {
788
+ this.$compress = val == null || val == true;
789
+ return this;
790
+ };
791
+
792
+ RESTP.cache = function(expire) {
793
+ this.$expire = expire;
794
+ return this;
795
+ };
796
+
797
+ RESTP.set = function(name, value) {
798
+ if (!this.options.body)
799
+ this.options.body = {};
800
+ if (typeof(name) !== 'object') {
801
+ this.options.body[name] = value;
802
+ } else {
803
+ for (var key in name)
804
+ this.options.body[key] = name[key];
805
+ }
806
+ return this;
807
+ };
808
+
809
+ RESTP.rem = function(name) {
810
+ if (this.options.body && this.options.body[name])
811
+ this.options.body[name] = undefined;
812
+ return this;
813
+ };
814
+
815
+ RESTP.progress = function(fn) {
816
+ this.options.onprogress = fn;
817
+ return this;
818
+ };
819
+
820
+ RESTP.stream = function(callback) {
821
+ var self = this;
822
+ self.options.custom = true;
823
+ setImmediate(streamresponse, self, callback);
824
+ return self;
825
+ };
826
+
827
+ function streamresponse(builder, callback) {
828
+ builder.exec(callback);
829
+ }
830
+
831
+ RESTP.keepalive = function() {
832
+ this.options.keepalive = true;
833
+ return this;
834
+ };
835
+
836
+ RESTP.exec = function(callback) {
837
+
838
+ if (!callback)
839
+ callback = NOOP;
840
+
841
+ var self = this;
842
+
843
+ if (self.operation) {
844
+
845
+ // API
846
+ if (self.options.body)
847
+ self.options.body = { data: self.options.body };
848
+ else
849
+ self.options.body = {};
850
+
851
+ if (self.options.query) {
852
+ self.options.body.query = self.options.query;
853
+ self.options.query = null;
854
+ }
855
+
856
+ self.options.body.schema = self.operation;
857
+ self.options.body = JSON.stringify(self.options.body, self.$compress ? exports.json2replacer : null);
858
+ self.options.type = 'json';
859
+ }
860
+
861
+ if (self.options.files && self.options.method === 'GET')
862
+ self.options.method = 'POST';
863
+
864
+ if (self.options.body && !self.options.files && typeof(self.options.body) !== 'string' && self.options.type !== 'raw')
865
+ self.options.body = self.options.type === 'urlencoded' ? F.TUtils.toURLEncode(self.options.body) : JSON.stringify(self.options.body);
866
+
867
+ if (self.options.unixsocket && self.options.url) {
868
+ if (!self.options.path)
869
+ self.options.path = self.options.url;
870
+ self.options.url = undefined;
871
+ }
872
+
873
+ self.$callback = callback;
874
+
875
+ if (restbuilderupgrades.length) {
876
+ for (var i = 0; i < restbuilderupgrades.length; i++)
877
+ restbuilderupgrades[i](self);
878
+ }
879
+
880
+ var key;
881
+
882
+ if (self.$expire && !self.$nocache) {
883
+ key = 'restbuilder' + ((self.options.url || '') + (self.options.socketpath || '') + (self.options.path || '') + (self.options.body || '')).hash(true);
884
+ var data = F.cache.read(key);
885
+ if (data) {
886
+ data = data.value;
887
+ if (self.$resolve) {
888
+ self.$resolve(data);
889
+ self.$reject = null;
890
+ self.$resolve = null;
891
+ } else
892
+ callback(null, data, data);
893
+ return self;
894
+ }
895
+ }
896
+
897
+ self.$cachekey = key;
898
+ self.options.callback = restbuilder_callback;
899
+ self.options.response = {};
900
+ self.options.response.builder = self;
901
+ self.request = F.TUtils.request(self.options);
902
+ return self;
903
+ };
904
+
905
+ function restbuilder_callback(err, response) {
906
+
907
+ var self = response.builder;
908
+
909
+ if (self.options.custom) {
910
+ if (self.$resolve) {
911
+ if (err)
912
+ self.$.invalid(err);
913
+ else
914
+ self.$resolve(response);
915
+ self.$ = null;
916
+ self.$reject = null;
917
+ self.$resolve = null;
918
+ } else
919
+ self.$callback.call(self, err, response);
920
+ return;
921
+ }
922
+
923
+ var callback = self.$callback;
924
+ var key = self.$cachekey;
925
+ var type = err ? '' : response.headers['content-type'] || '';
926
+ var output = new RESTBuilderResponse();
927
+
928
+ if (self.options.cook && self.options.cookies)
929
+ output.cookies = self.options.cookies;
930
+
931
+ if (type) {
932
+ var index = type.lastIndexOf(';');
933
+ if (index !== -1)
934
+ type = type.substring(0, index).trim();
935
+ }
936
+
937
+ var ishead = response.status === 204;
938
+ if (ishead) {
939
+ output.value = response.status < 400;
940
+ } else if (self.$plain || self.$noparse) {
941
+ output.value = response.body;
942
+ } else {
943
+ switch (type.toLowerCase()) {
944
+ case 'text/xml':
945
+ case 'application/xml':
946
+ output.value = response.body ? response.body.parseXML(self.$replace ? true : false) : {};
947
+ break;
948
+ case 'application/x-www-form-urlencoded':
949
+ output.value = response.body ? DEF.parsers.urlencoded(response.body) : {};
950
+ break;
951
+ case 'application/json':
952
+ case 'text/json':
953
+ case 'text/plain':
954
+ output.value = response.body ? response.body.parseJSON(true) : null;
955
+ break;
956
+ default:
957
+ if (response.body instanceof Buffer && response.body.length)
958
+ response.body = response.body.toString('utf8');
959
+ output.value = response.body ? (response.body.isJSON() ? response.body.parseJSON(true) : response.body) : null;
960
+ break;
961
+ }
962
+ }
963
+
964
+ if (output.value == null)
965
+ output.value = EMPTYOBJECT;
966
+
967
+ output.response = response.body;
968
+ output.status = response.status;
969
+ output.headers = response.headers;
970
+ output.hostname = response.host;
971
+ output.origin = response.origin;
972
+ output.cache = false;
973
+
974
+ if (self.$debug)
975
+ console.log('--DEBUG-- RESTBuilder: ' + response.status + ' ' + self.options.method + ' ' + QUERIFY(self.options.url || (self.options.unixsocket + self.options.path), self.options.query), '|', 'Error:', err, '|', 'Response:', response.body);
976
+
977
+ var val = output.value;
978
+
979
+ if (!err && output.status >= 400) {
980
+ err = output.status;
981
+ if (val instanceof Array && val.length && val[0] && val[0].error)
982
+ err = ErrorBuilder.assign(val);
983
+ else
984
+ err = null;
985
+ }
986
+
987
+ if (!err && key)
988
+ F.cache.add(key, output, self.$expire);
989
+
990
+ if (self.$errorbuilderhandler) {
991
+ // Is the response Total.js ErrorBuilder?
992
+ if (val instanceof Array && val.length && val[0] && val[0].error) {
993
+ err = ErrorBuilder.assign(val);
994
+ if (err)
995
+ val = null;
996
+ }
997
+ }
998
+
999
+ if (!err && output.status >= 400)
1000
+ err = output.status;
1001
+
1002
+ if (!err && self.$jsonschema && val) {
1003
+ let jsresponse = $jsonschema.transform(val);
1004
+ if (jsresponse.error)
1005
+ err = jsresponse.error;
1006
+ else
1007
+ val = jsresponse.response;
1008
+ }
1009
+
1010
+ if (self.$resolve) {
1011
+
1012
+ if (err)
1013
+ self.$.invalid(err);
1014
+ else
1015
+ self.$resolve(val);
1016
+
1017
+ self.$ = null;
1018
+ self.$reject = null;
1019
+ self.$resolve = null;
1020
+
1021
+ } else {
1022
+ callback(err, val, output);
1023
+ output.cache = true;
1024
+ }
1025
+
1026
+ }
1027
+
1028
+ function RESTBuilderResponse() {}
1029
+
1030
+ RESTBuilderResponse.prototype.cookie = function(name) {
1031
+
1032
+ var self = this;
1033
+ if (self.cookies)
1034
+ return F.TUtils.decodeURIComponent(self.cookies[name] || '');
1035
+
1036
+ self.cookies = {};
1037
+
1038
+ var cookies = self.headers['set-cookie'];
1039
+ if (!cookies)
1040
+ return '';
1041
+
1042
+ if (typeof(cookies) === 'string')
1043
+ cookies = [cookies];
1044
+
1045
+ for (var i = 0; i < cookies.length; i++) {
1046
+ var line = cookies[i].split(';', 1)[0];
1047
+ var index = line.indexOf('=');
1048
+ if (index !== -1)
1049
+ self.cookies[line.substring(0, index)] = line.substring(index + 1);
1050
+ }
1051
+
1052
+ return F.TUtils.decodeURIComponent(self.cookies[name] || '');
1053
+ };
1054
+
1055
+ function parseactioncache(obj, meta) {
1056
+
1057
+ var query = meta.query;
1058
+ var user = meta.user;
1059
+ var params = meta.params;
1060
+ var language = meta.language;
1061
+ var search = meta.id || meta.key;
1062
+
1063
+ if (typeof(user) === 'string')
1064
+ user = user.split(',').trim();
1065
+ else if (user === true)
1066
+ user = ['id'];
1067
+ else
1068
+ user = null;
1069
+
1070
+ if (typeof(params) === 'string')
1071
+ params = params.split(',').trim();
1072
+ else if (params === true) {
1073
+ if (obj.jsparams) {
1074
+ params = [];
1075
+ for (var key in obj.jsparams.properties)
1076
+ params.push(key);
1077
+ } else
1078
+ params = null;
1079
+ } else
1080
+ params = null;
1081
+
1082
+ if (typeof(query) === 'string')
1083
+ query = query.split(',').trim();
1084
+ else if (query === true) {
1085
+ if (obj.jsquery) {
1086
+ query = [];
1087
+ for (var key in obj.jsquery.properties)
1088
+ query.push(key);
1089
+ } else
1090
+ query = null;
1091
+ } else
1092
+ query = null;
1093
+
1094
+ return function($, value) {
1095
+ if (value === undefined) {
1096
+
1097
+ var key = 'action|' + (search ? (search + '|') : '') + $.ID;
1098
+ var sum = '';
1099
+ var tmp;
1100
+
1101
+ if (language)
1102
+ sum += ($.language || '');
1103
+
1104
+ if (query) {
1105
+ for (let key of query) {
1106
+ tmp = $.query[key];
1107
+ if (tmp)
1108
+ sum += '|' + tmp;
1109
+ }
1110
+ }
1111
+
1112
+ if (params) {
1113
+ for (let key of params) {
1114
+ tmp = $.params[key];
1115
+ if (tmp)
1116
+ sum += '|' + tmp;
1117
+ }
1118
+ }
1119
+
1120
+ if (user && $.user) {
1121
+ for (let key of user) {
1122
+ tmp = $.user[key];
1123
+ if (tmp)
1124
+ sum += '|' + tmp;
1125
+ }
1126
+ }
1127
+
1128
+ $.cachekey = key + sum;
1129
+ return F.cache.read($.cachekey);
1130
+ }
1131
+
1132
+ $.cachekey && F.cache.set($.cachekey, value && value.success ? CLONE(value) : value, meta.expire || '5 minutes');
1133
+ };
1134
+
1135
+ }
1136
+
1137
+ exports.newaction = function(name, obj) {
1138
+
1139
+ if (typeof(name) === 'object') {
1140
+ obj = name;
1141
+ name = obj.id || obj.name;
1142
+ }
1143
+
1144
+ var url = name;
1145
+ var tmp = name.split('/').trim();
1146
+ if (tmp.length)
1147
+ obj.$url = url.replace(/\//g, '_').toLowerCase();
1148
+
1149
+ if (F.actions[name])
1150
+ F.actions[name].remove();
1151
+
1152
+ F.actions[name] = obj;
1153
+ obj.id = name;
1154
+ obj.jsinput = obj.input ? F.TUtils.jsonschema(obj.input, true) : null;
1155
+ obj.jsoutput = obj.output ? F.TUtils.jsonschema(obj.output, true) : null;
1156
+ obj.jsparams = obj.params ? F.TUtils.jsonschema(obj.params, true) : null;
1157
+ obj.jsquery = obj.query ? F.TUtils.jsonschema(obj.query, true) : null;
1158
+ obj.options = {};
1159
+ obj.options.csrf = obj.csrf;
1160
+ obj.options.encrypt = obj.encrypt;
1161
+ obj.options.compress = obj.compress;
1162
+
1163
+ if (obj.cache)
1164
+ obj.cache = parseactioncache(obj, obj.cache);
1165
+
1166
+ if (obj.middleware)
1167
+ obj.middleware = obj.middleware.replace(/,/g, ' ').replace(/\s{2,}/, ' ');
1168
+
1169
+ obj.remove = function() {
1170
+ obj.route && obj.route.remove();
1171
+ delete F.actions[obj.id];
1172
+ obj = null;
1173
+ F.makesourcemap && F.makesourcemap();
1174
+ };
1175
+
1176
+ if (obj.route) {
1177
+ if (obj.route.indexOf('-->') === -1)
1178
+ obj.route = obj.route + ' ' + (obj.input ? '+' : '-') + obj.$url + ' --> ' + name;
1179
+ obj.route = F.route(obj.route + (obj.encrypt ? ' @encrypt' : ''));
1180
+ }
1181
+
1182
+ if (obj.permissions && typeof(obj.permissions) === 'string')
1183
+ obj.permissions = obj.permissions.split(/,|;/).trim();
1184
+
1185
+ if (obj.publish) {
1186
+
1187
+ var tmsschema = obj.publish == true ? (obj.input || obj.output) : obj.publish;
1188
+
1189
+ if (typeof(tmsschema) === 'string') {
1190
+ if (tmsschema[0] === '+')
1191
+ tmsschema = (obj.input || obj.output) + ',' + tmsschema.substring(1);
1192
+
1193
+ var keys = tmsschema.split(',');
1194
+ obj.$publish = [];
1195
+ for (var key of keys) {
1196
+ var index = key.indexOf(':');
1197
+ obj.$publish.push(index === -1 ? key : key.substring(0, index));
1198
+ }
1199
+ }
1200
+
1201
+ F.TTMS.newpublish(name, tmsschema);
1202
+ }
1203
+
1204
+ F.makesourcemap && F.makesourcemap();
1205
+ return obj;
1206
+ };
1207
+
1208
+ function ActionCallerExec(self) {
1209
+ self.exec();
1210
+ }
1211
+
1212
+ function ActionCaller() {
1213
+ var self = this;
1214
+ self.$ = new Options();
1215
+ self.error = new ErrorBuilder();
1216
+ self.options = {};
1217
+ self.actions = [];
1218
+ setImmediate(ActionCallerExec, self);
1219
+ }
1220
+
1221
+ ActionCaller.prototype.debug = function() {
1222
+ this.options.debug = true;
1223
+ return this;
1224
+ };
1225
+
1226
+ ActionCaller.prototype.params = function(value) {
1227
+ this.options.params = value;
1228
+ return this;
1229
+ };
1230
+
1231
+ ActionCaller.prototype.exec = function() {
1232
+
1233
+ var self = this;
1234
+ var id = self.actions.shift();
1235
+
1236
+ if (!id) {
1237
+ self.finish && self.finish();
1238
+ self.error = null;
1239
+ self.options = null;
1240
+ self.$ = null;
1241
+ return;
1242
+ }
1243
+
1244
+ var meta = F.temporary.actions[id];
1245
+ if (!meta) {
1246
+
1247
+ let arr = id.split(' ');
1248
+
1249
+ meta = {};
1250
+ meta.response = arr[1] ? true : false;
1251
+ meta.id = arr[0];
1252
+ meta.payload = null;
1253
+
1254
+ let c = meta.id[0];
1255
+ if (c === '+' || c === '-' || c === '%') {
1256
+ // + payload
1257
+ // - without payload
1258
+ // % partial payload
1259
+ meta.payload = c;
1260
+ meta.id = meta.id.substring(1);
1261
+ }
1262
+
1263
+ F.temporary.actions[id] = meta;
1264
+ }
1265
+
1266
+ var action = F.actions[meta.id];
1267
+
1268
+ if (!action) {
1269
+ self.error.push('The action "{0}" not found'.format(meta.id));
1270
+ self.cancel();
1271
+ return;
1272
+ }
1273
+
1274
+ var type = meta.payload || (action.input ? '+' : '-');
1275
+ var $ = self.$;
1276
+
1277
+ $.id = action.id;
1278
+ $.error = self.error;
1279
+ $.controller = self.controller;
1280
+ $.fields = action.fields;
1281
+ $.user = self.options.user;
1282
+
1283
+ $.$callback = function(err, response) {
1284
+ if (err) {
1285
+ // close
1286
+ self.cancel();
1287
+ } else {
1288
+ $.response[$.id] = response;
1289
+ meta.response && self.finish && self.finish(response);
1290
+ var key = '@' + $.id;
1291
+ if (F.$events[key])
1292
+ F.emit(key, $, response);
1293
+ self.exec();
1294
+ }
1295
+ };
1296
+
1297
+ if (action.user && !$.user) {
1298
+ $.invalid(401);
1299
+ return;
1300
+ }
1301
+
1302
+ if (action.sa) {
1303
+ if (!$.user || (!$.user.sa && !$.user.su)) {
1304
+ $.invalid(401);
1305
+ return;
1306
+ }
1307
+ }
1308
+
1309
+ if (action.permissions) {
1310
+ let permissions = action.permissions.slice(0);
1311
+ permissions.unshift($);
1312
+ if (F.unauthorized.apply(global, permissions)) {
1313
+ self.finish = null;
1314
+ self.cancel();
1315
+ return;
1316
+ }
1317
+ }
1318
+
1319
+ var params = self.options.params || EMPTYOBJECT;
1320
+ var query = self.options.query || EMPTYOBJECT;
1321
+ var payload = self.options.payload || EMPTYOBJECT;
1322
+ var response = null;
1323
+
1324
+ if (action.jsquery) {
1325
+ self.error.prefix = 'query.';
1326
+ response = action.jsquery.transform(query, false, self.error);
1327
+ self.error.prefix = '';
1328
+ if (response.error) {
1329
+ self.cancel();
1330
+ return;
1331
+ }
1332
+ $.query = response.response;
1333
+ } else
1334
+ $.query = query;
1335
+
1336
+ if (action.jsparams) {
1337
+ self.error.prefix = 'params.';
1338
+ response = action.jsparams.transform(params, false, self.error);
1339
+ self.error.prefix = '';
1340
+ if (response.error) {
1341
+ self.cancel();
1342
+ return;
1343
+ }
1344
+ $.params = response.response;
1345
+ } else
1346
+ $.params = params;
1347
+
1348
+ if (action.jsinput && type !== '-') {
1349
+ response = action.jsinput.transform(payload, action.partial, self.error);
1350
+ if (response.error) {
1351
+ self.cancel();
1352
+ return;
1353
+ }
1354
+ $.payload = response.response;
1355
+ } else
1356
+ $.payload = payload;
1357
+
1358
+ action.action($, $.payload);
1359
+ };
1360
+
1361
+ ActionCaller.prototype.finish = function(value) {
1362
+ var self = this;
1363
+ self.finish = null;
1364
+ if (self.options.callback) {
1365
+ self.options.callback(self.error.length ? self.error : null, value === undefined ? self.$.response : value);
1366
+ self.options.callback = null;
1367
+ }
1368
+ };
1369
+
1370
+ ActionCaller.prototype.cancel = function() {
1371
+ var self = this;
1372
+ self.actions.length = 0;
1373
+ self.exec();
1374
+ };
1375
+
1376
+ ActionCaller.prototype.payload = function(value) {
1377
+ this.options.payload = value;
1378
+ return this;
1379
+ };
1380
+
1381
+ ActionCaller.prototype.query = function(value) {
1382
+ this.options.query = value;
1383
+ return this;
1384
+ };
1385
+
1386
+ ActionCaller.prototype.user = function(value) {
1387
+
1388
+ if (value instanceof Options)
1389
+ value = value.user;
1390
+
1391
+ this.options.user = value;
1392
+ return this;
1393
+ };
1394
+
1395
+ ActionCaller.prototype.language = function(value) {
1396
+ this.options.language = value;
1397
+ return this;
1398
+ };
1399
+
1400
+ ActionCaller.prototype.error = function(value) {
1401
+ this.options.error = value;
1402
+ return this;
1403
+ };
1404
+
1405
+ ActionCaller.prototype.done = function($, fn) {
1406
+ this.options.callback = function(err, response) {
1407
+ if (err)
1408
+ $.invalid(err);
1409
+ else
1410
+ fn(response);
1411
+ };
1412
+ return this;
1413
+ };
1414
+
1415
+ ActionCaller.prototype.callback = function(value) {
1416
+ this.options.callback = value;
1417
+ return this;
1418
+ };
1419
+
1420
+ ActionCaller.prototype.promise = function($) {
1421
+ var self = this;
1422
+ return new Promise(function(resolve, reject) {
1423
+ self.options.callback = function(err, response) {
1424
+ if (err) {
1425
+ self.options.error && self.options.error(err);
1426
+ if ($ && $.invalid)
1427
+ $.invalid(err);
1428
+ else
1429
+ reject(err.reject());
1430
+ } else
1431
+ resolve(response);
1432
+ };
1433
+ });
1434
+ };
1435
+
1436
+ ActionCaller.prototype.autorespond = function() {
1437
+ var self = this;
1438
+ self.options.callback = function(err, response, a, b) {
1439
+ if (err)
1440
+ self.controller.invalid(err);
1441
+ else
1442
+ self.controller.respond(response, a, b);
1443
+ };
1444
+ return self;
1445
+ };
1446
+
1447
+ ActionCaller.prototype.controller = function(ctrl) {
1448
+ if (ctrl instanceof Options)
1449
+ ctrl = ctrl.controller;
1450
+ this.options.controller = ctrl;
1451
+ return this;
1452
+ };
1453
+
1454
+ exports.action = function(name, payload, controller) {
1455
+
1456
+ var key = '$' + name;
1457
+ var actions = F.temporary.actions[key];
1458
+
1459
+ if (!actions) {
1460
+ actions = name.replace(/(\s)?\(response\)/i, '\0').split(/\s|\,|\n/);
1461
+ let isresponse = false;
1462
+ for (let i = 0; i < actions.length; i++) {
1463
+ actions[i] = actions[i].replaceAll('\0', ' (response)');
1464
+ if (actions[i].indexOf('(') !== -1)
1465
+ isresponse = true;
1466
+ }
1467
+
1468
+ if (actions.length === 1 && !isresponse)
1469
+ actions[0] += ' (response)';
1470
+ F.temporary.actions[key] = actions;
1471
+ }
1472
+
1473
+ var action = new ActionCaller();
1474
+ action.controller = controller;
1475
+ action.payload = payload;
1476
+ action.actions = actions.slice(0);
1477
+ action.options.payload = payload;
1478
+ action.options.user = controller?.user;
1479
+ action.options.query = controller?.query;
1480
+ action.options.params = controller?.params;
1481
+ return action;
1482
+ };
1483
+
1484
+ exports.newschema = function(name, callback) {
1485
+
1486
+ if (name[0] === '@')
1487
+ name = name.substring(1);
1488
+
1489
+ if (typeof(callback) === 'string')
1490
+ return F.jsonschemas[name] = F.TUtils.jsonschema(callback, true);
1491
+
1492
+ var $ = {};
1493
+ $.name = name;
1494
+ $.actions = {};
1495
+ $.action = function(aname, meta) {
1496
+ return $.actions[aname] = F.newaction(name + '/' + aname, meta);
1497
+ };
1498
+
1499
+ callback($);
1500
+ };
1501
+
1502
+ exports.builtinauth = function(opt) {
1503
+
1504
+ // opt.secret {String}
1505
+ // opt.ddos {Number}
1506
+ // opt.expire {String}
1507
+ // opt.cookie {String} A cookie name
1508
+ // opt.header {String} A header name
1509
+ // opt.options {Object} A cookie options
1510
+ // opt.strict {Boolean}
1511
+
1512
+ if (opt.strict == null)
1513
+ opt.strict = true;
1514
+
1515
+ // Delegates
1516
+ // opt.onddos = function($)
1517
+ // opt.onread = function({ sessionid: String, userid: String, ua: String }, callback(USER_DATA), $)
1518
+ // opt.onfree = function({ sessions: Array, users: Array })
1519
+ // opt.onlogout = function(sessionid, userid)
1520
+ // opt.onauthorize = function($) must return true for canceling of processing
1521
+
1522
+ opt.sessions = {};
1523
+ opt.blocked = {};
1524
+ opt.pending = {};
1525
+
1526
+ if (!opt.cleaner)
1527
+ opt.cleaner = 5;
1528
+
1529
+ if (!opt.secret)
1530
+ opt.secret = F.secret;
1531
+
1532
+ opt.logout = function($) {
1533
+
1534
+ var id = $;
1535
+
1536
+ if (typeof(id) === 'object')
1537
+ id = $.sessionid;
1538
+
1539
+ var ctrl = $.controller;
1540
+
1541
+ if (ctrl && !id)
1542
+ id = ctrl.sessionid;
1543
+
1544
+ if (id) {
1545
+ for (var key in opt.sessions) {
1546
+ var session = opt.sessions[key];
1547
+ if (session.sessionid === id) {
1548
+ delete opt.sessions[key];
1549
+ opt.onlogout && opt.onlogout(session);
1550
+ opt.cookie && ctrl && !ctrl.parent && ctrl.cookie && ctrl.cookie(opt.cookie, '', '-1 year', opt.options);
1551
+ return true;
1552
+ }
1553
+ }
1554
+ opt.onremove && opt.onremove(id);
1555
+ }
1556
+ };
1557
+
1558
+ opt.update = function(userid, fn) {
1559
+ var count = 0;
1560
+ for (var key in opt.sessions) {
1561
+ var session = opt.sessions[key];
1562
+ if (session.userid === userid) {
1563
+ count++;
1564
+ fn(session.data, session);
1565
+ }
1566
+ }
1567
+ return count;
1568
+ };
1569
+
1570
+ opt.refresh = function(userid, exceptsessionid) {
1571
+ var count = 0;
1572
+ for (var key in opt.sessions) {
1573
+ var session = opt.sessions[key];
1574
+ if (session.userid === userid && session.sessionid !== exceptsessionid) {
1575
+ count++;
1576
+ delete opt.sessions[key];
1577
+ }
1578
+ }
1579
+ return count;
1580
+ };
1581
+
1582
+ opt.sign = function(sessionid, userid) {
1583
+ return (sessionid + SESSIONSEPARATOR + userid + SESSIONSEPARATOR + Date.now().toString(36)).encrypt(opt.secret);
1584
+ };
1585
+
1586
+ opt.authcookie = function($, sessionid, userid, expiration, options) {
1587
+ if (!options)
1588
+ options = opt.options;
1589
+ var ctrl = $.controller ? $.controller : $;
1590
+ var token = opt.sign(sessionid, userid);
1591
+ ctrl.cookie && !ctrl.parent && $.cookie(opt.cookie, token, expiration, options);
1592
+ return token;
1593
+ };
1594
+
1595
+ if (!opt.expire)
1596
+ opt.expire = '5 minutes';
1597
+
1598
+ var callpending = function(pending, data) {
1599
+ for (var i = 0; i < pending.length; i++) {
1600
+ if (data)
1601
+ pending[i].success(data);
1602
+ else
1603
+ pending[i].invalid();
1604
+ }
1605
+ };
1606
+
1607
+ opt.auth = function($) {
1608
+
1609
+ if (opt.onauthorize && opt.onauthorize($))
1610
+ return;
1611
+
1612
+ var sessionid = opt.cookie ? $.cookie(opt.cookie) : null;
1613
+ if (!sessionid && opt.header)
1614
+ sessionid = $.controller.headers[opt.header];
1615
+
1616
+ var localize = opt.locale || opt.localize;
1617
+
1618
+ if (!sessionid) {
1619
+
1620
+ if (localize)
1621
+ $.controller.language = localize(null, $.controller);
1622
+
1623
+ $.invalid();
1624
+ return;
1625
+ }
1626
+
1627
+ var id = sessionid.decrypt(opt.secret);
1628
+ if (id) {
1629
+
1630
+ id = id.split(SESSIONSEPARATOR);
1631
+
1632
+ if (!id[0] || !id[1] || !id[2])
1633
+ id = null;
1634
+
1635
+ if (id) {
1636
+ var session = opt.sessions[id[0]];
1637
+ if (session && session.data) {
1638
+ if (!opt.strict || session.ua === $.controller.ua) {
1639
+ $.controller.session = session;
1640
+ $.controller.sessionid = session.sessionid;
1641
+ if (!opt.onsession || !opt.onsession(session, $)) {
1642
+ if (localize)
1643
+ $.controller.language = localize(session.data, $.controller);
1644
+ $.success(session.data);
1645
+ }
1646
+ } else {
1647
+
1648
+ if (localize)
1649
+ $.controller.language = localize(null, $.controller);
1650
+
1651
+ $.invalid();
1652
+ sessionid = null;
1653
+ }
1654
+ return;
1655
+ }
1656
+ }
1657
+ }
1658
+
1659
+ if (opt.ddos && opt.blocked[$.controller.ip] > opt.ddos) {
1660
+ opt.onddos && opt.onddos($);
1661
+ $.invalid();
1662
+ return;
1663
+ }
1664
+
1665
+ if (!id) {
1666
+
1667
+ if (opt.ddos) {
1668
+ if (opt.blocked[$.controller.ip])
1669
+ opt.blocked[$.controller.ip]++;
1670
+ else
1671
+ opt.blocked[$.controller.ip] = 1;
1672
+ }
1673
+
1674
+ opt.cookie && $.controller && !$.controller.parent && $.controller.cookie && $.controller.cookie(opt.cookie, '', '-1 year', opt.options);
1675
+ $.invalid();
1676
+ return;
1677
+ }
1678
+
1679
+ var meta = { ip: $.controller.ip, ua: $.controller.ua, sessionid: id[0], userid: id[1] };
1680
+
1681
+ if (opt.pending[meta.sessionid]) {
1682
+ opt.pending[meta.sessionid].push($);
1683
+ return;
1684
+ }
1685
+
1686
+ opt.pending[meta.sessionid] = [];
1687
+ opt.onread(meta, function(err, data) {
1688
+
1689
+ var pending = opt.pending[meta.sessionid];
1690
+ delete opt.pending[meta.sessionid];
1691
+
1692
+ if (!err && data) {
1693
+
1694
+ $.controller.session = opt.sessions[meta.sessionid] = { sessionid: meta.sessionid, userid: meta.userid, data: data, ua: $.controller.ua, expire: NOW.add(opt.expire) };
1695
+ $.controller.sessionid = meta.sessionid;
1696
+
1697
+ if (localize)
1698
+ $.controller.language = localize(data, $.controller);
1699
+
1700
+ if (!opt.onsession || !opt.onsession($.controller.session, $, true))
1701
+ $.success(data);
1702
+
1703
+ if (pending.length)
1704
+ setImmediate(callpending, pending, data);
1705
+
1706
+ } else {
1707
+
1708
+ if (opt.ddos) {
1709
+ if (opt.blocked[$.controller.ip])
1710
+ opt.blocked[$.controller.ip]++;
1711
+ else
1712
+ opt.blocked[$.controller.ip] = 1;
1713
+ }
1714
+
1715
+ opt.cookie && !$.controller.parent && $.controller.cookie && $.controller.cookie(opt.cookie, '', '-1 year', opt.options);
1716
+ $.invalid();
1717
+
1718
+ if (pending.length)
1719
+ setImmediate(callpending, pending);
1720
+ }
1721
+
1722
+ }, $);
1723
+
1724
+ };
1725
+
1726
+ F.def.onAuthorize = opt.auth;
1727
+
1728
+ F.on('service', function(counter) {
1729
+
1730
+ if (counter % opt.cleaner)
1731
+ return;
1732
+
1733
+ var expired = [];
1734
+ var users_expired = {};
1735
+ var users_live = {};
1736
+
1737
+ for (var key in opt.sessions) {
1738
+ var session = opt.sessions[key];
1739
+ if (session.expire < NOW) {
1740
+ expired.push(key);
1741
+ delete opt.sessions[key];
1742
+ users_expired[session.userid] = 1;
1743
+ } else
1744
+ users_live[session.userid] = 1;
1745
+ }
1746
+
1747
+ if (expired.length) {
1748
+ for (var key in users_expired) {
1749
+ if (users_live[key])
1750
+ delete users_expired[key];
1751
+ }
1752
+ }
1753
+
1754
+ if (expired.length && opt.onfree) {
1755
+ var meta = {};
1756
+ meta.sessions = expired;
1757
+ meta.users = expired.length ? Object.keys(users_expired) : null;
1758
+ opt.onfree && opt.onfree(meta);
1759
+ }
1760
+
1761
+ opt.blocked = {};
1762
+
1763
+ });
1764
+
1765
+ return opt;
1766
+ };
1767
+
1768
+ exports.RESTBuilder = RESTBuilder;
1769
+ exports.ErrorBuilder = ErrorBuilder;
1770
+ exports.Options = Options;