@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.
@@ -1,18 +1,18 @@
1
- import { ServerConnection, MemoryStore, Log, parseId } from '@logux/core'
2
1
  import { LoguxNotFoundError } from '@logux/actions'
3
- import { createNanoEvents } from 'nanoevents'
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 { nanoid } from 'nanoid'
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({ store, nodeId: this.nodeId })
144
+ log = this.options.time.nextLog({ nodeId: this.nodeId, store })
141
145
  } else {
142
- log = new Log({ store, nodeId: this.nodeId })
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', { err, actionId: meta.id })
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
- connectionId: err.connectionId
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
- connectionId: err.connectionId
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
- headers: { 'Content-Type': 'text/html' },
344
- body: await readHello()
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
- headers: { 'Content-Type': 'text/plain' },
352
- body: 'Logux Server: OK\n'
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
- controlSecret: this.options.controlSecret,
584
+ backend: this.options.backend,
585
+ cert: !!this.options.cert,
436
586
  controlMask: this.options.controlMask,
437
- loguxServer: pkg.version,
587
+ controlSecret: this.options.controlSecret,
438
588
  environment: this.env,
439
- subprotocol: this.options.subprotocol,
440
- supports: this.options.supports,
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
- cert: !!this.options.cert,
447
- host: this.options.host,
448
- port: this.options.port
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
- destroy() {
461
- this.destroying = true
462
- this.emitter.emit('report', 'destroy')
463
- return Promise.all(this.unbind.map(i => i()))
464
- }
465
-
466
- type(name, callbacks) {
467
- if (typeof name === 'function') name = name.type
468
- normalizeTypeCallbacks(`Action type ${name}`, callbacks)
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
- channel(pattern, callbacks) {
489
- normalizeChannelCallbacks(`Channel ${pattern}`, callbacks)
490
- let channel = Object.assign({}, callbacks)
491
- if (typeof pattern === 'string') {
492
- channel.pattern = new UrlPattern(pattern, {
493
- segmentValueCharset: '^/'
494
- })
495
- } else {
496
- channel.regexp = pattern
497
- }
498
- this.channels.push(channel)
499
- }
500
-
501
- otherChannel(callbacks) {
502
- normalizeChannelCallbacks('Unknown channel', callbacks)
503
- if (this.otherSubscriber) {
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.otherSubscriber = channel
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
- undo(action, meta, reason = 'error', extra = {}) {
536
- let clientId = parseId(meta.id).clientId
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
- debugError(error) {
543
- for (let i of this.connected.values()) {
544
- if (i.connection.connected) {
545
- try {
546
- i.connection.send(['debug', 'error', error.stack])
547
- } catch {}
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
- subscribe(nodeId, channel) {
553
- if (!this.subscribers[channel] || !this.subscribers[channel][nodeId]) {
554
- if (!this.subscribers[channel]) {
555
- this.subscribers[channel] = {}
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
- this.subscribers[channel][nodeId] = { filter: true }
558
- this.log.add({ type: 'logux/subscribed', channel }, { nodes: [nodeId] })
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 filter = subscriber.filter
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
- if (filter && client) {
617
- ignoreClients.add(clientId)
618
- client.node.onAdd(action, meta)
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
- addClient(connection) {
629
- this.lastClient += 1
630
- let key = this.lastClient.toString()
631
- let client = new ServerClient(this, connection, key)
632
- this.connected.set(key, client)
633
- return this.lastClient
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
- createContext(action, meta) {
702
- let context = this.contexts.get(action)
703
- if (!context) {
704
- context = new Context(this, meta)
705
- this.contexts.set(action, context)
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 filter = true
751
- if (channel.filter) filter = await channel.filter(ctx, action, meta)
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
- filter,
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
- performUnsubscribe(clientNodeId, action, meta) {
806
- if (this.subscribers[action.channel]) {
807
- let subscriber = this.subscribers[action.channel][clientNodeId]
808
- if (subscriber) {
809
- if (subscriber.unsubscribe) {
810
- subscriber.unsubscribe(action, meta)
811
- this.contexts.delete(action)
812
- }
813
- delete this.subscribers[action.channel][clientNodeId]
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
- this.emitter.emit('unsubscribed', action, meta)
820
- this.emitter.emit('report', 'unsubscribed', {
821
- actionId: meta.id,
822
- channel: action.channel
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
- denyAction(action, meta) {
844
- this.emitter.emit('report', 'denied', { actionId: meta.id })
845
- this.undo(action, meta, 'denied')
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
  }