@logux/server 0.12.2 → 0.12.4
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/README.md +4 -6
- package/add-sync-map/index.d.ts +32 -32
- package/add-sync-map/index.js +4 -4
- package/base-server/hello.html +1 -1
- package/base-server/index.d.ts +401 -401
- package/base-server/index.js +332 -321
- package/bind-backend-proxy/index.js +77 -77
- package/bind-control-server/index.js +1 -1
- package/context/index.d.ts +36 -36
- package/context/index.js +1 -1
- package/create-http-server/index.js +4 -4
- package/create-reporter/index.js +107 -107
- package/filter-meta/index.d.ts +1 -1
- package/human-formatter/index.js +13 -13
- package/index.d.ts +24 -24
- package/index.js +16 -16
- package/options-loader/index.js +2 -2
- package/options-loader/index.test.js +25 -24
- package/package.json +13 -13
- package/request/index.d.ts +2 -1
- package/request/test.test.js +60 -34
- package/server/index.d.ts +27 -23
- package/server/index.js +56 -56
- package/server-client/index.d.ts +41 -41
- package/server-client/index.js +61 -61
- package/test-client/index.d.ts +50 -50
- package/test-client/index.js +28 -28
- package/test-server/index.d.ts +44 -43
- package/test-server/index.js +20 -20
- package/vite.config.ts +13 -0
package/base-server/index.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { ServerConnection, MemoryStore, Log, parseId } from '@logux/core'
|
|
2
1
|
import { LoguxNotFoundError } from '@logux/actions'
|
|
3
|
-
import {
|
|
4
|
-
import { WebSocketServer } from 'ws'
|
|
2
|
+
import { Log, MemoryStore, parseId, ServerConnection } from '@logux/core'
|
|
5
3
|
import { promises as fs } from 'fs'
|
|
4
|
+
import { createNanoEvents } from 'nanoevents'
|
|
5
|
+
import { nanoid } from 'nanoid'
|
|
6
|
+
import { dirname, join } from 'path'
|
|
6
7
|
import { fileURLToPath } from 'url'
|
|
7
|
-
import { join, dirname } from 'path'
|
|
8
8
|
import UrlPattern from 'url-pattern'
|
|
9
|
-
import {
|
|
9
|
+
import { WebSocketServer } from 'ws'
|
|
10
10
|
|
|
11
|
-
import { bindControlServer } from '../bind-control-server/index.js'
|
|
12
11
|
import { bindBackendProxy } from '../bind-backend-proxy/index.js'
|
|
12
|
+
import { bindControlServer } from '../bind-control-server/index.js'
|
|
13
|
+
import { Context } from '../context/index.js'
|
|
13
14
|
import { createHttpServer } from '../create-http-server/index.js'
|
|
14
15
|
import { ServerClient } from '../server-client/index.js'
|
|
15
|
-
import { Context } from '../context/index.js'
|
|
16
16
|
|
|
17
17
|
const SKIP_PROCESS = Symbol('skipProcess')
|
|
18
18
|
const RESEND_META = ['channels', 'users', 'clients', 'nodes']
|
|
@@ -94,6 +94,10 @@ function normalizeChannelCallbacks(pattern, callbacks) {
|
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
function subscriberFilterId(action) {
|
|
98
|
+
return JSON.stringify(action.filter || {})
|
|
99
|
+
}
|
|
100
|
+
|
|
97
101
|
export class BaseServer {
|
|
98
102
|
constructor(opts = {}) {
|
|
99
103
|
this.options = opts
|
|
@@ -137,15 +141,9 @@ export class BaseServer {
|
|
|
137
141
|
|
|
138
142
|
let log
|
|
139
143
|
if (this.options.time) {
|
|
140
|
-
log = this.options.time.nextLog({
|
|
144
|
+
log = this.options.time.nextLog({ nodeId: this.nodeId, store })
|
|
141
145
|
} else {
|
|
142
|
-
log = new Log({
|
|
143
|
-
if (typeof this.options.timeout === 'undefined') {
|
|
144
|
-
this.options.timeout = 20000
|
|
145
|
-
}
|
|
146
|
-
if (typeof this.options.ping === 'undefined') {
|
|
147
|
-
this.options.ping = 10000
|
|
148
|
-
}
|
|
146
|
+
log = new Log({ nodeId: this.nodeId, store })
|
|
149
147
|
}
|
|
150
148
|
|
|
151
149
|
this.logger = console
|
|
@@ -274,13 +272,13 @@ export class BaseServer {
|
|
|
274
272
|
})
|
|
275
273
|
this.on('error', (err, action, meta) => {
|
|
276
274
|
if (meta) {
|
|
277
|
-
this.emitter.emit('report', 'error', {
|
|
275
|
+
this.emitter.emit('report', 'error', { actionId: meta.id, err })
|
|
278
276
|
} else if (err.nodeId) {
|
|
279
277
|
this.emitter.emit('report', 'error', { err, nodeId: err.nodeId })
|
|
280
278
|
} else if (err.connectionId) {
|
|
281
279
|
this.emitter.emit('report', 'error', {
|
|
282
|
-
err,
|
|
283
|
-
|
|
280
|
+
connectionId: err.connectionId,
|
|
281
|
+
err
|
|
284
282
|
})
|
|
285
283
|
}
|
|
286
284
|
if (this.env === 'development') this.debugError(err)
|
|
@@ -290,8 +288,8 @@ export class BaseServer {
|
|
|
290
288
|
this.emitter.emit('report', 'clientError', { err, nodeId: err.nodeId })
|
|
291
289
|
} else if (err.connectionId) {
|
|
292
290
|
this.emitter.emit('report', 'clientError', {
|
|
293
|
-
err,
|
|
294
|
-
|
|
291
|
+
connectionId: err.connectionId,
|
|
292
|
+
err
|
|
295
293
|
})
|
|
296
294
|
}
|
|
297
295
|
})
|
|
@@ -337,20 +335,20 @@ export class BaseServer {
|
|
|
337
335
|
|
|
338
336
|
this.controls = {
|
|
339
337
|
'GET /': {
|
|
340
|
-
safe: true,
|
|
341
338
|
async request() {
|
|
342
339
|
return {
|
|
343
|
-
|
|
344
|
-
|
|
340
|
+
body: await readHello(),
|
|
341
|
+
headers: { 'Content-Type': 'text/html' }
|
|
345
342
|
}
|
|
346
|
-
}
|
|
343
|
+
},
|
|
344
|
+
safe: true
|
|
347
345
|
},
|
|
348
346
|
'GET /health': {
|
|
349
|
-
safe: true,
|
|
350
347
|
request: () => ({
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
})
|
|
348
|
+
body: 'Logux Server: OK\n',
|
|
349
|
+
headers: { 'Content-Type': 'text/plain' }
|
|
350
|
+
}),
|
|
351
|
+
safe: true
|
|
354
352
|
}
|
|
355
353
|
}
|
|
356
354
|
|
|
@@ -377,10 +375,119 @@ export class BaseServer {
|
|
|
377
375
|
)
|
|
378
376
|
}
|
|
379
377
|
|
|
378
|
+
addClient(connection) {
|
|
379
|
+
this.lastClient += 1
|
|
380
|
+
let key = this.lastClient.toString()
|
|
381
|
+
let client = new ServerClient(this, connection, key)
|
|
382
|
+
this.connected.set(key, client)
|
|
383
|
+
return this.lastClient
|
|
384
|
+
}
|
|
385
|
+
|
|
380
386
|
auth(authenticator) {
|
|
381
387
|
this.authenticator = authenticator
|
|
382
388
|
}
|
|
383
389
|
|
|
390
|
+
buildUndo(action, meta, reason, extra) {
|
|
391
|
+
let undoMeta = { status: 'processed' }
|
|
392
|
+
|
|
393
|
+
if (meta.users) undoMeta.users = meta.users.slice(0)
|
|
394
|
+
if (meta.nodes) undoMeta.nodes = meta.nodes.slice(0)
|
|
395
|
+
if (meta.clients) undoMeta.clients = meta.clients.slice(0)
|
|
396
|
+
if (meta.reasons) undoMeta.reasons = meta.reasons.slice(0)
|
|
397
|
+
if (meta.channels) undoMeta.channels = meta.channels.slice(0)
|
|
398
|
+
if (meta.excludeClients) {
|
|
399
|
+
undoMeta.excludeClients = meta.excludeClients.slice(0)
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
let undoAction = {
|
|
403
|
+
...extra,
|
|
404
|
+
action,
|
|
405
|
+
id: meta.id,
|
|
406
|
+
reason,
|
|
407
|
+
type: 'logux/undo'
|
|
408
|
+
}
|
|
409
|
+
return [undoAction, undoMeta]
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
channel(pattern, callbacks) {
|
|
413
|
+
normalizeChannelCallbacks(`Channel ${pattern}`, callbacks)
|
|
414
|
+
let channel = Object.assign({}, callbacks)
|
|
415
|
+
if (typeof pattern === 'string') {
|
|
416
|
+
channel.pattern = new UrlPattern(pattern, {
|
|
417
|
+
segmentValueCharset: '^/'
|
|
418
|
+
})
|
|
419
|
+
} else {
|
|
420
|
+
channel.regexp = pattern
|
|
421
|
+
}
|
|
422
|
+
this.channels.push(channel)
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
createContext(action, meta) {
|
|
426
|
+
let context = this.contexts.get(action)
|
|
427
|
+
if (!context) {
|
|
428
|
+
context = new Context(this, meta)
|
|
429
|
+
this.contexts.set(action, context)
|
|
430
|
+
}
|
|
431
|
+
return context
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
debugActionError(meta, msg) {
|
|
435
|
+
if (this.env === 'development') {
|
|
436
|
+
let clientId = parseId(meta.id).clientId
|
|
437
|
+
if (this.clientIds.has(clientId)) {
|
|
438
|
+
this.clientIds.get(clientId).connection.send(['debug', 'error', msg])
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
debugError(error) {
|
|
444
|
+
for (let i of this.connected.values()) {
|
|
445
|
+
if (i.connection.connected) {
|
|
446
|
+
try {
|
|
447
|
+
i.connection.send(['debug', 'error', error.stack])
|
|
448
|
+
} catch {}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
denyAction(action, meta) {
|
|
454
|
+
this.emitter.emit('report', 'denied', { actionId: meta.id })
|
|
455
|
+
this.undo(action, meta, 'denied')
|
|
456
|
+
this.debugActionError(meta, `Action "${meta.id}" was denied`)
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
destroy() {
|
|
460
|
+
this.destroying = true
|
|
461
|
+
this.emitter.emit('report', 'destroy')
|
|
462
|
+
return Promise.all(this.unbind.map(i => i()))
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
finally(processor, ctx, action, meta) {
|
|
466
|
+
this.contexts.delete(action)
|
|
467
|
+
if (processor && processor.finally) {
|
|
468
|
+
try {
|
|
469
|
+
processor.finally(ctx, action, meta)
|
|
470
|
+
} catch (err) {
|
|
471
|
+
this.emitter.emit('error', err, action, meta)
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
getProcessor(type) {
|
|
477
|
+
return (
|
|
478
|
+
this.types[type] || this.getRegexProcessor(type) || this.otherProcessor
|
|
479
|
+
)
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
getRegexProcessor(type) {
|
|
483
|
+
for (let regexp of this.regexTypes.keys()) {
|
|
484
|
+
if (type.match(regexp) !== null) {
|
|
485
|
+
return this.regexTypes.get(regexp)
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
return undefined
|
|
489
|
+
}
|
|
490
|
+
|
|
384
491
|
http(listener) {
|
|
385
492
|
if (this.options.disableHttpServer) {
|
|
386
493
|
throw new Error(
|
|
@@ -390,6 +497,48 @@ export class BaseServer {
|
|
|
390
497
|
this.httpListener = listener
|
|
391
498
|
}
|
|
392
499
|
|
|
500
|
+
internalUnkownType(action, meta) {
|
|
501
|
+
this.contexts.delete(action)
|
|
502
|
+
this.log.changeMeta(meta.id, { status: 'error' })
|
|
503
|
+
this.emitter.emit('report', 'unknownType', {
|
|
504
|
+
actionId: meta.id,
|
|
505
|
+
type: action.type
|
|
506
|
+
})
|
|
507
|
+
if (parseId(meta.id).userId !== 'server') {
|
|
508
|
+
this.undo(action, meta, 'unknownType')
|
|
509
|
+
}
|
|
510
|
+
this.debugActionError(meta, `Action with unknown type ${action.type}`)
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
internalWrongChannel(action, meta) {
|
|
514
|
+
this.contexts.delete(action)
|
|
515
|
+
this.emitter.emit('report', 'wrongChannel', {
|
|
516
|
+
actionId: meta.id,
|
|
517
|
+
channel: action.channel
|
|
518
|
+
})
|
|
519
|
+
this.undo(action, meta, 'wrongChannel')
|
|
520
|
+
this.debugActionError(meta, `Wrong channel name ${action.channel}`)
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
isBruteforce(ip) {
|
|
524
|
+
let attempts = this.authAttempts[ip]
|
|
525
|
+
return attempts && attempts >= 3
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
isUseless(action, meta) {
|
|
529
|
+
if (
|
|
530
|
+
meta.status !== 'processed' ||
|
|
531
|
+
this.types[action.type] ||
|
|
532
|
+
this.getRegexProcessor(action.type)
|
|
533
|
+
) {
|
|
534
|
+
return false
|
|
535
|
+
}
|
|
536
|
+
for (let i of ['channels', 'nodes', 'clients', 'users']) {
|
|
537
|
+
if (Array.isArray(meta[i]) && meta[i].length > 0) return false
|
|
538
|
+
}
|
|
539
|
+
return true
|
|
540
|
+
}
|
|
541
|
+
|
|
393
542
|
async listen() {
|
|
394
543
|
if (!this.authenticator) {
|
|
395
544
|
throw new Error('You must set authentication callback by server.auth()')
|
|
@@ -432,23 +581,34 @@ export class BaseServer {
|
|
|
432
581
|
this.addClient(new ServerConnection(ws))
|
|
433
582
|
})
|
|
434
583
|
this.emitter.emit('report', 'listen', {
|
|
435
|
-
|
|
584
|
+
backend: this.options.backend,
|
|
585
|
+
cert: !!this.options.cert,
|
|
436
586
|
controlMask: this.options.controlMask,
|
|
437
|
-
|
|
587
|
+
controlSecret: this.options.controlSecret,
|
|
438
588
|
environment: this.env,
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
backend: this.options.backend,
|
|
442
|
-
server: !!this.options.server,
|
|
589
|
+
host: this.options.host,
|
|
590
|
+
loguxServer: pkg.version,
|
|
443
591
|
nodeId: this.nodeId,
|
|
444
|
-
redis: this.options.redis,
|
|
445
592
|
notes: this.listenNotes,
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
593
|
+
port: this.options.port,
|
|
594
|
+
redis: this.options.redis,
|
|
595
|
+
server: !!this.options.server,
|
|
596
|
+
subprotocol: this.options.subprotocol,
|
|
597
|
+
supports: this.options.supports
|
|
449
598
|
})
|
|
450
599
|
}
|
|
451
600
|
|
|
601
|
+
markAsProcessed(meta) {
|
|
602
|
+
this.log.changeMeta(meta.id, { status: 'processed' })
|
|
603
|
+
let data = parseId(meta.id)
|
|
604
|
+
if (data.userId !== 'server') {
|
|
605
|
+
this.log.add(
|
|
606
|
+
{ id: meta.id, type: 'logux/processed' },
|
|
607
|
+
{ clients: [data.clientId], status: 'processed' }
|
|
608
|
+
)
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
452
612
|
on(event, listener) {
|
|
453
613
|
if (event === 'preadd' || event === 'add' || event === 'clean') {
|
|
454
614
|
return this.log.emitter.on(event, listener)
|
|
@@ -457,24 +617,18 @@ export class BaseServer {
|
|
|
457
617
|
}
|
|
458
618
|
}
|
|
459
619
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
this.
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
if (name instanceof RegExp) {
|
|
471
|
-
this.regexTypes.set(name, callbacks)
|
|
472
|
-
} else {
|
|
473
|
-
if (this.types[name]) {
|
|
474
|
-
throw new Error(`Action type ${name} was already defined`)
|
|
620
|
+
otherChannel(callbacks) {
|
|
621
|
+
normalizeChannelCallbacks('Unknown channel', callbacks)
|
|
622
|
+
if (this.otherSubscriber) {
|
|
623
|
+
throw new Error('Callbacks for unknown channel are already defined')
|
|
624
|
+
}
|
|
625
|
+
let channel = Object.assign({}, callbacks)
|
|
626
|
+
channel.pattern = {
|
|
627
|
+
match(name) {
|
|
628
|
+
return [name]
|
|
475
629
|
}
|
|
476
|
-
this.types[name] = callbacks
|
|
477
630
|
}
|
|
631
|
+
this.otherSubscriber = channel
|
|
478
632
|
}
|
|
479
633
|
|
|
480
634
|
otherType(callbacks) {
|
|
@@ -485,31 +639,29 @@ export class BaseServer {
|
|
|
485
639
|
this.otherProcessor = callbacks
|
|
486
640
|
}
|
|
487
641
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
throw new Error('Callbacks for unknown channel are already defined')
|
|
505
|
-
}
|
|
506
|
-
let channel = Object.assign({}, callbacks)
|
|
507
|
-
channel.pattern = {
|
|
508
|
-
match(name) {
|
|
509
|
-
return [name]
|
|
642
|
+
performUnsubscribe(clientNodeId, action, meta) {
|
|
643
|
+
if (this.subscribers[action.channel]) {
|
|
644
|
+
let subscriber = this.subscribers[action.channel][clientNodeId]
|
|
645
|
+
if (subscriber) {
|
|
646
|
+
if (subscriber.unsubscribe) {
|
|
647
|
+
subscriber.unsubscribe(action, meta)
|
|
648
|
+
this.contexts.delete(action)
|
|
649
|
+
}
|
|
650
|
+
let filterId = subscriberFilterId(action)
|
|
651
|
+
delete subscriber.filters[filterId]
|
|
652
|
+
if (Object.keys(subscriber.filters).length === 0) {
|
|
653
|
+
delete this.subscribers[action.channel][clientNodeId]
|
|
654
|
+
}
|
|
655
|
+
if (Object.keys(this.subscribers[action.channel]).length === 0) {
|
|
656
|
+
delete this.subscribers[action.channel]
|
|
657
|
+
}
|
|
510
658
|
}
|
|
511
659
|
}
|
|
512
|
-
this.
|
|
660
|
+
this.emitter.emit('unsubscribed', action, meta)
|
|
661
|
+
this.emitter.emit('report', 'unsubscribed', {
|
|
662
|
+
actionId: meta.id,
|
|
663
|
+
channel: action.channel
|
|
664
|
+
})
|
|
513
665
|
}
|
|
514
666
|
|
|
515
667
|
process(action, meta = {}) {
|
|
@@ -532,30 +684,54 @@ export class BaseServer {
|
|
|
532
684
|
})
|
|
533
685
|
}
|
|
534
686
|
|
|
535
|
-
|
|
536
|
-
let
|
|
537
|
-
let [undoAction, undoMeta] = this.buildUndo(action, meta, reason, extra)
|
|
538
|
-
undoMeta.clients = (undoMeta.clients || []).concat([clientId])
|
|
539
|
-
return this.log.add(undoAction, undoMeta)
|
|
540
|
-
}
|
|
687
|
+
async processAction(processor, action, meta, start) {
|
|
688
|
+
let ctx = this.createContext(action, meta)
|
|
541
689
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
690
|
+
let latency
|
|
691
|
+
this.processing += 1
|
|
692
|
+
try {
|
|
693
|
+
await processor.process(ctx, action, meta)
|
|
694
|
+
latency = Date.now() - start
|
|
695
|
+
this.markAsProcessed(meta)
|
|
696
|
+
} catch (e) {
|
|
697
|
+
this.log.changeMeta(meta.id, { status: 'error' })
|
|
698
|
+
this.undo(action, meta, 'error')
|
|
699
|
+
this.emitter.emit('error', e, action, meta)
|
|
700
|
+
} finally {
|
|
701
|
+
this.finally(processor, ctx, action, meta)
|
|
549
702
|
}
|
|
703
|
+
if (typeof latency === 'undefined') latency = Date.now() - start
|
|
704
|
+
this.processing -= 1
|
|
705
|
+
this.emitter.emit('processed', action, meta, latency)
|
|
550
706
|
}
|
|
551
707
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
708
|
+
rememberBadAuth(ip) {
|
|
709
|
+
this.authAttempts[ip] = (this.authAttempts[ip] || 0) + 1
|
|
710
|
+
this.setTimeout(() => {
|
|
711
|
+
if (this.authAttempts[ip] === 1) {
|
|
712
|
+
delete this.authAttempts[ip]
|
|
713
|
+
} else {
|
|
714
|
+
this.authAttempts[ip] -= 1
|
|
556
715
|
}
|
|
557
|
-
|
|
558
|
-
|
|
716
|
+
}, 3000)
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
replaceResendShortcuts(meta) {
|
|
720
|
+
if (meta.channel) {
|
|
721
|
+
meta.channels = [meta.channel]
|
|
722
|
+
delete meta.channel
|
|
723
|
+
}
|
|
724
|
+
if (meta.user) {
|
|
725
|
+
meta.users = [meta.user]
|
|
726
|
+
delete meta.user
|
|
727
|
+
}
|
|
728
|
+
if (meta.client) {
|
|
729
|
+
meta.clients = [meta.client]
|
|
730
|
+
delete meta.client
|
|
731
|
+
}
|
|
732
|
+
if (meta.node) {
|
|
733
|
+
meta.nodes = [meta.node]
|
|
734
|
+
delete meta.node
|
|
559
735
|
}
|
|
560
736
|
}
|
|
561
737
|
|
|
@@ -599,7 +775,6 @@ export class BaseServer {
|
|
|
599
775
|
}
|
|
600
776
|
|
|
601
777
|
if (meta.channels) {
|
|
602
|
-
let ctx
|
|
603
778
|
for (let channel of meta.channels) {
|
|
604
779
|
if (this.subscribers[channel]) {
|
|
605
780
|
for (let nodeId in this.subscribers[channel]) {
|
|
@@ -607,15 +782,16 @@ export class BaseServer {
|
|
|
607
782
|
if (!ignoreClients.has(clientId)) {
|
|
608
783
|
let subscriber = this.subscribers[channel][nodeId]
|
|
609
784
|
if (subscriber) {
|
|
610
|
-
let
|
|
611
|
-
if (typeof filter === 'function') {
|
|
612
|
-
if (!ctx) ctx = this.createContext(action, meta)
|
|
613
|
-
filter = await filter(ctx, action, meta)
|
|
614
|
-
}
|
|
785
|
+
let ctx = this.createContext(action, meta)
|
|
615
786
|
let client = this.clientIds.get(clientId)
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
787
|
+
for (let filter of Object.values(subscriber.filters)) {
|
|
788
|
+
filter = typeof filter === 'function'
|
|
789
|
+
? await filter(ctx, action, meta)
|
|
790
|
+
: filter
|
|
791
|
+
if (filter && client) {
|
|
792
|
+
ignoreClients.add(clientId)
|
|
793
|
+
client.node.onAdd(action, meta)
|
|
794
|
+
}
|
|
619
795
|
}
|
|
620
796
|
}
|
|
621
797
|
}
|
|
@@ -625,86 +801,23 @@ export class BaseServer {
|
|
|
625
801
|
}
|
|
626
802
|
}
|
|
627
803
|
|
|
628
|
-
|
|
629
|
-
this.
|
|
630
|
-
let
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
unknownType(action, meta) {
|
|
637
|
-
this.internalUnkownType(action, meta)
|
|
638
|
-
this.unknownTypes[meta.id] = true
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
wrongChannel(action, meta) {
|
|
642
|
-
this.internalWrongChannel(action, meta)
|
|
643
|
-
this.wrongChannels[meta.id] = true
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
internalUnkownType(action, meta) {
|
|
647
|
-
this.contexts.delete(action)
|
|
648
|
-
this.log.changeMeta(meta.id, { status: 'error' })
|
|
649
|
-
this.emitter.emit('report', 'unknownType', {
|
|
650
|
-
type: action.type,
|
|
651
|
-
actionId: meta.id
|
|
652
|
-
})
|
|
653
|
-
if (parseId(meta.id).userId !== 'server') {
|
|
654
|
-
this.undo(action, meta, 'unknownType')
|
|
655
|
-
}
|
|
656
|
-
this.debugActionError(meta, `Action with unknown type ${action.type}`)
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
internalWrongChannel(action, meta) {
|
|
660
|
-
this.contexts.delete(action)
|
|
661
|
-
this.emitter.emit('report', 'wrongChannel', {
|
|
662
|
-
actionId: meta.id,
|
|
663
|
-
channel: action.channel
|
|
664
|
-
})
|
|
665
|
-
this.undo(action, meta, 'wrongChannel')
|
|
666
|
-
this.debugActionError(meta, `Wrong channel name ${action.channel}`)
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
async processAction(processor, action, meta, start) {
|
|
670
|
-
let ctx = this.createContext(action, meta)
|
|
671
|
-
|
|
672
|
-
let latency
|
|
673
|
-
this.processing += 1
|
|
674
|
-
try {
|
|
675
|
-
await processor.process(ctx, action, meta)
|
|
676
|
-
latency = Date.now() - start
|
|
677
|
-
this.markAsProcessed(meta)
|
|
678
|
-
} catch (e) {
|
|
679
|
-
this.log.changeMeta(meta.id, { status: 'error' })
|
|
680
|
-
this.undo(action, meta, 'error')
|
|
681
|
-
this.emitter.emit('error', e, action, meta)
|
|
682
|
-
} finally {
|
|
683
|
-
this.finally(processor, ctx, action, meta)
|
|
684
|
-
}
|
|
685
|
-
if (typeof latency === 'undefined') latency = Date.now() - start
|
|
686
|
-
this.processing -= 1
|
|
687
|
-
this.emitter.emit('processed', action, meta, latency)
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
markAsProcessed(meta) {
|
|
691
|
-
this.log.changeMeta(meta.id, { status: 'processed' })
|
|
692
|
-
let data = parseId(meta.id)
|
|
693
|
-
if (data.userId !== 'server') {
|
|
694
|
-
this.log.add(
|
|
695
|
-
{ type: 'logux/processed', id: meta.id },
|
|
696
|
-
{ clients: [data.clientId], status: 'processed' }
|
|
697
|
-
)
|
|
698
|
-
}
|
|
804
|
+
setTimeout(callback, ms) {
|
|
805
|
+
this.lastTimeout += 1
|
|
806
|
+
let id = this.lastTimeout
|
|
807
|
+
this.timeouts[id] = setTimeout(() => {
|
|
808
|
+
delete this.timeouts[id]
|
|
809
|
+
callback()
|
|
810
|
+
}, ms)
|
|
699
811
|
}
|
|
700
812
|
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
813
|
+
subscribe(nodeId, channel) {
|
|
814
|
+
if (!this.subscribers[channel] || !this.subscribers[channel][nodeId]) {
|
|
815
|
+
if (!this.subscribers[channel]) {
|
|
816
|
+
this.subscribers[channel] = {}
|
|
817
|
+
}
|
|
818
|
+
this.subscribers[channel][nodeId] = { filters: { '{}': true } }
|
|
819
|
+
this.log.add({ channel, type: 'logux/subscribed' }, { nodes: [nodeId] })
|
|
706
820
|
}
|
|
707
|
-
return context
|
|
708
821
|
}
|
|
709
822
|
|
|
710
823
|
async subscribeAction(action, meta, start) {
|
|
@@ -747,8 +860,13 @@ export class BaseServer {
|
|
|
747
860
|
return
|
|
748
861
|
}
|
|
749
862
|
|
|
750
|
-
let
|
|
751
|
-
|
|
863
|
+
let filterId = subscriberFilterId(action)
|
|
864
|
+
let filters = { [filterId]: true }
|
|
865
|
+
|
|
866
|
+
if (channel.filter) {
|
|
867
|
+
let filter = await channel.filter(ctx, action, meta)
|
|
868
|
+
filters = { [filterId]: filter }
|
|
869
|
+
}
|
|
752
870
|
|
|
753
871
|
this.emitter.emit('report', 'subscribed', {
|
|
754
872
|
actionId: meta.id,
|
|
@@ -759,8 +877,12 @@ export class BaseServer {
|
|
|
759
877
|
this.subscribers[action.channel] = {}
|
|
760
878
|
this.emitter.emit('subscribing', action, meta)
|
|
761
879
|
}
|
|
880
|
+
let subscriber = this.subscribers[action.channel][ctx.nodeId]
|
|
881
|
+
if (subscriber) {
|
|
882
|
+
filters = { ...subscriber.filters, ...filters }
|
|
883
|
+
}
|
|
762
884
|
this.subscribers[action.channel][ctx.nodeId] = {
|
|
763
|
-
|
|
885
|
+
filters,
|
|
764
886
|
unsubscribe: channel.unsubscribe
|
|
765
887
|
? (unsubscribeAction, unsubscribeMeta) =>
|
|
766
888
|
channel.unsubscribe(ctx, unsubscribeAction, unsubscribeMeta)
|
|
@@ -802,25 +924,30 @@ export class BaseServer {
|
|
|
802
924
|
if (!match) this.wrongChannel(action, meta)
|
|
803
925
|
}
|
|
804
926
|
|
|
805
|
-
|
|
806
|
-
if (
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
if (Object.keys(this.subscribers[action.channel]).length === 0) {
|
|
815
|
-
delete this.subscribers[action.channel]
|
|
816
|
-
}
|
|
927
|
+
type(name, callbacks) {
|
|
928
|
+
if (typeof name === 'function') name = name.type
|
|
929
|
+
normalizeTypeCallbacks(`Action type ${name}`, callbacks)
|
|
930
|
+
|
|
931
|
+
if (name instanceof RegExp) {
|
|
932
|
+
this.regexTypes.set(name, callbacks)
|
|
933
|
+
} else {
|
|
934
|
+
if (this.types[name]) {
|
|
935
|
+
throw new Error(`Action type ${name} was already defined`)
|
|
817
936
|
}
|
|
937
|
+
this.types[name] = callbacks
|
|
818
938
|
}
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
undo(action, meta, reason = 'error', extra = {}) {
|
|
942
|
+
let clientId = parseId(meta.id).clientId
|
|
943
|
+
let [undoAction, undoMeta] = this.buildUndo(action, meta, reason, extra)
|
|
944
|
+
undoMeta.clients = (undoMeta.clients || []).concat([clientId])
|
|
945
|
+
return this.log.add(undoAction, undoMeta)
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
unknownType(action, meta) {
|
|
949
|
+
this.internalUnkownType(action, meta)
|
|
950
|
+
this.unknownTypes[meta.id] = true
|
|
824
951
|
}
|
|
825
952
|
|
|
826
953
|
unsubscribe(action, meta) {
|
|
@@ -840,124 +967,8 @@ export class BaseServer {
|
|
|
840
967
|
this.contexts.delete(action)
|
|
841
968
|
}
|
|
842
969
|
|
|
843
|
-
|
|
844
|
-
this.
|
|
845
|
-
this.
|
|
846
|
-
this.debugActionError(meta, `Action "${meta.id}" was denied`)
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
debugActionError(meta, msg) {
|
|
850
|
-
if (this.env === 'development') {
|
|
851
|
-
let clientId = parseId(meta.id).clientId
|
|
852
|
-
if (this.clientIds.has(clientId)) {
|
|
853
|
-
this.clientIds.get(clientId).connection.send(['debug', 'error', msg])
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
setTimeout(callback, ms) {
|
|
859
|
-
this.lastTimeout += 1
|
|
860
|
-
let id = this.lastTimeout
|
|
861
|
-
this.timeouts[id] = setTimeout(() => {
|
|
862
|
-
delete this.timeouts[id]
|
|
863
|
-
callback()
|
|
864
|
-
}, ms)
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
isUseless(action, meta) {
|
|
868
|
-
if (
|
|
869
|
-
meta.status !== 'processed' ||
|
|
870
|
-
this.types[action.type] ||
|
|
871
|
-
this.getRegexProcessor(action.type)
|
|
872
|
-
) {
|
|
873
|
-
return false
|
|
874
|
-
}
|
|
875
|
-
for (let i of ['channels', 'nodes', 'clients', 'users']) {
|
|
876
|
-
if (Array.isArray(meta[i]) && meta[i].length > 0) return false
|
|
877
|
-
}
|
|
878
|
-
return true
|
|
879
|
-
}
|
|
880
|
-
|
|
881
|
-
rememberBadAuth(ip) {
|
|
882
|
-
this.authAttempts[ip] = (this.authAttempts[ip] || 0) + 1
|
|
883
|
-
this.setTimeout(() => {
|
|
884
|
-
if (this.authAttempts[ip] === 1) {
|
|
885
|
-
delete this.authAttempts[ip]
|
|
886
|
-
} else {
|
|
887
|
-
this.authAttempts[ip] -= 1
|
|
888
|
-
}
|
|
889
|
-
}, 3000)
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
isBruteforce(ip) {
|
|
893
|
-
let attempts = this.authAttempts[ip]
|
|
894
|
-
return attempts && attempts >= 3
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
getProcessor(type) {
|
|
898
|
-
return (
|
|
899
|
-
this.types[type] || this.getRegexProcessor(type) || this.otherProcessor
|
|
900
|
-
)
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
getRegexProcessor(type) {
|
|
904
|
-
for (let regexp of this.regexTypes.keys()) {
|
|
905
|
-
if (type.match(regexp) !== null) {
|
|
906
|
-
return this.regexTypes.get(regexp)
|
|
907
|
-
}
|
|
908
|
-
}
|
|
909
|
-
return undefined
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
finally(processor, ctx, action, meta) {
|
|
913
|
-
this.contexts.delete(action)
|
|
914
|
-
if (processor && processor.finally) {
|
|
915
|
-
try {
|
|
916
|
-
processor.finally(ctx, action, meta)
|
|
917
|
-
} catch (err) {
|
|
918
|
-
this.emitter.emit('error', err, action, meta)
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
buildUndo(action, meta, reason, extra) {
|
|
924
|
-
let undoMeta = { status: 'processed' }
|
|
925
|
-
|
|
926
|
-
if (meta.users) undoMeta.users = meta.users.slice(0)
|
|
927
|
-
if (meta.nodes) undoMeta.nodes = meta.nodes.slice(0)
|
|
928
|
-
if (meta.clients) undoMeta.clients = meta.clients.slice(0)
|
|
929
|
-
if (meta.reasons) undoMeta.reasons = meta.reasons.slice(0)
|
|
930
|
-
if (meta.channels) undoMeta.channels = meta.channels.slice(0)
|
|
931
|
-
if (meta.excludeClients) {
|
|
932
|
-
undoMeta.excludeClients = meta.excludeClients.slice(0)
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
let undoAction = {
|
|
936
|
-
...extra,
|
|
937
|
-
type: 'logux/undo',
|
|
938
|
-
id: meta.id,
|
|
939
|
-
action,
|
|
940
|
-
reason
|
|
941
|
-
}
|
|
942
|
-
return [undoAction, undoMeta]
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
replaceResendShortcuts(meta) {
|
|
946
|
-
if (meta.channel) {
|
|
947
|
-
meta.channels = [meta.channel]
|
|
948
|
-
delete meta.channel
|
|
949
|
-
}
|
|
950
|
-
if (meta.user) {
|
|
951
|
-
meta.users = [meta.user]
|
|
952
|
-
delete meta.user
|
|
953
|
-
}
|
|
954
|
-
if (meta.client) {
|
|
955
|
-
meta.clients = [meta.client]
|
|
956
|
-
delete meta.client
|
|
957
|
-
}
|
|
958
|
-
if (meta.node) {
|
|
959
|
-
meta.nodes = [meta.node]
|
|
960
|
-
delete meta.node
|
|
961
|
-
}
|
|
970
|
+
wrongChannel(action, meta) {
|
|
971
|
+
this.internalWrongChannel(action, meta)
|
|
972
|
+
this.wrongChannels[meta.id] = true
|
|
962
973
|
}
|
|
963
974
|
}
|