1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 | 'use strict';
|
10 |
|
11 | function sort_fn(a, b) {
|
12 | if (a.date < b.date){
|
13 | return -1;
|
14 | }
|
15 | else if (a.date > b.date){
|
16 | return 1;
|
17 | }
|
18 | else{
|
19 | return 0;
|
20 | }
|
21 | }
|
22 | class RamIndexer {
|
23 | static waitError() {
|
24 | const err = new Error('Индекс прочитн не полностью, повторите запрос позже');
|
25 | err.status = 403;
|
26 | throw err;
|
27 | }
|
28 | static truth(fld, cond) {
|
29 | const blank = '00000000-0000-0000-0000-000000000000';
|
30 | if(cond === true || (cond && cond.hasOwnProperty('$ne') && !cond.$ne)) {
|
31 | return function (doc) {
|
32 | return doc[fld];
|
33 | };
|
34 | }
|
35 | else if(cond === false || (cond && cond.hasOwnProperty('$ne') && cond.$ne && typeof cond.$ne === 'boolean')) {
|
36 | return function (doc) {
|
37 | return !doc[fld];
|
38 | };
|
39 | }
|
40 | else if(cond && cond.hasOwnProperty('filled')) {
|
41 | return function (doc) {
|
42 | return doc[fld] && doc[fld] !== blank;
|
43 | };
|
44 | }
|
45 | else if(cond && cond.hasOwnProperty('nfilled')) {
|
46 | return function (doc) {
|
47 | return !doc[fld] || doc[fld] === blank;
|
48 | };
|
49 | }
|
50 | else if(cond && cond.hasOwnProperty('$ne')) {
|
51 | return function (doc) {
|
52 | return doc[fld] !== cond.$ne;
|
53 | };
|
54 | }
|
55 | else if(cond && cond.hasOwnProperty('$in')) {
|
56 | const acond = typeof cond.$in === 'string' ? cond.$in.split(',').map((v) => v.trim()) : cond.$in;
|
57 | return function (doc) {
|
58 | return acond.includes(doc[fld]);
|
59 | };
|
60 | }
|
61 | else if(cond && cond.hasOwnProperty('$nin')) {
|
62 | const acond = typeof cond.$nin === 'string' ? cond.$nin.split(',').map((v) => v.trim()) : cond.$nin;
|
63 | return function (doc) {
|
64 | return !acond.includes(doc[fld]);
|
65 | };
|
66 | }
|
67 | else {
|
68 | return function (doc) {
|
69 | return doc[fld] === cond;
|
70 | };
|
71 | }
|
72 | }
|
73 | constructor({fields, search_fields, mgr}) {
|
74 | this._fields = fields;
|
75 | this._search_fields = search_fields;
|
76 | this._mgrs = Array.isArray(mgr) ? mgr : [mgr];
|
77 | this._count = 0;
|
78 | this._ready = false;
|
79 | this._listeners = new Map();
|
80 | this._area = this._mgrs.length > 1;
|
81 | this.by_date = {};
|
82 | }
|
83 | sort() {
|
84 | for(const date in this.by_date) {
|
85 | this.by_date[date].sort(sort_fn);
|
86 | }
|
87 | this._ready = true;
|
88 | }
|
89 | put(indoc, force) {
|
90 | const doc = {};
|
91 | if(this._area) {
|
92 | doc._area = indoc._area;
|
93 | }
|
94 | this._fields.forEach((fld) => {
|
95 | if(indoc.hasOwnProperty(fld)) {
|
96 | doc[fld] = indoc[fld];
|
97 | }
|
98 | });
|
99 | const date = doc.date.substr(0, 7);
|
100 | const arr = this.by_date[date];
|
101 | if(arr) {
|
102 | if(force || !arr.some((row) => {
|
103 | if(row._id === doc._id) {
|
104 | Object.assign(row, doc);
|
105 | return true;
|
106 | }
|
107 | })) {
|
108 | arr.push(doc);
|
109 | !force && arr.sort(sort_fn);
|
110 | }
|
111 | }
|
112 | else {
|
113 | this.by_date[date] = [doc];
|
114 | }
|
115 | }
|
116 | get_range(from, till, step, desc) {
|
117 | if(!from || !till) {
|
118 | return [];
|
119 | }
|
120 | if(desc) {
|
121 | if(step) {
|
122 | let [year, month] = till.split('-');
|
123 | month = parseInt(month, 10) - step;
|
124 | while (month < 1) {
|
125 | year = parseInt(year, 10) - 1;
|
126 | month += 12;
|
127 | }
|
128 | till = `${year}-${month.pad(2)}`;
|
129 | }
|
130 | if(till < from) {
|
131 | return null;
|
132 | }
|
133 | let res = this.by_date[till];
|
134 | if(!res) {
|
135 | res = [];
|
136 | }
|
137 | return res;
|
138 | }
|
139 | else {
|
140 | if(step) {
|
141 | let [year, month] = from.split('-');
|
142 | month = parseInt(month, 10) + step;
|
143 | while (month > 12) {
|
144 | year = parseInt(year, 10) + 1;
|
145 | month -= 12;
|
146 | }
|
147 | from = `${year}-${month.pad(2)}`;
|
148 | }
|
149 | if(from > till) {
|
150 | return null;
|
151 | }
|
152 | let res = this.by_date[from];
|
153 | if(!res) {
|
154 | res = [];
|
155 | }
|
156 | return res;
|
157 | }
|
158 | }
|
159 | find({selector, sort, ref, limit, skip = 0}, auth) {
|
160 | if(!this._ready) {
|
161 | RamIndexer.waitError();
|
162 | }
|
163 | let dfrom, dtill, from, till, search;
|
164 | for(const row of selector.$and) {
|
165 | const fld = Object.keys(row)[0];
|
166 | const cond = Object.keys(row[fld])[0];
|
167 | if(fld === 'date') {
|
168 | if(cond === '$lt' || cond === '$lte') {
|
169 | dtill = row[fld][cond];
|
170 | till = dtill.substr(0,7);
|
171 | }
|
172 | else if(cond === '$gt' || cond === '$gte') {
|
173 | dfrom = row[fld][cond];
|
174 | from = dfrom.substr(0,7);
|
175 | }
|
176 | }
|
177 | else if(fld === 'search') {
|
178 | search = row[fld][cond] ? row[fld][cond].toLowerCase().split(' ') : [];
|
179 | }
|
180 | }
|
181 | if(sort && sort.length && sort[0][Object.keys(sort[0])[0]] === 'desc' || sort === 'desc') {
|
182 | sort = 'desc';
|
183 | }
|
184 | else {
|
185 | sort = 'asc';
|
186 | }
|
187 | const {_search_fields} = this;
|
188 | const {utils} = $p;
|
189 | let part,
|
190 | step = 0,
|
191 | flag = skip === 0 && utils.is_guid(ref),
|
192 | scroll = null,
|
193 | count = 0;
|
194 | const docs = [];
|
195 | function add(doc) {
|
196 | count++;
|
197 | if(flag && doc._id.endsWith(ref)) {
|
198 | scroll = count - 1;
|
199 | flag = false;
|
200 | }
|
201 | if(skip > 0) {
|
202 | return skip--;
|
203 | }
|
204 | if(limit > 0) {
|
205 | limit--;
|
206 | docs.push(doc);
|
207 | }
|
208 | }
|
209 | function check(doc) {
|
210 | if(doc.date < dfrom || doc.date > dtill) {
|
211 | return;
|
212 | }
|
213 | let ok = true;
|
214 | for(const word of search) {
|
215 | if(!word) {
|
216 | continue;
|
217 | }
|
218 | if(!_search_fields.some((fld) => {
|
219 | const val = doc[fld];
|
220 | return val && typeof val === 'string' && val.toLowerCase().includes(word);
|
221 | })){
|
222 | ok = false;
|
223 | break;
|
224 | }
|
225 | }
|
226 | ok && add(doc);
|
227 | }
|
228 | while((part = this.get_range(from, till, step, sort === 'desc'))) {
|
229 | step += 1;
|
230 | if(sort === 'desc') {
|
231 | for(let i = part.length - 1; i >= 0; i--){
|
232 | check(part[i]);
|
233 | }
|
234 | }
|
235 | else {
|
236 | for(let i = 0; i < part.length; i++){
|
237 | check(part[i]);
|
238 | }
|
239 | }
|
240 | }
|
241 | return {docs, scroll, flag, count};
|
242 | }
|
243 | init(bookmark, _mgr) {
|
244 | if(!_mgr) {
|
245 | return this._mgrs.reduce((sum, _mgr) => sum.then(() => this.init(bookmark, _mgr)), Promise.resolve())
|
246 | .then(() => {
|
247 | this.sort();
|
248 | });
|
249 | }
|
250 | if(!bookmark) {
|
251 | const listener = (change) => {
|
252 | if(!change) {
|
253 | return;
|
254 | }
|
255 | if(this._area) {
|
256 | change._area = _mgr.cachable;
|
257 | }
|
258 | this.put(change);
|
259 | };
|
260 | this._listeners.set(_mgr, listener);
|
261 | _mgr.on('change', listener);
|
262 | }
|
263 | return _mgr.pouch_db.find({
|
264 | selector: {
|
265 | class_name: _mgr.class_name,
|
266 | },
|
267 | fields: this._fields,
|
268 | bookmark,
|
269 | limit: 10000,
|
270 | })
|
271 | .then(({bookmark, docs}) => {
|
272 | this._count += docs.length;
|
273 | for(const doc of docs) {
|
274 | if(this._area) {
|
275 | doc._area = _mgr.cachable;
|
276 | }
|
277 | this.put(doc, true);
|
278 | }
|
279 | _mgr.adapter.emit('indexer_page', {indexer: this, bookmark: bookmark || '', _mgr});
|
280 | return docs.length === 10000 && this.init(bookmark, _mgr);
|
281 | });
|
282 | }
|
283 | reset(mgrs) {
|
284 | for(const date in this.by_date) {
|
285 | this.by_date[date].length = 0;
|
286 | }
|
287 | for(const [_mgr, listener] of this._listeners) {
|
288 | _mgr.off('change', listener);
|
289 | }
|
290 | this._listeners.clear();
|
291 | this._mgrs.length = 0;
|
292 | mgrs && this._mgrs.push.apply(this._mgrs, mgrs);
|
293 | this._area = this._mgrs.length > 1;
|
294 | }
|
295 | }
|
296 |
|
297 | var proto = ({classes}) => {
|
298 | const {DataManager, DataObj, DocObj, TaskObj, BusinessProcessObj} = classes;
|
299 | classes.RamIndexer = RamIndexer;
|
300 | DataObj.prototype.new_number_doc = function new_number_doc(prefix) {
|
301 | if (!this._metadata().code_length) {
|
302 | return Promise.resolve(this);
|
303 | }
|
304 | const {organization, _manager} = this;
|
305 | const {current_user, utils} = _manager._owner.$p;
|
306 | if(this.date === utils.blank.date) {
|
307 | this.date = new Date();
|
308 | }
|
309 | const year = (this.date instanceof Date) ? this.date.getFullYear() : 0;
|
310 | if (!prefix) {
|
311 | prefix = ((current_user && current_user.prefix) || '') + ((organization && organization.prefix) || '');
|
312 | }
|
313 | let part = '',
|
314 | code_length = this._metadata().code_length - prefix.length;
|
315 | if (_manager.cachable == 'ram' || _manager.cachable == 'doc_ram') {
|
316 | return Promise.resolve(this.new_cat_id(prefix));
|
317 | }
|
318 | return _manager.pouch_db.query('doc/number_doc',
|
319 | {
|
320 | limit: 1,
|
321 | include_docs: false,
|
322 | startkey: [_manager.class_name, year, prefix + '\ufff0'],
|
323 | endkey: [_manager.class_name, year, prefix],
|
324 | descending: true,
|
325 | })
|
326 | .then((res) => {
|
327 | if(res.rows.length) {
|
328 | const num0 = res.rows[0].key[2];
|
329 | for (let i = num0.length - 1; i >= prefix.length; i--) {
|
330 | if(isNaN(parseInt(num0[i]))) {
|
331 | break;
|
332 | }
|
333 | part = num0[i] + part;
|
334 | }
|
335 | part = (parseInt(part || 0) + 1).toFixed(0);
|
336 | }
|
337 | else {
|
338 | part = '1';
|
339 | }
|
340 | while (part.length < code_length) {
|
341 | part = '0' + part;
|
342 | }
|
343 | if (this instanceof DocObj || this instanceof TaskObj || this instanceof BusinessProcessObj){
|
344 | this.number_doc = prefix + part;
|
345 | }
|
346 | else{
|
347 | this.id = prefix + part;
|
348 | }
|
349 | return this;
|
350 | });
|
351 | };
|
352 | DataObj.prototype.new_cat_id = function new_cat_id(prefix) {
|
353 | const {organization, _manager} = this;
|
354 | const {current_user, wsql} = _manager._owner.$p;
|
355 | if (!prefix)
|
356 | prefix = ((current_user && current_user.prefix) || '') +
|
357 | (organization && organization.prefix ? organization.prefix : (wsql.get_user_param('zone') + '-'));
|
358 | let code_length = this._metadata().code_length - prefix.length,
|
359 | field = (this instanceof DocObj || this instanceof TaskObj || this instanceof BusinessProcessObj) ? 'number_doc' : 'id',
|
360 | part = '',
|
361 | res = wsql.alasql('select top 1 ' + field + ' as id from ? where ' + field + ' like "' + prefix + '%" order by ' + field + ' desc', [_manager.alatable]);
|
362 | if (res.length) {
|
363 | const num0 = res[0].id || '';
|
364 | for (let i = num0.length - 1; i > 0; i--) {
|
365 | if (isNaN(parseInt(num0[i])))
|
366 | break;
|
367 | part = num0[i] + part;
|
368 | }
|
369 | part = (parseInt(part || 0) + 1).toFixed(0);
|
370 | } else {
|
371 | part = '1';
|
372 | }
|
373 | while (part.length < code_length){
|
374 | part = '0' + part;
|
375 | }
|
376 | this[field] = prefix + part;
|
377 | return this;
|
378 | };
|
379 | Object.defineProperties(DataManager.prototype, {
|
380 | pouch_db: {
|
381 | get () {
|
382 | const cachable = this.cachable.replace('_ram', '').replace('_doc', '');
|
383 | const {adapter} = this;
|
384 | if(cachable.indexOf('remote') != -1) {
|
385 | return adapter.remote[cachable.replace('_remote', '')];
|
386 | }
|
387 | else {
|
388 | return adapter.local[cachable] || adapter.remote[cachable];
|
389 | }
|
390 | }
|
391 | },
|
392 | });
|
393 | };
|
394 |
|
395 | let PouchDB;
|
396 | if(typeof process !== 'undefined' && process.versions && process.versions.node) {
|
397 | PouchDB = require('pouchdb-core')
|
398 | .plugin(require('pouchdb-adapter-http'))
|
399 | .plugin(require('pouchdb-replication'))
|
400 | .plugin(require('pouchdb-mapreduce'))
|
401 | .plugin(require('pouchdb-find'))
|
402 | .plugin(require('pouchdb-adapter-memory'));
|
403 | }
|
404 | else if(typeof window !== 'undefined' && window.PouchDB) {
|
405 | PouchDB = window.PouchDB;
|
406 | }
|
407 | else {
|
408 | PouchDB = require('pouchdb-core').default
|
409 | .plugin(require('pouchdb-adapter-http').default)
|
410 | .plugin(require('pouchdb-replication').default)
|
411 | .plugin(require('pouchdb-mapreduce').default)
|
412 | .plugin(require('pouchdb-find').default)
|
413 | .plugin(require('pouchdb-adapter-idb').default);
|
414 | if(typeof window !== 'undefined') {
|
415 | window.PouchDB = PouchDB;
|
416 | }
|
417 | }
|
418 | var PouchDB$1 = PouchDB;
|
419 |
|
420 | function adapter({AbstracrAdapter}) {
|
421 | const fieldsToDelete = '_id,search,timestamp'.split(',');
|
422 | return class AdapterPouch extends AbstracrAdapter {
|
423 | constructor($p) {
|
424 | super($p);
|
425 | this.props = {
|
426 | _data_loaded: false,
|
427 | _doc_ram_loading: false,
|
428 | _doc_ram_loaded: false,
|
429 | _auth: null,
|
430 | _suffix: '',
|
431 | _user: '',
|
432 | _push_only: false,
|
433 | branch: null,
|
434 | };
|
435 | this.local = {_loading: false, sync: {}};
|
436 | this.remote = {};
|
437 | this.fetch = this.fetch.bind(this);
|
438 | }
|
439 | init(wsql, job_prm) {
|
440 | const {props, local, remote, fetch, $p: {md}} = this;
|
441 | Object.assign(props, {
|
442 | path: wsql.get_user_param('couch_path', 'string') || job_prm.couch_path || '',
|
443 | zone: wsql.get_user_param('zone', 'number'),
|
444 | prefix: job_prm.local_storage_prefix,
|
445 | direct: wsql.get_user_param('zone', 'number') == job_prm.zone_demo ? false :
|
446 | (job_prm.hasOwnProperty('couch_direct') ? job_prm.couch_direct : wsql.get_user_param('couch_direct', 'boolean')),
|
447 | user_node: job_prm.user_node,
|
448 | noreplicate: job_prm.noreplicate,
|
449 | autologin: job_prm.autologin || [],
|
450 | });
|
451 | if(props.path && props.path.indexOf('http') != 0 && typeof location != 'undefined') {
|
452 | props.path = `${location.protocol}//${location.host}${props.path}`;
|
453 | }
|
454 | if(job_prm.use_meta === false) {
|
455 | props.use_meta = false;
|
456 | }
|
457 | if(job_prm.use_ram === false) {
|
458 | props.use_ram = false;
|
459 | }
|
460 | if(props.user_node && props.user_node.suffix) {
|
461 | props._suffix = props.user_node.suffix;
|
462 | }
|
463 | const opts = {auto_compaction: true, revs_limit: 3, owner: this, fetch};
|
464 | const bases = md.bases();
|
465 | if(props.use_meta !== false) {
|
466 | local.meta = new PouchDB$1(props.prefix + 'meta', opts);
|
467 | if(props.path) {
|
468 | remote.meta = new PouchDB$1(props.path + 'meta', {skip_setup: true, owner: this, fetch});
|
469 | setTimeout(() => this.run_sync('meta'));
|
470 | }
|
471 | }
|
472 | const pbases = ['doc', 'user'];
|
473 | if(props.use_ram !== false) {
|
474 | pbases.push('ram');
|
475 | }
|
476 | for (const name of pbases) {
|
477 | if(bases.indexOf(name) != -1) {
|
478 | Object.defineProperty(local, name, {
|
479 | get() {
|
480 | const dynamic_doc = wsql.get_user_param('dynamic_doc');
|
481 | if(dynamic_doc && name === 'doc' && props.direct) {
|
482 | return remote[dynamic_doc];
|
483 | }
|
484 | else {
|
485 | return local[`__${name}`] || remote[name];
|
486 | }
|
487 | }
|
488 | });
|
489 | if(job_prm.couch_memory && job_prm.couch_memory.includes(name)) {
|
490 | local[`__${name}`] = new PouchDB$1(props.prefix + props.zone + '_' + name, Object.assign({adapter: 'memory'}, opts));
|
491 | }
|
492 | else if(props.user_node || (props.direct && name != 'ram' && name != 'user')) {
|
493 | local[`__${name}`] = null;
|
494 | }
|
495 | else {
|
496 | local[`__${name}`] = new PouchDB$1(props.prefix + props.zone + '_' + name, opts);
|
497 | }
|
498 | }
|
499 | }
|
500 | this.after_init( props.user_node ? bases : (props.autologin.length ? props.autologin : ['ram']));
|
501 | }
|
502 | after_init(bases, auth) {
|
503 | const {props, remote, fetch, $p: {md, wsql}} = this;
|
504 | const opts = {skip_setup: true, adapter: 'http', owner: this, fetch};
|
505 | if(auth) {
|
506 | opts.auth = auth;
|
507 | }
|
508 | else if(props.user_node) {
|
509 | opts.auth = props.user_node;
|
510 | }
|
511 | (bases || md.bases()).forEach((name) => {
|
512 | if((!auth && remote[name]) ||
|
513 | name.match(/(e1cib|github|user)/) ||
|
514 | (name === 'ram' && props.use_ram === false) ||
|
515 | (name === 'pgsql' && wsql.alasql.utils.isNode)) {
|
516 | return;
|
517 | }
|
518 | remote[name] = new PouchDB$1(this.dbpath(name), opts);
|
519 | });
|
520 | }
|
521 | after_log_in() {
|
522 | const {props, local, remote, $p: {md, wsql}} = this;
|
523 | const run_sync = [];
|
524 | md.bases().forEach((dbid) => {
|
525 | if(dbid !== 'meta' &&
|
526 | local[dbid] && remote[dbid] && local[dbid] != remote[dbid] &&
|
527 | (dbid !== 'doc' || !wsql.get_user_param('dynamic_doc'))
|
528 | ) {
|
529 | if(props.noreplicate && props.noreplicate.includes(dbid)) {
|
530 | return;
|
531 | }
|
532 | run_sync.push(this.run_sync(dbid));
|
533 | }
|
534 | });
|
535 | return Promise.all(run_sync)
|
536 | .then(() => {
|
537 | if(props.use_ram === false) ;
|
538 | else if(local._loading) {
|
539 | return new Promise((resolve) => {
|
540 | this.once('pouch_data_loaded', resolve);
|
541 | });
|
542 | }
|
543 | else if(!props.user_node) {
|
544 | return this.call_data_loaded();
|
545 | }
|
546 | });
|
547 | }
|
548 | log_in(username, password) {
|
549 | const {props, remote, $p} = this;
|
550 | const {job_prm, wsql, aes, md} = $p;
|
551 | if(username == undefined && password == undefined) {
|
552 | if(job_prm.guests && job_prm.guests.length) {
|
553 | username = job_prm.guests[0].username;
|
554 | password = aes.Ctr.decrypt(job_prm.guests[0].password);
|
555 | }
|
556 | else {
|
557 | const err = new Error('empty login or password');
|
558 | this.emit('user_log_fault', err);
|
559 | return Promise.reject(err);
|
560 | }
|
561 | }
|
562 | else if(!username || !password){
|
563 | const err = new Error('empty login or password');
|
564 | this.emit('user_log_fault', err);
|
565 | return Promise.reject(err);
|
566 | }
|
567 | if(props._auth) {
|
568 | if(props._auth.username == username) {
|
569 | return Promise.resolve();
|
570 | }
|
571 | else {
|
572 | const err = new Error('need logout first');
|
573 | this.emit('user_log_fault', err);
|
574 | return Promise.reject(err);
|
575 | }
|
576 | }
|
577 | const bases = md.bases();
|
578 | let try_auth = (props.user_node || !remote.ram) ?
|
579 | Promise.resolve(true) :
|
580 | remote.ram.login(username, password)
|
581 | .then((user) => {
|
582 | if(user.ref && typeof user.roles === 'string') {
|
583 | if(user.zones && user.zones.length && !user.zones.includes(props.zone)) {
|
584 | props.zone = user.zones[0];
|
585 | wsql.set_user_param('zone', props.zone);
|
586 | }
|
587 | this.emit('authenticated', user);
|
588 | props._suffix = user.suffix || '';
|
589 | props._user = user.ref;
|
590 | props._push_only = Boolean(user.push_only);
|
591 | if(user.direct && !props.direct && props.zone != job_prm.zone_demo) {
|
592 | props.direct = true;
|
593 | wsql.set_user_param('couch_direct', true);
|
594 | }
|
595 | if(user.su) {
|
596 | username = user.su;
|
597 | }
|
598 | }
|
599 | else {
|
600 | const {roles} = user;
|
601 | const suffix = /^suffix:/;
|
602 | const ref = /^ref:/;
|
603 | roles.forEach((role) => {
|
604 | if(suffix.test(role)) {
|
605 | props._suffix = role.substr(7);
|
606 | }
|
607 | else if(ref.test(role)) {
|
608 | props._user = role.substr(4);
|
609 | }
|
610 | else if(role === 'direct' && !props.direct && props.zone != job_prm.zone_demo) {
|
611 | props.direct = true;
|
612 | wsql.set_user_param('couch_direct', true);
|
613 | }
|
614 | else if(role === 'push_only' && !props._push_only) {
|
615 | props._push_only = true;
|
616 | }
|
617 | });
|
618 | }
|
619 | if(props._push_only && props.direct) {
|
620 | props.direct = false;
|
621 | wsql.set_user_param('couch_direct', false);
|
622 | }
|
623 | if(props._suffix) {
|
624 | while (props._suffix.length < 4) {
|
625 | props._suffix = '0' + props._suffix;
|
626 | }
|
627 | }
|
628 | return true;
|
629 | })
|
630 | .catch((err) => {
|
631 | if(props.direct) {
|
632 | throw err;
|
633 | }
|
634 | return new Promise((resolve, reject) => {
|
635 | let count = 0;
|
636 | function props_by_user() {
|
637 | setTimeout(() => {
|
638 | const {current_user} = $p;
|
639 | if(current_user) {
|
640 | if(current_user.push_only) {
|
641 | props._push_only = true;
|
642 | }
|
643 | if(current_user.suffix) {
|
644 | props._suffix = current_user.suffix;
|
645 | while (props._suffix.length < 4) {
|
646 | props._suffix = '0' + props._suffix;
|
647 | }
|
648 | }
|
649 | resolve();
|
650 | }
|
651 | else {
|
652 | if(count > 4) {
|
653 | return reject();
|
654 | }
|
655 | count++;
|
656 | props_by_user();
|
657 | }
|
658 | }, 100 + count * 500);
|
659 | }
|
660 | props_by_user();
|
661 | });
|
662 | });
|
663 | if(!props.user_node) {
|
664 | try_auth = try_auth
|
665 | .then((ram_logged_in) => {
|
666 | ram_logged_in && this.after_init(bases, {username, password});
|
667 | return ram_logged_in;
|
668 | })
|
669 | .then((ram_logged_in) => {
|
670 | let postlogin = Promise.resolve(ram_logged_in);
|
671 | if(!props.user_node) {
|
672 | bases.forEach((dbid) => {
|
673 | if(dbid !== 'meta' && dbid !== 'ram' && remote[dbid]) {
|
674 | postlogin = postlogin
|
675 | .then((ram_logged_in) => ram_logged_in && remote[dbid].info());
|
676 | }
|
677 | });
|
678 | }
|
679 | return postlogin;
|
680 | });
|
681 | }
|
682 | return try_auth.then((info) => {
|
683 | props._auth = {username};
|
684 | if(wsql.get_user_param('user_name') != username) {
|
685 | wsql.set_user_param('user_name', username);
|
686 | }
|
687 | if(info) {
|
688 | if(wsql.get_user_param('enable_save_pwd')) {
|
689 | if(aes.Ctr.decrypt(wsql.get_user_param('user_pwd')) != password) {
|
690 | wsql.set_user_param('user_pwd', aes.Ctr.encrypt(password));
|
691 | }
|
692 | }
|
693 | else if(wsql.get_user_param('user_pwd') != '') {
|
694 | wsql.set_user_param('user_pwd', '');
|
695 | }
|
696 | this.emit('user_log_in', username);
|
697 | }
|
698 | else {
|
699 | this.emit('user_log_stop', username);
|
700 | }
|
701 | return this.emit_promise('on_log_in').then(() => info);
|
702 | })
|
703 | .then((info) => {
|
704 | if(props._data_loaded && !props._doc_ram_loading) {
|
705 | if(props._doc_ram_loaded) {
|
706 | this.emit('pouch_doc_ram_loaded');
|
707 | }
|
708 | else {
|
709 | this.load_doc_ram();
|
710 | }
|
711 | }
|
712 | return info && this.after_log_in();
|
713 | })
|
714 | .catch(err => {
|
715 | this.emit('user_log_fault', err);
|
716 | });
|
717 | }
|
718 | log_out() {
|
719 | const {props, local, remote, fetch, authorized, $p: {md}} = this;
|
720 | if(authorized) {
|
721 | for (const name in local.sync) {
|
722 | if(name != 'meta' && props.autologin.indexOf(name) === -1) {
|
723 | try {
|
724 | local.sync[name].removeAllListeners();
|
725 | local.sync[name].cancel();
|
726 | local.sync[name] = null;
|
727 | }
|
728 | catch (err) {
|
729 | }
|
730 | }
|
731 | }
|
732 | props._auth = null;
|
733 | }
|
734 | return Promise.all(md.bases().map((name) => {
|
735 | if(name != 'meta' && remote[name]) {
|
736 | let res = remote[name].logout && remote[name].logout();
|
737 | if(name != 'ram') {
|
738 | const dbpath = AdapterPouch.prototype.dbpath.call(this, name);
|
739 | if(remote[name].name !== dbpath) {
|
740 | const sub = remote[name].close()
|
741 | .then(() => {
|
742 | remote[name].removeAllListeners();
|
743 | if(props.autologin.indexOf(name) === -1) {
|
744 | remote[name] = null;
|
745 | }
|
746 | else {
|
747 | remote[name] = new PouchDB$1(dbpath, {skip_setup: true, adapter: 'http', owner: this, fetch});
|
748 | }
|
749 | });
|
750 | res = res ? res.then(() => sub) : sub;
|
751 | }
|
752 | }
|
753 | return res;
|
754 | }
|
755 | }))
|
756 | .then(() => {
|
757 | props._user = '';
|
758 | this.emit('user_log_out');
|
759 | });
|
760 | }
|
761 | auth_prefix() {
|
762 | switch (this.props._auth_provider) {
|
763 | case 'google':
|
764 | return 'Google ';
|
765 | case 'ldap':
|
766 | return 'LDAP ';
|
767 | case 'github':
|
768 | return 'Github ';
|
769 | case 'vkontakte':
|
770 | return 'Vkontakte ';
|
771 | case 'facebook':
|
772 | return 'Facebook ';
|
773 | default:
|
774 | return 'Basic ';
|
775 | }
|
776 | }
|
777 | load_data(db) {
|
778 | const {local, $p: {job_prm}} = this;
|
779 | const options = {
|
780 | limit: 700,
|
781 | include_docs: true,
|
782 | };
|
783 | const _page = {
|
784 | total_rows: 0,
|
785 | limit: options.limit,
|
786 | page: 0,
|
787 | start: Date.now(),
|
788 | };
|
789 | if(job_prm.second_instance) {
|
790 | return Promise.reject(new Error('second_instance'));
|
791 | }
|
792 | if(!db) {
|
793 | db = local.ram;
|
794 | }
|
795 | return new Promise((resolve, reject) => {
|
796 | let index;
|
797 | const processPage = (err, response) => {
|
798 | if(response) {
|
799 | _page.page++;
|
800 | _page.total_rows = response.total_rows;
|
801 | this.emit('pouch_data_page', Object.assign({}, _page));
|
802 | if(this.load_changes(response, options)) {
|
803 | fetchNextPage();
|
804 | }
|
805 | else {
|
806 | local._loading = false;
|
807 | this.call_data_loaded(_page);
|
808 | resolve();
|
809 | }
|
810 | }
|
811 | else if(err) {
|
812 | reject(err);
|
813 | this.emit('pouch_data_error', 'ram', err);
|
814 | }
|
815 | };
|
816 | const fetchNextPage = () => {
|
817 | if(index){
|
818 | db.query('server/load_order', options, processPage);
|
819 | }
|
820 | else {
|
821 | db.allDocs(options, processPage);
|
822 | }
|
823 | };
|
824 | db.get('_design/server')
|
825 | .catch((err) => {
|
826 | if(err.status === 404) {
|
827 | return {views: {}}
|
828 | }
|
829 | else {
|
830 | reject(err);
|
831 | }
|
832 | })
|
833 | .then((design) => {
|
834 | if(design) {
|
835 | const {views} = design;
|
836 | if(views.load_order){
|
837 | index = true;
|
838 | }
|
839 | return (Object.keys(views).length ? this.rebuild_indexes('ram') : Promise.resolve())
|
840 | .then(() => db.info());
|
841 | }
|
842 | })
|
843 | .then((info) => {
|
844 | if(info) {
|
845 | if(info.doc_count >= (job_prm.pouch_ram_doc_count || 10)) {
|
846 | this.emit('pouch_load_start', Object.assign(_page, {local_rows: info.doc_count}));
|
847 | local._loading = true;
|
848 | fetchNextPage();
|
849 | }
|
850 | else {
|
851 | this.emit('pouch_no_data', info);
|
852 | resolve();
|
853 | }
|
854 | }
|
855 | });
|
856 | });
|
857 | }
|
858 | dbpath(name) {
|
859 | const {props: {path, zone, _suffix}, $p: {wsql, job_prm}} = this;
|
860 | if(name == 'meta') {
|
861 | return path + 'meta';
|
862 | }
|
863 | else if(name == 'ram') {
|
864 | return path + zone + '_ram';
|
865 | }
|
866 | else if(name === 'pgsql') {
|
867 | return (job_prm.pg_path.startsWith('/') && !wsql.alasql.utils.isNode ? location.origin + job_prm.pg_path : job_prm.pg_path) + zone;
|
868 | }
|
869 | else {
|
870 | return path + zone + '_' + name + (_suffix ? '_' + _suffix : '');
|
871 | }
|
872 | }
|
873 | db(_mgr) {
|
874 | const dbid = _mgr.cachable.replace('_remote', '').replace('_ram', '').replace('_doc', '');
|
875 | const {props, local, remote} = this;
|
876 | if(dbid.indexOf('remote') != -1 || dbid === 'pgsql' || (props.noreplicate && props.noreplicate.includes(dbid))) {
|
877 | return remote[dbid.replace('_remote', '')];
|
878 | }
|
879 | else {
|
880 | return local[dbid] || remote[dbid] || local.user;
|
881 | }
|
882 | }
|
883 | back_off (delay) {
|
884 | if (!delay) {
|
885 | return 1000 + Math.floor(Math.random() * 2000);
|
886 | }
|
887 | else if (delay >= 200000) {
|
888 | return 200000;
|
889 | }
|
890 | return delay * 3;
|
891 | }
|
892 | rerun_sync(id, options, sync_events) {
|
893 | const {local, remote} = this;
|
894 | const sync = local.sync[id];
|
895 | const rerun = () => {
|
896 | if(sync) {
|
897 | sync.cancel();
|
898 | sync.removeAllListeners();
|
899 | }
|
900 | setTimeout(() => {
|
901 | local.sync[id] = sync_events(local[id].replicate.from(remote[id], options));
|
902 | this.rerun_sync(id, options, sync_events);
|
903 | }, 300000 + Math.floor(Math.random() * 60000));
|
904 | };
|
905 | if(sync) {
|
906 | sync.listeners('complete').forEach((fn) => {
|
907 | if(fn.toString().includes('(info)')) {
|
908 | sync.removeListener('complete', fn);
|
909 | }
|
910 | });
|
911 | sync.on('complete', rerun);
|
912 | sync.on('error', rerun);
|
913 | }
|
914 | else {
|
915 | rerun();
|
916 | }
|
917 | }
|
918 | run_sync(id) {
|
919 | const {local, remote, $p: {wsql, job_prm, record_log}, props} = this;
|
920 | if(local.sync[id]) {
|
921 | return Promise.resolve(id);
|
922 | }
|
923 | const {_push_only, _user} = props;
|
924 | const db_local = local[id];
|
925 | const db_remote = remote[id];
|
926 | let linfo, _page;
|
927 | return db_local.info()
|
928 | .then((info) => {
|
929 | linfo = info;
|
930 | return db_remote.info();
|
931 | })
|
932 | .then((rinfo) => {
|
933 | if(id == 'ram') {
|
934 | return db_remote.get('data_version')
|
935 | .then((v) => {
|
936 | if(v.version != wsql.get_user_param('couch_ram_data_version')) {
|
937 | if(wsql.get_user_param('couch_ram_data_version')) {
|
938 | rinfo = this.reset_local_data();
|
939 | }
|
940 | wsql.set_user_param('couch_ram_data_version', v.version);
|
941 | }
|
942 | return rinfo;
|
943 | })
|
944 | .catch(record_log)
|
945 | .then(() => rinfo);
|
946 | }
|
947 | return rinfo;
|
948 | })
|
949 | .then((rinfo) => {
|
950 | if(!rinfo) {
|
951 | return;
|
952 | }
|
953 | if(!_push_only && rinfo.data_size > (job_prm.data_size_sync_limit || 2e8)) {
|
954 | this.emit('pouch_sync_error', id, {data_size: rinfo.data_size});
|
955 | props.direct = true;
|
956 | wsql.set_user_param('couch_direct', true);
|
957 | return;
|
958 | }
|
959 | if(id == 'ram' && linfo.doc_count < (job_prm.pouch_ram_doc_count || 10)) {
|
960 | _page = {
|
961 | total_rows: rinfo.doc_count,
|
962 | local_rows: linfo.doc_count,
|
963 | docs_written: 0,
|
964 | limit: 300,
|
965 | page: 0,
|
966 | start: Date.now(),
|
967 | };
|
968 | this.emit('pouch_load_start', _page);
|
969 | }
|
970 | return new Promise((resolve) => {
|
971 | const options = {
|
972 | batch_size: 200,
|
973 | batches_limit: 3,
|
974 | heartbeat: 20000,
|
975 | retry: true,
|
976 | };
|
977 | if(job_prm.pouch_filter && job_prm.pouch_filter[id]) {
|
978 | options.filter = job_prm.pouch_filter[id];
|
979 | }
|
980 | else if(id == 'meta') {
|
981 | options.filter = 'auth/meta';
|
982 | }
|
983 | const final_sync = (options) => {
|
984 | options.back_off_function = this.back_off;
|
985 | if(id == 'ram' || id == 'meta' || props.zone == job_prm.zone_demo) {
|
986 | options.live = !job_prm.crazy_ram;
|
987 | if(options.live) {
|
988 | local.sync[id] = sync_events(db_local.replicate.from(db_remote, options));
|
989 | }
|
990 | else {
|
991 | this.rerun_sync(id, options, sync_events);
|
992 | }
|
993 | }
|
994 | else if(_push_only) {
|
995 | options.live = true;
|
996 | if(options.filter) {
|
997 | delete options.filter;
|
998 | delete options.query_params;
|
999 | }
|
1000 | local.sync[id] = sync_events(db_local.replicate.to(db_remote, Object.assign({}, options, {batch_size: 50})));
|
1001 | }
|
1002 | else {
|
1003 | options.live = true;
|
1004 | local.sync[id] = sync_events(db_local.sync(db_remote, options));
|
1005 | }
|
1006 | };
|
1007 | const sync_events = (sync, options) => {
|
1008 | sync.on('change', (change) => {
|
1009 | if(change.pending > 10) {
|
1010 | change.db = id;
|
1011 | this.emit_async('repl_state', change);
|
1012 | }
|
1013 | change.update_only = id !== 'ram';
|
1014 | this.load_changes(change);
|
1015 | this.emit('pouch_sync_data', id, change);
|
1016 | })
|
1017 | .on('denied', (info) => {
|
1018 | this.emit('pouch_sync_denied', id, info);
|
1019 | })
|
1020 | .on('error', (err) => {
|
1021 | this.emit('pouch_sync_error', id, err);
|
1022 | })
|
1023 | .on('complete', (info) => {
|
1024 | sync.cancel();
|
1025 | sync.removeAllListeners();
|
1026 | if(options) {
|
1027 | info.db = id;
|
1028 | this.emit_async('repl_state', info);
|
1029 | final_sync(options);
|
1030 | this.rebuild_indexes(id)
|
1031 | .then(() => resolve(id));
|
1032 | }
|
1033 | });
|
1034 | if(id == 'ram') {
|
1035 | sync
|
1036 | .on('paused', (info) => this.emit('pouch_sync_paused', id, info))
|
1037 | .on('active', (info) => this.emit('pouch_sync_resumed', id, info));
|
1038 | }
|
1039 | return sync;
|
1040 | };
|
1041 | if(_push_only && !options.filter && id !== 'ram' && id !== 'meta') {
|
1042 | options.filter = 'auth/push_only';
|
1043 | options.query_params = {user: _user};
|
1044 | }
|
1045 | (job_prm.templates ? this.from_files(db_local, db_remote, options) : this.from_dump(db_local, db_remote, options))
|
1046 | .then((synced) => {
|
1047 | if(synced) {
|
1048 | final_sync(options);
|
1049 | if(typeof synced === 'number') {
|
1050 | this.rebuild_indexes(id)
|
1051 | .then(() => this.load_data());
|
1052 | }
|
1053 | }
|
1054 | else {
|
1055 | sync_events(db_local.replicate.from(db_remote, options), options);
|
1056 | }
|
1057 | });
|
1058 | });
|
1059 | });
|
1060 | }
|
1061 | from_dump(local, remote, opts = {}) {
|
1062 | const {utils, job_prm} = this.$p;
|
1063 | if(job_prm.crazy_ram && local === this.local.ram) {
|
1064 | return Promise.resolve(false);
|
1065 | }
|
1066 | return local.get('_local/dumped')
|
1067 | .then(() => true)
|
1068 | .catch(() => remote.get('_local/dump'))
|
1069 | .then(doc => {
|
1070 | if(doc === true) {
|
1071 | return doc;
|
1072 | }
|
1073 | const byteCharacters = atob(doc.dump);
|
1074 | const byteNumbers = new Array(byteCharacters.length);
|
1075 | for (let i = 0; i < byteCharacters.length; i++) {
|
1076 | byteNumbers[i] = byteCharacters.charCodeAt(i);
|
1077 | }
|
1078 | const blob = new Blob([new Uint8Array(byteNumbers)], {type: 'application/zip'});
|
1079 | return utils.blob_as_text(blob, 'array');
|
1080 | })
|
1081 | .then((uarray) => {
|
1082 | if(uarray === true) {
|
1083 | return uarray;
|
1084 | }
|
1085 | return ('JSZip' in window ? Promise.resolve() : utils.load_script('https://cdn.jsdelivr.net/jszip/2/jszip.min.js', 'script'))
|
1086 | .then(() => {
|
1087 | const zip = new JSZip(uarray);
|
1088 | return zip.files.dump.asText();
|
1089 | });
|
1090 | })
|
1091 | .then((text) => {
|
1092 | if(text === true) {
|
1093 | return text;
|
1094 | }
|
1095 | const opt = {
|
1096 | proxy: remote.name,
|
1097 | checkpoints: 'target',
|
1098 | emit: (docs) => {
|
1099 | this.emit('pouch_dumped', {db: local, docs});
|
1100 | if(local.name.indexOf('ram') !== -1) {
|
1101 | this.emit('pouch_data_page', {
|
1102 | total_rows: docs.length,
|
1103 | local_rows: 3,
|
1104 | docs_written: 3,
|
1105 | limit: 300,
|
1106 | page: 0,
|
1107 | start: Date.now(),
|
1108 | });
|
1109 | }
|
1110 | }
|
1111 | };
|
1112 | if(remote.__opts.auth) {
|
1113 | opt.auth = remote.__opts.auth;
|
1114 | }
|
1115 | if(opts.filter) {
|
1116 | opt.filter = opts.filter;
|
1117 | }
|
1118 | if(opts.query_params) {
|
1119 | opt.query_params = opts.query_params;
|
1120 | }
|
1121 | if(opts.selector) {
|
1122 | opt.selector = opts.selector;
|
1123 | }
|
1124 | return (local.load ? Promise.resolve() : utils.load_script('/dist/pouchdb.load.js', 'script'))
|
1125 | .then(() => {
|
1126 | return local.load(text, opt);
|
1127 | })
|
1128 | .then(() => local.put({_id: '_local/dumped'}))
|
1129 | .then(() => -1);
|
1130 | })
|
1131 | .catch((err) => {
|
1132 | err.status !== 404 && console.log(err);
|
1133 | return false;
|
1134 | });
|
1135 | }
|
1136 | from_files(local, remote, opts = {}) {
|
1137 | const li = local.name.lastIndexOf('_');
|
1138 | const id = local.name.substr(li + 1);
|
1139 | const {job_prm} = this.$p;
|
1140 | if(job_prm.crazy_ram && local === this.local.ram) {
|
1141 | return Promise.resolve(false);
|
1142 | }
|
1143 | return fetch(`/${id}/00000.json`)
|
1144 | .then((res) => res.json())
|
1145 | .then((info) => {
|
1146 | return local.get('_local/stamp')
|
1147 | .then((doc) => {
|
1148 | if(doc.stamp === info.stamp) {
|
1149 | return true;
|
1150 | }
|
1151 | info._rev = doc._rev;
|
1152 | return info;
|
1153 | })
|
1154 | .catch(() => {
|
1155 | return info;
|
1156 | });
|
1157 | })
|
1158 | .then((info) => {
|
1159 | if(info === true) {
|
1160 | return info;
|
1161 | }
|
1162 | if(info) {
|
1163 | return (local.load ? Promise.resolve() : this.$p.utils.load_script('/dist/pouchdb.load.js', 'script'))
|
1164 | .then(() => info);
|
1165 | }
|
1166 | })
|
1167 | .then((info) => {
|
1168 | if(info === true) {
|
1169 | return info;
|
1170 | }
|
1171 | if(info) {
|
1172 | const {origin} = location;
|
1173 | let series = Promise.resolve();
|
1174 | const msg = {db: id, ok: true, docs_read: 0, pending: info.doc_count, start_time: new Date().toISOString()};
|
1175 | this.emit_async('repl_state', msg);
|
1176 | const opt = {
|
1177 | proxy: remote.name,
|
1178 | checkpoints: 'target',
|
1179 | emit: (docs) => {
|
1180 | this.emit('pouch_dumped', {db: local, docs});
|
1181 | }
|
1182 | };
|
1183 | if(remote.__opts.auth) {
|
1184 | opt.auth = remote.__opts.auth;
|
1185 | }
|
1186 | if(opts.filter) {
|
1187 | opt.filter = opts.filter;
|
1188 | }
|
1189 | if(opts.query_params) {
|
1190 | opt.query_params = opts.query_params;
|
1191 | }
|
1192 | if(opts.selector) {
|
1193 | opt.selector = opts.selector;
|
1194 | }
|
1195 | for(let i = 1; i <= info.files; i++) {
|
1196 | series = series.then(() => {
|
1197 | return local.load(`${origin}/${id}/${i.pad(5)}.json`, opt);
|
1198 | })
|
1199 | .then((step) => {
|
1200 | msg.docs_read = (info.doc_count * i / info.files).round();
|
1201 | msg.pending = info.doc_count - msg.docs_read;
|
1202 | this.emit_async('repl_state', msg);
|
1203 | });
|
1204 | }
|
1205 | return series
|
1206 | .then(() => {
|
1207 | info._id = '_local/stamp';
|
1208 | return local.put(info);
|
1209 | })
|
1210 | .then(() => -1);
|
1211 | }
|
1212 | })
|
1213 | .catch(() => {
|
1214 | return false;
|
1215 | });
|
1216 | }
|
1217 | rebuild_indexes(id, silent) {
|
1218 | const {local, remote} = this;
|
1219 | const msg = {db: id, ok: true, docs_read: 0, pending: 0, start_time: new Date().toISOString()};
|
1220 | let promises = Promise.resolve();
|
1221 | return local[id] === remote[id] ?
|
1222 | Promise.resolve() :
|
1223 | local[id].allDocs({
|
1224 | include_docs: true,
|
1225 | startkey: '_design/',
|
1226 | endkey : '_design/\u0fff',
|
1227 | limit: 1000,
|
1228 | })
|
1229 | .then(({rows}) => {
|
1230 | for(const {doc} of rows) {
|
1231 | if(doc._id.indexOf('/server') !== -1 && id !== 'ram') {
|
1232 | continue;
|
1233 | }
|
1234 | if(doc.views) {
|
1235 | for(const name in doc.views) {
|
1236 | const view = doc.views[name];
|
1237 | const index = doc._id.replace('_design/', '') + '/' + name;
|
1238 | if(doc.language === 'javascript') {
|
1239 | promises = promises.then(() => {
|
1240 | if(silent) {
|
1241 | this.emit('rebuild_indexes', {id, index, start: true});
|
1242 | }
|
1243 | else {
|
1244 | msg.index = index;
|
1245 | this.emit('repl_state', msg);
|
1246 | }
|
1247 | return local[id].query(index, {limit: 1}).catch(() => null);
|
1248 | });
|
1249 | }
|
1250 | else {
|
1251 | const selector = {
|
1252 | limit: 1,
|
1253 | fields: ['_id'],
|
1254 | selector: {},
|
1255 | use_index: index.split('/'),
|
1256 | };
|
1257 | for(const fld of view.options.def.fields) {
|
1258 | selector.selector[fld] = '';
|
1259 | }
|
1260 | promises = promises.then(() => {
|
1261 | if(silent) {
|
1262 | this.emit('rebuild_indexes', {id, index, start: true});
|
1263 | }
|
1264 | else {
|
1265 | msg.index = index;
|
1266 | this.emit('repl_state', msg);
|
1267 | }
|
1268 | return local[id].find(selector).catch(() => null);
|
1269 | });
|
1270 | }
|
1271 | }
|
1272 | }
|
1273 | }
|
1274 | return promises.then(() => {
|
1275 | msg.index = '';
|
1276 | msg.end_time = new Date().toISOString();
|
1277 | this.emit('repl_state', msg);
|
1278 | this.emit('rebuild_indexes', {id, start: false, finish: true});
|
1279 | });
|
1280 | });
|
1281 | }
|
1282 | call_data_loaded(page) {
|
1283 | const {local, props} = this;
|
1284 | if(!props._data_loaded) {
|
1285 | props._data_loaded = true;
|
1286 | if(!page) {
|
1287 | page = local.sync._page || {};
|
1288 | }
|
1289 | if(!local.sync._page) {
|
1290 | local.sync._page = page;
|
1291 | }
|
1292 | Promise.resolve().then(() => {
|
1293 | this.emit(page.note = 'pouch_data_loaded', page);
|
1294 | this.authorized && this.load_doc_ram();
|
1295 | });
|
1296 | }
|
1297 | else if(!props._doc_ram_loaded && !props._doc_ram_loading && this.authorized) {
|
1298 | this.load_doc_ram();
|
1299 | }
|
1300 | }
|
1301 | reset_local_data() {
|
1302 | const {local, remote} = this;
|
1303 | const do_reload = () => {
|
1304 | setTimeout(() => typeof location != 'undefined' && location.reload(true), 1000);
|
1305 | };
|
1306 | return this.log_out()
|
1307 | .then(() => {
|
1308 | return local.templates && local.templates.adapter === 'idb' && local.templates.destroy()
|
1309 | })
|
1310 | .then(() => {
|
1311 | return remote.ram != local.ram && local.ram.destroy()
|
1312 | })
|
1313 | .then(() => {
|
1314 | return remote.doc != local.doc && local.doc.destroy()
|
1315 | })
|
1316 | .then(do_reload)
|
1317 | .catch(do_reload);
|
1318 | }
|
1319 | load_obj(tObj, attr) {
|
1320 | const db = (attr && attr.db) || this.db(tObj._manager);
|
1321 | if(!db) {
|
1322 | return Promise.resolve(tObj);
|
1323 | }
|
1324 | return db.get(tObj._manager.class_name + '|' + tObj.ref)
|
1325 | .then((res) => {
|
1326 | for(const fld of fieldsToDelete) {
|
1327 | delete res[fld];
|
1328 | }
|
1329 | tObj._data._loading = true;
|
1330 | tObj._mixin(res);
|
1331 | tObj._obj._rev = res._rev;
|
1332 | })
|
1333 | .catch((err) => {
|
1334 | if(err.status != 404) {
|
1335 | throw err;
|
1336 | }
|
1337 | })
|
1338 | .then(() => {
|
1339 | return tObj;
|
1340 | });
|
1341 | }
|
1342 | save_obj(tObj, attr) {
|
1343 | const {_manager, _obj, _data, ref, class_name} = tObj;
|
1344 | const db = attr.db || this.db(_manager);
|
1345 | if(!_data || (_data._saving && !_data._modified) || !db) {
|
1346 | return Promise.resolve(tObj);
|
1347 | }
|
1348 | if(_data._saving && _data._modified) {
|
1349 | _data._saving++;
|
1350 | if(_data._saving > 10) {
|
1351 | return Promise.reject(new Error(`Циклическая перезапись`));
|
1352 | }
|
1353 | return new Promise((resolve) => {
|
1354 | setTimeout(() => resolve(this.save_obj(tObj, attr)), 200);
|
1355 | });
|
1356 | }
|
1357 | _data._saving = 1;
|
1358 | const tmp = Object.assign({_id: `${class_name}|${ref}`, class_name}, _obj);
|
1359 | const {utils, wsql} = this.$p;
|
1360 | if(utils.is_doc_obj(tObj) || _manager.build_search) {
|
1361 | if(_manager.build_search) {
|
1362 | _manager.build_search(tmp, tObj);
|
1363 | }
|
1364 | else {
|
1365 | tmp.search = ((_obj.number_doc || '') + (_obj.note ? ' ' + _obj.note : '')).toLowerCase();
|
1366 | }
|
1367 | }
|
1368 | tmp.timestamp = {
|
1369 | user: this.authorized || wsql.get_user_param('user_name'),
|
1370 | moment: utils.moment().format('YYYY-MM-DDTHH:mm:ss ZZ'),
|
1371 | };
|
1372 | if(attr.attachments) {
|
1373 | tmp._attachments = attr.attachments;
|
1374 | }
|
1375 | if(_manager.metadata().grouping === 'array') {
|
1376 | delete tmp._id;
|
1377 | delete tmp.class_name;
|
1378 | const index = Math.floor(utils.crc32(tmp.ref) / 268435455);
|
1379 | const _id = `${class_name}|${index.pad()}`;
|
1380 | return db.get(_id)
|
1381 | .catch((err) => {
|
1382 | if(err.status !== 404) throw err;
|
1383 | return {_id, class_name, rows: []};
|
1384 | })
|
1385 | .then((doc) => {
|
1386 | let finded;
|
1387 | for(const row of doc.rows) {
|
1388 | if(row.ref === tmp.ref) {
|
1389 | Object.assign(row, tmp);
|
1390 | finded = true;
|
1391 | break;
|
1392 | }
|
1393 | }
|
1394 | if(!finded) {
|
1395 | doc.rows.push(tmp);
|
1396 | }
|
1397 | return db.put(doc);
|
1398 | })
|
1399 | .then(() => {
|
1400 | tObj.is_new() && tObj._set_loaded(tObj.ref);
|
1401 | return tObj;
|
1402 | });
|
1403 | }
|
1404 | else {
|
1405 | delete tmp.ref;
|
1406 | return (tObj.is_new() ? Promise.resolve(true) : db.get(tmp._id))
|
1407 | .then((res) => {
|
1408 | if(typeof res === 'object') {
|
1409 | if(tmp._rev !== res._rev && _manager.metadata().check_rev !== false) {
|
1410 | const {timestamp} = res;
|
1411 | const err = new Error(`Объект ${timestamp && typeof timestamp.user === 'string' ?
|
1412 | `изменил ${timestamp.user}<br/>${timestamp.moment}` : 'изменён другим пользователем'}`);
|
1413 | err._rev = true;
|
1414 | throw err;
|
1415 | }
|
1416 | tmp._rev = res._rev;
|
1417 | for (let att in res._attachments) {
|
1418 | if(!tmp._attachments) {
|
1419 | tmp._attachments = {};
|
1420 | }
|
1421 | if(!tmp._attachments[att]) {
|
1422 | tmp._attachments[att] = res._attachments[att];
|
1423 | }
|
1424 | }
|
1425 | }
|
1426 | return res;
|
1427 | })
|
1428 | .catch((err) => {
|
1429 | if(err.status !== 404) throw err;
|
1430 | })
|
1431 | .then(() => db.put(tmp))
|
1432 | .then((res) => {
|
1433 | if(res) {
|
1434 | tObj.is_new() && tObj._set_loaded(tObj.ref);
|
1435 | if(tmp._attachments) {
|
1436 | if(!tObj._attachments) {
|
1437 | tObj._attachments = {};
|
1438 | }
|
1439 | for (let att in tmp._attachments) {
|
1440 | if(!tObj._attachments[att] || !tmp._attachments[att].stub) {
|
1441 | tObj._attachments[att] = tmp._attachments[att];
|
1442 | }
|
1443 | }
|
1444 | }
|
1445 | _obj._rev = res.rev;
|
1446 | return tObj;
|
1447 | }
|
1448 | });
|
1449 | }
|
1450 | }
|
1451 | get_tree(_mgr, attr) {
|
1452 | return this.find_rows(_mgr, {
|
1453 | is_folder: true,
|
1454 | _raw: true,
|
1455 | _top: attr.count || 300,
|
1456 | _skip: attr.start || 0
|
1457 | })
|
1458 | .then((rows) => {
|
1459 | rows.sort((a, b) => {
|
1460 | const {guid} = this.$p.utils.blank;
|
1461 | if(a.parent == guid && b.parent != guid) {
|
1462 | return -1;
|
1463 | }
|
1464 | if(b.parent == guid && a.parent != guid) {
|
1465 | return 1;
|
1466 | }
|
1467 | if(a.name < b.name) {
|
1468 | return -1;
|
1469 | }
|
1470 | if(a.name > b.name) {
|
1471 | return 1;
|
1472 | }
|
1473 | return 0;
|
1474 | });
|
1475 | return rows.map((row) => ({
|
1476 | ref: row.ref,
|
1477 | parent: row.parent,
|
1478 | presentation: row.name
|
1479 | }));
|
1480 | })
|
1481 | .then((ares) => this.$p.iface.data_to_tree.call(_mgr, ares, attr));
|
1482 | }
|
1483 | get_selection(_mgr, attr) {
|
1484 | const {classes} = this.$p;
|
1485 | const cmd = attr.metadata || _mgr.metadata();
|
1486 | const flds = ['ref', '_deleted'];
|
1487 | const selection = {
|
1488 | _raw: true,
|
1489 | _total_count: true,
|
1490 | _top: attr.count || 30,
|
1491 | _skip: attr.start || 0,
|
1492 | };
|
1493 | const ares = [];
|
1494 | if(cmd.form && cmd.form.selection) {
|
1495 | cmd.form.selection.fields.forEach((fld) => flds.push(fld));
|
1496 | }
|
1497 | else if(_mgr instanceof classes.DocManager) {
|
1498 | flds.push('posted');
|
1499 | flds.push('date');
|
1500 | flds.push('number_doc');
|
1501 | }
|
1502 | else if(_mgr instanceof classes.TaskManager) {
|
1503 | flds.push('name as presentation');
|
1504 | flds.push('date');
|
1505 | flds.push('number_doc');
|
1506 | flds.push('completed');
|
1507 | }
|
1508 | else if(_mgr instanceof classes.BusinessProcessManager) {
|
1509 | flds.push('date');
|
1510 | flds.push('number_doc');
|
1511 | flds.push('started');
|
1512 | flds.push('finished');
|
1513 | }
|
1514 | else {
|
1515 | if(cmd.hierarchical && cmd.group_hierarchy) {
|
1516 | flds.push('is_folder');
|
1517 | }
|
1518 | else {
|
1519 | flds.push('0 as is_folder');
|
1520 | }
|
1521 | if(cmd.main_presentation_name) {
|
1522 | flds.push('name as presentation');
|
1523 | }
|
1524 | else {
|
1525 | if(cmd.code_length) {
|
1526 | flds.push('id as presentation');
|
1527 | }
|
1528 | else {
|
1529 | flds.push('... as presentation');
|
1530 | }
|
1531 | }
|
1532 | if(cmd.has_owners) {
|
1533 | flds.push('owner');
|
1534 | }
|
1535 | if(cmd.code_length) {
|
1536 | flds.push('id');
|
1537 | }
|
1538 | }
|
1539 | if(_mgr.metadata('date') && (attr.date_from || attr.date_till)) {
|
1540 | if(!attr.date_from) {
|
1541 | attr.date_from = new Date('2017-01-01');
|
1542 | }
|
1543 | if(!attr.date_till) {
|
1544 | attr.date_till = $p.utils.date_add_day(new Date(), 1);
|
1545 | }
|
1546 | selection.date = {between: [attr.date_from, attr.date_till]};
|
1547 | }
|
1548 | if(cmd.hierarchical && attr.parent) {
|
1549 | selection.parent = attr.parent;
|
1550 | }
|
1551 | if(attr.selection) {
|
1552 | if(Array.isArray(attr.selection)) {
|
1553 | attr.selection.forEach((asel) => {
|
1554 | for (const fld in asel) {
|
1555 | if(fld[0] != '_' || fld == '_view' || fld == '_key') {
|
1556 | selection[fld] = asel[fld];
|
1557 | }
|
1558 | }
|
1559 | });
|
1560 | }
|
1561 | else {
|
1562 | for (const fld in attr.selection) {
|
1563 | if(fld[0] != '_' || fld == '_view' || fld == '_key') {
|
1564 | selection[fld] = attr.selection[fld];
|
1565 | }
|
1566 | }
|
1567 | }
|
1568 | }
|
1569 | if(selection._key && selection._key._drop_date && selection.date) {
|
1570 | delete selection.date;
|
1571 | }
|
1572 | if(attr.filter && (!selection._key || !selection._key._search)) {
|
1573 | if(cmd.input_by_string.length == 1) {
|
1574 | selection[cmd.input_by_string] = {like: attr.filter};
|
1575 | }
|
1576 | else {
|
1577 | selection.or = [];
|
1578 | cmd.input_by_string.forEach((ifld) => {
|
1579 | const flt = {};
|
1580 | flt[ifld] = {like: attr.filter};
|
1581 | selection.or.push(flt);
|
1582 | });
|
1583 | }
|
1584 | }
|
1585 | if(selection._key && selection._key._order_by) {
|
1586 | selection._key._order_by = attr.direction;
|
1587 | }
|
1588 | return this.find_rows(_mgr, selection)
|
1589 | .then((rows) => {
|
1590 | if(rows.hasOwnProperty('_total_count') && rows.hasOwnProperty('rows')) {
|
1591 | attr._total_count = rows._total_count;
|
1592 | rows = rows.rows;
|
1593 | }
|
1594 | rows.forEach((doc) => {
|
1595 | const o = {};
|
1596 | flds.forEach((fld) => {
|
1597 | let fldsyn;
|
1598 | if(fld == 'ref') {
|
1599 | o[fld] = doc[fld];
|
1600 | return;
|
1601 | }
|
1602 | else if(fld.indexOf(' as ') != -1) {
|
1603 | fldsyn = fld.split(' as ')[1];
|
1604 | fld = fld.split(' as ')[0].split('.');
|
1605 | fld = fld[fld.length - 1];
|
1606 | }
|
1607 | else {
|
1608 | fldsyn = fld;
|
1609 | }
|
1610 | const mf = _mgr.metadata(fld);
|
1611 | if(mf) {
|
1612 | if(mf.type.date_part) {
|
1613 | o[fldsyn] = $p.moment(doc[fld]).format($p.moment._masks[mf.type.date_part]);
|
1614 | }
|
1615 | else if(mf.type.is_ref) {
|
1616 | if(!doc[fld] || doc[fld] == $p.utils.blank.guid) {
|
1617 | o[fldsyn] = '';
|
1618 | }
|
1619 | else {
|
1620 | const mgr = _mgr.value_mgr(o, fld, mf.type, false, doc[fld]);
|
1621 | if(mgr) {
|
1622 | o[fldsyn] = mgr.get(doc[fld]).presentation;
|
1623 | }
|
1624 | else {
|
1625 | o[fldsyn] = '';
|
1626 | }
|
1627 | }
|
1628 | }
|
1629 | else if(typeof doc[fld] === 'number' && mf.type.fraction) {
|
1630 | o[fldsyn] = doc[fld].toFixed(mf.type.fraction);
|
1631 | }
|
1632 | else {
|
1633 | o[fldsyn] = doc[fld];
|
1634 | }
|
1635 | }
|
1636 | });
|
1637 | ares.push(o);
|
1638 | });
|
1639 | return $p.iface.data_to_grid.call(_mgr, ares, attr);
|
1640 | })
|
1641 | .catch($p.record_log);
|
1642 | }
|
1643 | load_array(_mgr, refs, with_attachments, db) {
|
1644 | if(!refs || !refs.length) {
|
1645 | return Promise.resolve(false);
|
1646 | }
|
1647 | if(!db && _mgr) {
|
1648 | db = this.db(_mgr);
|
1649 | }
|
1650 | const options = {
|
1651 | limit: refs.length + 1,
|
1652 | include_docs: true,
|
1653 | keys: _mgr ? refs.map((v) => _mgr.class_name + '|' + v) : refs,
|
1654 | };
|
1655 | if(with_attachments) {
|
1656 | options.attachments = true;
|
1657 | options.binary = true;
|
1658 | }
|
1659 | return db.allDocs(options).then((result) => this.load_changes(result, {}));
|
1660 | }
|
1661 | load_view(_mgr, _view, options) {
|
1662 | return new Promise((resolve, reject) => {
|
1663 | const db = this.db(_mgr);
|
1664 | if(!options) {
|
1665 | options = {
|
1666 | limit: 1000,
|
1667 | include_docs: true,
|
1668 | startkey: _mgr.class_name + '|',
|
1669 | endkey: _mgr.class_name + '|\ufff0',
|
1670 | };
|
1671 | }
|
1672 | function process_docs(err, result) {
|
1673 | if(result) {
|
1674 | if(result.rows.length) {
|
1675 | options.startkey = result.rows[result.rows.length - 1].key;
|
1676 | options.skip = 1;
|
1677 | _mgr.load_array(result.rows.map(({doc}) => {
|
1678 | doc.ref = doc._id.split('|')[1];
|
1679 | delete doc._id;
|
1680 | return doc;
|
1681 | }), true);
|
1682 | if(result.rows.length < options.limit) {
|
1683 | resolve();
|
1684 | }
|
1685 | else {
|
1686 | db.query(_view, options, process_docs);
|
1687 | }
|
1688 | }
|
1689 | else {
|
1690 | resolve();
|
1691 | }
|
1692 | }
|
1693 | else if(err && err.status !== 404) {
|
1694 | reject(err);
|
1695 | }
|
1696 | }
|
1697 | db.query(_view, options, process_docs);
|
1698 | });
|
1699 | }
|
1700 | load_doc_ram() {
|
1701 | const {local, props, $p: {md}} = this;
|
1702 | if(!local.doc){
|
1703 | return;
|
1704 | }
|
1705 | const res = [];
|
1706 | const {_m} = md;
|
1707 | this.emit('pouch_doc_ram_start');
|
1708 | props._doc_ram_loading = true;
|
1709 | ['cat', 'cch', 'ireg'].forEach((kind) => {
|
1710 | for (const name in _m[kind]) {
|
1711 | (_m[kind][name].cachable === 'doc_ram' || _m[kind][name].cachable === 'templates_ram') && res.push(kind + '.' + name);
|
1712 | }
|
1713 | });
|
1714 | return res.reduce((acc, name) => {
|
1715 | return acc.then(() => {
|
1716 | const opt = {
|
1717 | include_docs: true,
|
1718 | startkey: name + '|',
|
1719 | endkey: name + '|\ufff0',
|
1720 | limit: 10000,
|
1721 | };
|
1722 | const page = local.sync._page || {};
|
1723 | const meta = md.get(name);
|
1724 | this.emit('pouch_data_page', Object.assign(page, {synonym: meta.synonym}));
|
1725 | return local[meta.cachable.replace(/_ram$/, '')].allDocs(opt).then((res) => {
|
1726 | this.load_changes(res, opt);
|
1727 | });
|
1728 | });
|
1729 | }, Promise.resolve())
|
1730 | .catch((err) => {
|
1731 | props._doc_ram_loading = false;
|
1732 | this.emit('pouch_sync_error', 'doc', err);
|
1733 | return {docs: []};
|
1734 | })
|
1735 | .then(() => {
|
1736 | props._doc_ram_loading = false;
|
1737 | props._doc_ram_loaded = true;
|
1738 | this.emit('pouch_doc_ram_loaded');
|
1739 | });
|
1740 | }
|
1741 | find_rows(_mgr, selection, db) {
|
1742 | if(!db) {
|
1743 | db = this.db(_mgr);
|
1744 | }
|
1745 | if(!db) {
|
1746 | return Promise.resolve([]);
|
1747 | }
|
1748 | const err_handler = this.emit.bind(this, 'pouch_sync_error', _mgr.cachable);
|
1749 | if(selection && selection._mango) {
|
1750 | const sel = {};
|
1751 | for(const fld in selection) {
|
1752 | if(fld[0] !== '_') {
|
1753 | sel[fld] = selection[fld];
|
1754 | }
|
1755 | }
|
1756 | return db.find(sel)
|
1757 | .then(({docs}) => {
|
1758 | if(!docs) {
|
1759 | docs = [];
|
1760 | }
|
1761 | for (const doc of docs) {
|
1762 | doc.ref = doc._id.split('|')[1];
|
1763 | if(!selection._raw){
|
1764 | delete doc._id;
|
1765 | delete doc.class_name;
|
1766 | }
|
1767 | }
|
1768 | return selection._raw ? docs : _mgr.load_array(docs);
|
1769 | })
|
1770 | .catch((err) => {
|
1771 | err_handler(err);
|
1772 | return [];
|
1773 | });
|
1774 | }
|
1775 | const {utils} = this.$p;
|
1776 | const res = [];
|
1777 | const options = {
|
1778 | limit: 100,
|
1779 | include_docs: true,
|
1780 | startkey: _mgr.class_name + '|',
|
1781 | endkey: _mgr.class_name + '|\ufff0',
|
1782 | };
|
1783 | let doc, _raw, _view, _total_count, top, calc_count, top_count = 0, skip = 0, skip_count = 0;
|
1784 | if(selection) {
|
1785 | if(selection._top) {
|
1786 | top = selection._top;
|
1787 | delete selection._top;
|
1788 | }
|
1789 | else {
|
1790 | top = 300;
|
1791 | }
|
1792 | if(selection._raw) {
|
1793 | _raw = selection._raw;
|
1794 | delete selection._raw;
|
1795 | }
|
1796 | if(selection._total_count) {
|
1797 | _total_count = selection._total_count;
|
1798 | delete selection._total_count;
|
1799 | }
|
1800 | if(selection._view) {
|
1801 | _view = selection._view;
|
1802 | delete selection._view;
|
1803 | }
|
1804 | if(selection._key) {
|
1805 | if(selection._key._order_by == 'des') {
|
1806 | options.startkey = selection._key.endkey || selection._key + '\ufff0';
|
1807 | options.endkey = selection._key.startkey || selection._key;
|
1808 | options.descending = true;
|
1809 | }
|
1810 | else {
|
1811 | options.startkey = selection._key.startkey || selection._key;
|
1812 | options.endkey = selection._key.endkey || selection._key + '\ufff0';
|
1813 | }
|
1814 | }
|
1815 | if(typeof selection._skip == 'number') {
|
1816 | skip = selection._skip;
|
1817 | delete selection._skip;
|
1818 | }
|
1819 | if(selection._attachments) {
|
1820 | options.attachments = true;
|
1821 | options.binary = true;
|
1822 | delete selection._attachments;
|
1823 | }
|
1824 | }
|
1825 | if(_total_count) {
|
1826 | calc_count = true;
|
1827 | _total_count = 0;
|
1828 | if(Object.keys(selection).length <= 1) {
|
1829 | if(selection._key && selection._key.hasOwnProperty('_search')) {
|
1830 | options.include_docs = false;
|
1831 | options.limit = 100000;
|
1832 | return db.query(_view, options)
|
1833 | .then((result) => {
|
1834 | result.rows.forEach((row) => {
|
1835 | if(!selection._key._search || row.key[row.key.length - 1].toLowerCase().indexOf(selection._key._search) != -1) {
|
1836 | _total_count++;
|
1837 | if(skip) {
|
1838 | skip_count++;
|
1839 | if(skip_count < skip) {
|
1840 | return;
|
1841 | }
|
1842 | }
|
1843 | if(top) {
|
1844 | top_count++;
|
1845 | if(top_count > top) {
|
1846 | return;
|
1847 | }
|
1848 | }
|
1849 | res.push(row.id);
|
1850 | }
|
1851 | });
|
1852 | delete options.startkey;
|
1853 | delete options.endkey;
|
1854 | if(options.descending) {
|
1855 | delete options.descending;
|
1856 | }
|
1857 | options.keys = res;
|
1858 | options.include_docs = true;
|
1859 | return db.allDocs(options);
|
1860 | })
|
1861 | .catch((err) => {
|
1862 | err_handler(err);
|
1863 | return {rows: []};
|
1864 | })
|
1865 | .then((result) => {
|
1866 | return {
|
1867 | rows: result.rows.map(({doc}) => {
|
1868 | doc.ref = doc._id.split('|')[1];
|
1869 | if(!_raw) {
|
1870 | delete doc._id;
|
1871 | }
|
1872 | return doc;
|
1873 | }),
|
1874 | _total_count: _total_count,
|
1875 | };
|
1876 | });
|
1877 | }
|
1878 | }
|
1879 | }
|
1880 | return new Promise((resolve, reject) => {
|
1881 | function process_docs(result) {
|
1882 | if(result && result.rows.length) {
|
1883 | options.startkey = result.rows[result.rows.length - 1].key;
|
1884 | options.skip = 1;
|
1885 | result.rows.forEach((rev) => {
|
1886 | doc = rev.doc;
|
1887 | let key = doc._id.split('|');
|
1888 | doc.ref = key[1];
|
1889 | if(!_raw) {
|
1890 | delete doc._id;
|
1891 | }
|
1892 | if(!utils._selection.call(_mgr, doc, selection)) {
|
1893 | return;
|
1894 | }
|
1895 | if(calc_count) {
|
1896 | _total_count++;
|
1897 | }
|
1898 | if(skip) {
|
1899 | skip_count++;
|
1900 | if(skip_count < skip) {
|
1901 | return;
|
1902 | }
|
1903 | }
|
1904 | if(top) {
|
1905 | top_count++;
|
1906 | if(top_count > top) {
|
1907 | return;
|
1908 | }
|
1909 | }
|
1910 | res.push(doc);
|
1911 | });
|
1912 | if((result.rows.length < options.limit) || top && top_count > top && !calc_count) {
|
1913 | resolve(_raw ? res : _mgr.load_array(res));
|
1914 | }
|
1915 | else {
|
1916 | fetch_next_page();
|
1917 | }
|
1918 | }
|
1919 | else {
|
1920 | if(calc_count) {
|
1921 | resolve({
|
1922 | rows: _raw ? res : _mgr.load_array(res),
|
1923 | _total_count: _total_count,
|
1924 | });
|
1925 | }
|
1926 | else {
|
1927 | resolve(_raw ? res : _mgr.load_array(res));
|
1928 | }
|
1929 | }
|
1930 | }
|
1931 | function fetch_next_page() {
|
1932 | (_view ? db.query(_view, options) : db.allDocs(options))
|
1933 | .catch((err) => {
|
1934 | err_handler(err);
|
1935 | reject(err);
|
1936 | })
|
1937 | .then(process_docs);
|
1938 | }
|
1939 | fetch_next_page();
|
1940 | });
|
1941 | }
|
1942 | save_attachment(_mgr, ref, att_id, attachment, type) {
|
1943 | if(!type) {
|
1944 | type = {type: 'text/plain'};
|
1945 | }
|
1946 | if(!(attachment instanceof Blob) && type.indexOf('text') == -1) {
|
1947 | attachment = new Blob([attachment], {type: type});
|
1948 | }
|
1949 | let _rev,
|
1950 | db = this.db(_mgr);
|
1951 | ref = _mgr.class_name + '|' + this.$p.utils.fix_guid(ref);
|
1952 | return db.get(ref)
|
1953 | .then((res) => {
|
1954 | if(res) {
|
1955 | _rev = res._rev;
|
1956 | }
|
1957 | })
|
1958 | .catch((err) => {
|
1959 | if(err.status != 404) {
|
1960 | throw err;
|
1961 | }
|
1962 | })
|
1963 | .then(() => {
|
1964 | return db.putAttachment(ref, att_id, _rev, attachment, type);
|
1965 | });
|
1966 | }
|
1967 | get_attachment(_mgr, ref, att_id) {
|
1968 | return this.db(_mgr).getAttachment(_mgr.class_name + '|' + this.$p.utils.fix_guid(ref), att_id);
|
1969 | }
|
1970 | delete_attachment(_mgr, ref, att_id) {
|
1971 | let _rev,
|
1972 | db = this.db(_mgr);
|
1973 | ref = _mgr.class_name + '|' + this.$p.utils.fix_guid(ref);
|
1974 | return db.get(ref)
|
1975 | .then((res) => {
|
1976 | if(res) {
|
1977 | _rev = res._rev;
|
1978 | }
|
1979 | })
|
1980 | .catch((err) => {
|
1981 | if(err.status != 404) {
|
1982 | throw err;
|
1983 | }
|
1984 | })
|
1985 | .then(() => {
|
1986 | return db.removeAttachment(ref, att_id, _rev);
|
1987 | });
|
1988 | }
|
1989 | load_changes(changes, options) {
|
1990 | let docs, doc, res = {}, cn, key, {$p} = this;
|
1991 | if(!options) {
|
1992 | if(changes.direction) {
|
1993 | if(changes.direction != 'pull') {
|
1994 | return;
|
1995 | }
|
1996 | docs = changes.change.docs;
|
1997 | }
|
1998 | else {
|
1999 | docs = changes.docs;
|
2000 | }
|
2001 | }
|
2002 | else {
|
2003 | docs = changes.rows;
|
2004 | }
|
2005 | if(docs.length > 0) {
|
2006 | if(options) {
|
2007 | options.startkey = docs[docs.length - 1].key;
|
2008 | options.skip = 1;
|
2009 | }
|
2010 | docs.forEach((rev) => {
|
2011 | doc = options ? rev.doc : rev;
|
2012 | if(!doc) {
|
2013 | if((rev.value && rev.value.deleted)) {
|
2014 | doc = {
|
2015 | _id: rev.id,
|
2016 | _deleted: true,
|
2017 | };
|
2018 | }
|
2019 | else if(rev.error) {
|
2020 | return;
|
2021 | }
|
2022 | }
|
2023 | key = doc._id.split('|');
|
2024 | if(key[0] === 'system') {
|
2025 | return !options && this.emit('system', key[1], doc);
|
2026 | }
|
2027 | cn = key[0].split('.');
|
2028 | doc.ref = key[1];
|
2029 | delete doc._id;
|
2030 | if(!res[cn[0]]) {
|
2031 | res[cn[0]] = {};
|
2032 | }
|
2033 | if(!res[cn[0]][cn[1]]) {
|
2034 | res[cn[0]][cn[1]] = [];
|
2035 | }
|
2036 | res[cn[0]][cn[1]].push(doc);
|
2037 | });
|
2038 | for (let mgr in res) {
|
2039 | for (cn in res[mgr]) {
|
2040 | if($p[mgr] && $p[mgr][cn]) {
|
2041 | $p[mgr][cn].load_array(res[mgr][cn], changes.update_only ? 'update_only' : true);
|
2042 | }
|
2043 | }
|
2044 | }
|
2045 | return true;
|
2046 | }
|
2047 | return false;
|
2048 | }
|
2049 | attach_refresher(regex, timout = 500000) {
|
2050 | if(this.props._refresher) {
|
2051 | clearInterval(this.props._refresher);
|
2052 | }
|
2053 | setInterval(() => {
|
2054 | if(this.authorized && this.remote.ram && this.remote.ram.adapter == 'http') {
|
2055 | this.remote.ram.info()
|
2056 | .then(response => {
|
2057 | })
|
2058 | .catch(err => {
|
2059 | });
|
2060 | }
|
2061 | }, timout);
|
2062 | }
|
2063 | backup_database(do_zip) {
|
2064 | }
|
2065 | restore_database(do_zip) {
|
2066 | }
|
2067 | get authorized() {
|
2068 | const {_auth} = this.props;
|
2069 | return _auth && _auth.username;
|
2070 | }
|
2071 | fetch(url, opts) {
|
2072 | const {authorized, remote, props} = this;
|
2073 | if(!opts.headers.get('Authorization')) {
|
2074 | if(authorized) {
|
2075 | for(const name in remote) {
|
2076 | const db = remote[name];
|
2077 | const {auth} = db.__opts;
|
2078 | if(auth) {
|
2079 | const {Authorization} = db.getBasicAuthHeaders({prefix: this.auth_prefix(), ...auth});
|
2080 | opts.headers.set('Authorization', Authorization);
|
2081 | break;
|
2082 | }
|
2083 | }
|
2084 | }
|
2085 | }
|
2086 | if(props.branch) {
|
2087 | opts.headers.set('branch', props.branch.valueOf());
|
2088 | }
|
2089 | return PouchDB$1.fetch(url, opts);
|
2090 | }
|
2091 | };
|
2092 | }
|
2093 | var adapter$1 = (constructor) => {
|
2094 | const {classes} = constructor;
|
2095 | classes.PouchDB = PouchDB$1;
|
2096 | classes.AdapterPouch = adapter(classes);
|
2097 | };
|
2098 |
|
2099 | const plugin = {
|
2100 | proto(constructor) {
|
2101 | proto(constructor);
|
2102 | adapter$1(constructor);
|
2103 | },
|
2104 | constructor(){
|
2105 | const {AdapterPouch} = this.classes;
|
2106 | this.adapters.pouch = new AdapterPouch(this);
|
2107 | }
|
2108 | };
|
2109 |
|
2110 | module.exports = plugin;
|
2111 |
|