@logux/server 0.12.3 → 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 +30 -29
- package/add-sync-map/index.js +4 -4
- package/base-server/hello.html +1 -1
- package/base-server/index.d.ts +397 -396
- package/base-server/index.js +307 -307
- package/bind-backend-proxy/index.js +77 -77
- package/bind-control-server/index.js +1 -1
- package/context/index.d.ts +34 -33
- package/context/index.js +1 -1
- package/create-http-server/index.js +4 -4
- package/create-reporter/index.js +107 -107
- 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 -25
- package/package.json +11 -11
- package/request/test.test.js +19 -19
- package/server/index.d.ts +27 -25
- package/server/index.js +56 -56
- package/server-client/index.d.ts +40 -39
- package/server-client/index.js +61 -61
- package/test-client/index.d.ts +47 -46
- package/test-client/index.js +28 -28
- package/test-server/index.d.ts +43 -42
- package/test-server/index.js +20 -20
- package/vite.config.ts +5 -5
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']
|
|
@@ -141,9 +141,9 @@ export class BaseServer {
|
|
|
141
141
|
|
|
142
142
|
let log
|
|
143
143
|
if (this.options.time) {
|
|
144
|
-
log = this.options.time.nextLog({
|
|
144
|
+
log = this.options.time.nextLog({ nodeId: this.nodeId, store })
|
|
145
145
|
} else {
|
|
146
|
-
log = new Log({
|
|
146
|
+
log = new Log({ nodeId: this.nodeId, store })
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
this.logger = console
|
|
@@ -272,13 +272,13 @@ export class BaseServer {
|
|
|
272
272
|
})
|
|
273
273
|
this.on('error', (err, action, meta) => {
|
|
274
274
|
if (meta) {
|
|
275
|
-
this.emitter.emit('report', 'error', {
|
|
275
|
+
this.emitter.emit('report', 'error', { actionId: meta.id, err })
|
|
276
276
|
} else if (err.nodeId) {
|
|
277
277
|
this.emitter.emit('report', 'error', { err, nodeId: err.nodeId })
|
|
278
278
|
} else if (err.connectionId) {
|
|
279
279
|
this.emitter.emit('report', 'error', {
|
|
280
|
-
err,
|
|
281
|
-
|
|
280
|
+
connectionId: err.connectionId,
|
|
281
|
+
err
|
|
282
282
|
})
|
|
283
283
|
}
|
|
284
284
|
if (this.env === 'development') this.debugError(err)
|
|
@@ -288,8 +288,8 @@ export class BaseServer {
|
|
|
288
288
|
this.emitter.emit('report', 'clientError', { err, nodeId: err.nodeId })
|
|
289
289
|
} else if (err.connectionId) {
|
|
290
290
|
this.emitter.emit('report', 'clientError', {
|
|
291
|
-
err,
|
|
292
|
-
|
|
291
|
+
connectionId: err.connectionId,
|
|
292
|
+
err
|
|
293
293
|
})
|
|
294
294
|
}
|
|
295
295
|
})
|
|
@@ -335,20 +335,20 @@ export class BaseServer {
|
|
|
335
335
|
|
|
336
336
|
this.controls = {
|
|
337
337
|
'GET /': {
|
|
338
|
-
safe: true,
|
|
339
338
|
async request() {
|
|
340
339
|
return {
|
|
341
|
-
|
|
342
|
-
|
|
340
|
+
body: await readHello(),
|
|
341
|
+
headers: { 'Content-Type': 'text/html' }
|
|
343
342
|
}
|
|
344
|
-
}
|
|
343
|
+
},
|
|
344
|
+
safe: true
|
|
345
345
|
},
|
|
346
346
|
'GET /health': {
|
|
347
|
-
safe: true,
|
|
348
347
|
request: () => ({
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
})
|
|
348
|
+
body: 'Logux Server: OK\n',
|
|
349
|
+
headers: { 'Content-Type': 'text/plain' }
|
|
350
|
+
}),
|
|
351
|
+
safe: true
|
|
352
352
|
}
|
|
353
353
|
}
|
|
354
354
|
|
|
@@ -375,10 +375,119 @@ export class BaseServer {
|
|
|
375
375
|
)
|
|
376
376
|
}
|
|
377
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
|
+
|
|
378
386
|
auth(authenticator) {
|
|
379
387
|
this.authenticator = authenticator
|
|
380
388
|
}
|
|
381
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
|
+
|
|
382
491
|
http(listener) {
|
|
383
492
|
if (this.options.disableHttpServer) {
|
|
384
493
|
throw new Error(
|
|
@@ -388,6 +497,48 @@ export class BaseServer {
|
|
|
388
497
|
this.httpListener = listener
|
|
389
498
|
}
|
|
390
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
|
+
|
|
391
542
|
async listen() {
|
|
392
543
|
if (!this.authenticator) {
|
|
393
544
|
throw new Error('You must set authentication callback by server.auth()')
|
|
@@ -430,23 +581,34 @@ export class BaseServer {
|
|
|
430
581
|
this.addClient(new ServerConnection(ws))
|
|
431
582
|
})
|
|
432
583
|
this.emitter.emit('report', 'listen', {
|
|
433
|
-
|
|
584
|
+
backend: this.options.backend,
|
|
585
|
+
cert: !!this.options.cert,
|
|
434
586
|
controlMask: this.options.controlMask,
|
|
435
|
-
|
|
587
|
+
controlSecret: this.options.controlSecret,
|
|
436
588
|
environment: this.env,
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
backend: this.options.backend,
|
|
440
|
-
server: !!this.options.server,
|
|
589
|
+
host: this.options.host,
|
|
590
|
+
loguxServer: pkg.version,
|
|
441
591
|
nodeId: this.nodeId,
|
|
442
|
-
redis: this.options.redis,
|
|
443
592
|
notes: this.listenNotes,
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
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
|
|
447
598
|
})
|
|
448
599
|
}
|
|
449
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
|
+
|
|
450
612
|
on(event, listener) {
|
|
451
613
|
if (event === 'preadd' || event === 'add' || event === 'clean') {
|
|
452
614
|
return this.log.emitter.on(event, listener)
|
|
@@ -455,24 +617,18 @@ export class BaseServer {
|
|
|
455
617
|
}
|
|
456
618
|
}
|
|
457
619
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
this.
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
if (name instanceof RegExp) {
|
|
469
|
-
this.regexTypes.set(name, callbacks)
|
|
470
|
-
} else {
|
|
471
|
-
if (this.types[name]) {
|
|
472
|
-
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]
|
|
473
629
|
}
|
|
474
|
-
this.types[name] = callbacks
|
|
475
630
|
}
|
|
631
|
+
this.otherSubscriber = channel
|
|
476
632
|
}
|
|
477
633
|
|
|
478
634
|
otherType(callbacks) {
|
|
@@ -483,31 +639,29 @@ export class BaseServer {
|
|
|
483
639
|
this.otherProcessor = callbacks
|
|
484
640
|
}
|
|
485
641
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
throw new Error('Callbacks for unknown channel are already defined')
|
|
503
|
-
}
|
|
504
|
-
let channel = Object.assign({}, callbacks)
|
|
505
|
-
channel.pattern = {
|
|
506
|
-
match(name) {
|
|
507
|
-
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
|
+
}
|
|
508
658
|
}
|
|
509
659
|
}
|
|
510
|
-
this.
|
|
660
|
+
this.emitter.emit('unsubscribed', action, meta)
|
|
661
|
+
this.emitter.emit('report', 'unsubscribed', {
|
|
662
|
+
actionId: meta.id,
|
|
663
|
+
channel: action.channel
|
|
664
|
+
})
|
|
511
665
|
}
|
|
512
666
|
|
|
513
667
|
process(action, meta = {}) {
|
|
@@ -530,30 +684,54 @@ export class BaseServer {
|
|
|
530
684
|
})
|
|
531
685
|
}
|
|
532
686
|
|
|
533
|
-
|
|
534
|
-
let
|
|
535
|
-
let [undoAction, undoMeta] = this.buildUndo(action, meta, reason, extra)
|
|
536
|
-
undoMeta.clients = (undoMeta.clients || []).concat([clientId])
|
|
537
|
-
return this.log.add(undoAction, undoMeta)
|
|
538
|
-
}
|
|
687
|
+
async processAction(processor, action, meta, start) {
|
|
688
|
+
let ctx = this.createContext(action, meta)
|
|
539
689
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
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)
|
|
547
702
|
}
|
|
703
|
+
if (typeof latency === 'undefined') latency = Date.now() - start
|
|
704
|
+
this.processing -= 1
|
|
705
|
+
this.emitter.emit('processed', action, meta, latency)
|
|
548
706
|
}
|
|
549
707
|
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
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
|
|
554
715
|
}
|
|
555
|
-
|
|
556
|
-
|
|
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
|
|
557
735
|
}
|
|
558
736
|
}
|
|
559
737
|
|
|
@@ -623,86 +801,23 @@ export class BaseServer {
|
|
|
623
801
|
}
|
|
624
802
|
}
|
|
625
803
|
|
|
626
|
-
|
|
627
|
-
this.
|
|
628
|
-
let
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
unknownType(action, meta) {
|
|
635
|
-
this.internalUnkownType(action, meta)
|
|
636
|
-
this.unknownTypes[meta.id] = true
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
wrongChannel(action, meta) {
|
|
640
|
-
this.internalWrongChannel(action, meta)
|
|
641
|
-
this.wrongChannels[meta.id] = true
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
internalUnkownType(action, meta) {
|
|
645
|
-
this.contexts.delete(action)
|
|
646
|
-
this.log.changeMeta(meta.id, { status: 'error' })
|
|
647
|
-
this.emitter.emit('report', 'unknownType', {
|
|
648
|
-
type: action.type,
|
|
649
|
-
actionId: meta.id
|
|
650
|
-
})
|
|
651
|
-
if (parseId(meta.id).userId !== 'server') {
|
|
652
|
-
this.undo(action, meta, 'unknownType')
|
|
653
|
-
}
|
|
654
|
-
this.debugActionError(meta, `Action with unknown type ${action.type}`)
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
internalWrongChannel(action, meta) {
|
|
658
|
-
this.contexts.delete(action)
|
|
659
|
-
this.emitter.emit('report', 'wrongChannel', {
|
|
660
|
-
actionId: meta.id,
|
|
661
|
-
channel: action.channel
|
|
662
|
-
})
|
|
663
|
-
this.undo(action, meta, 'wrongChannel')
|
|
664
|
-
this.debugActionError(meta, `Wrong channel name ${action.channel}`)
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
async processAction(processor, action, meta, start) {
|
|
668
|
-
let ctx = this.createContext(action, meta)
|
|
669
|
-
|
|
670
|
-
let latency
|
|
671
|
-
this.processing += 1
|
|
672
|
-
try {
|
|
673
|
-
await processor.process(ctx, action, meta)
|
|
674
|
-
latency = Date.now() - start
|
|
675
|
-
this.markAsProcessed(meta)
|
|
676
|
-
} catch (e) {
|
|
677
|
-
this.log.changeMeta(meta.id, { status: 'error' })
|
|
678
|
-
this.undo(action, meta, 'error')
|
|
679
|
-
this.emitter.emit('error', e, action, meta)
|
|
680
|
-
} finally {
|
|
681
|
-
this.finally(processor, ctx, action, meta)
|
|
682
|
-
}
|
|
683
|
-
if (typeof latency === 'undefined') latency = Date.now() - start
|
|
684
|
-
this.processing -= 1
|
|
685
|
-
this.emitter.emit('processed', action, meta, latency)
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
markAsProcessed(meta) {
|
|
689
|
-
this.log.changeMeta(meta.id, { status: 'processed' })
|
|
690
|
-
let data = parseId(meta.id)
|
|
691
|
-
if (data.userId !== 'server') {
|
|
692
|
-
this.log.add(
|
|
693
|
-
{ type: 'logux/processed', id: meta.id },
|
|
694
|
-
{ clients: [data.clientId], status: 'processed' }
|
|
695
|
-
)
|
|
696
|
-
}
|
|
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)
|
|
697
811
|
}
|
|
698
812
|
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
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] })
|
|
704
820
|
}
|
|
705
|
-
return context
|
|
706
821
|
}
|
|
707
822
|
|
|
708
823
|
async subscribeAction(action, meta, start) {
|
|
@@ -809,29 +924,30 @@ export class BaseServer {
|
|
|
809
924
|
if (!match) this.wrongChannel(action, meta)
|
|
810
925
|
}
|
|
811
926
|
|
|
812
|
-
|
|
813
|
-
if (
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
delete subscriber.filters[filterId]
|
|
822
|
-
if (Object.keys(subscriber.filters).length === 0) {
|
|
823
|
-
delete this.subscribers[action.channel][clientNodeId]
|
|
824
|
-
}
|
|
825
|
-
if (Object.keys(this.subscribers[action.channel]).length === 0) {
|
|
826
|
-
delete this.subscribers[action.channel]
|
|
827
|
-
}
|
|
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`)
|
|
828
936
|
}
|
|
937
|
+
this.types[name] = callbacks
|
|
829
938
|
}
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
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
|
|
835
951
|
}
|
|
836
952
|
|
|
837
953
|
unsubscribe(action, meta) {
|
|
@@ -851,124 +967,8 @@ export class BaseServer {
|
|
|
851
967
|
this.contexts.delete(action)
|
|
852
968
|
}
|
|
853
969
|
|
|
854
|
-
|
|
855
|
-
this.
|
|
856
|
-
this.
|
|
857
|
-
this.debugActionError(meta, `Action "${meta.id}" was denied`)
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
debugActionError(meta, msg) {
|
|
861
|
-
if (this.env === 'development') {
|
|
862
|
-
let clientId = parseId(meta.id).clientId
|
|
863
|
-
if (this.clientIds.has(clientId)) {
|
|
864
|
-
this.clientIds.get(clientId).connection.send(['debug', 'error', msg])
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
setTimeout(callback, ms) {
|
|
870
|
-
this.lastTimeout += 1
|
|
871
|
-
let id = this.lastTimeout
|
|
872
|
-
this.timeouts[id] = setTimeout(() => {
|
|
873
|
-
delete this.timeouts[id]
|
|
874
|
-
callback()
|
|
875
|
-
}, ms)
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
isUseless(action, meta) {
|
|
879
|
-
if (
|
|
880
|
-
meta.status !== 'processed' ||
|
|
881
|
-
this.types[action.type] ||
|
|
882
|
-
this.getRegexProcessor(action.type)
|
|
883
|
-
) {
|
|
884
|
-
return false
|
|
885
|
-
}
|
|
886
|
-
for (let i of ['channels', 'nodes', 'clients', 'users']) {
|
|
887
|
-
if (Array.isArray(meta[i]) && meta[i].length > 0) return false
|
|
888
|
-
}
|
|
889
|
-
return true
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
rememberBadAuth(ip) {
|
|
893
|
-
this.authAttempts[ip] = (this.authAttempts[ip] || 0) + 1
|
|
894
|
-
this.setTimeout(() => {
|
|
895
|
-
if (this.authAttempts[ip] === 1) {
|
|
896
|
-
delete this.authAttempts[ip]
|
|
897
|
-
} else {
|
|
898
|
-
this.authAttempts[ip] -= 1
|
|
899
|
-
}
|
|
900
|
-
}, 3000)
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
isBruteforce(ip) {
|
|
904
|
-
let attempts = this.authAttempts[ip]
|
|
905
|
-
return attempts && attempts >= 3
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
getProcessor(type) {
|
|
909
|
-
return (
|
|
910
|
-
this.types[type] || this.getRegexProcessor(type) || this.otherProcessor
|
|
911
|
-
)
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
getRegexProcessor(type) {
|
|
915
|
-
for (let regexp of this.regexTypes.keys()) {
|
|
916
|
-
if (type.match(regexp) !== null) {
|
|
917
|
-
return this.regexTypes.get(regexp)
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
return undefined
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
finally(processor, ctx, action, meta) {
|
|
924
|
-
this.contexts.delete(action)
|
|
925
|
-
if (processor && processor.finally) {
|
|
926
|
-
try {
|
|
927
|
-
processor.finally(ctx, action, meta)
|
|
928
|
-
} catch (err) {
|
|
929
|
-
this.emitter.emit('error', err, action, meta)
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
buildUndo(action, meta, reason, extra) {
|
|
935
|
-
let undoMeta = { status: 'processed' }
|
|
936
|
-
|
|
937
|
-
if (meta.users) undoMeta.users = meta.users.slice(0)
|
|
938
|
-
if (meta.nodes) undoMeta.nodes = meta.nodes.slice(0)
|
|
939
|
-
if (meta.clients) undoMeta.clients = meta.clients.slice(0)
|
|
940
|
-
if (meta.reasons) undoMeta.reasons = meta.reasons.slice(0)
|
|
941
|
-
if (meta.channels) undoMeta.channels = meta.channels.slice(0)
|
|
942
|
-
if (meta.excludeClients) {
|
|
943
|
-
undoMeta.excludeClients = meta.excludeClients.slice(0)
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
let undoAction = {
|
|
947
|
-
...extra,
|
|
948
|
-
type: 'logux/undo',
|
|
949
|
-
id: meta.id,
|
|
950
|
-
action,
|
|
951
|
-
reason
|
|
952
|
-
}
|
|
953
|
-
return [undoAction, undoMeta]
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
replaceResendShortcuts(meta) {
|
|
957
|
-
if (meta.channel) {
|
|
958
|
-
meta.channels = [meta.channel]
|
|
959
|
-
delete meta.channel
|
|
960
|
-
}
|
|
961
|
-
if (meta.user) {
|
|
962
|
-
meta.users = [meta.user]
|
|
963
|
-
delete meta.user
|
|
964
|
-
}
|
|
965
|
-
if (meta.client) {
|
|
966
|
-
meta.clients = [meta.client]
|
|
967
|
-
delete meta.client
|
|
968
|
-
}
|
|
969
|
-
if (meta.node) {
|
|
970
|
-
meta.nodes = [meta.node]
|
|
971
|
-
delete meta.node
|
|
972
|
-
}
|
|
970
|
+
wrongChannel(action, meta) {
|
|
971
|
+
this.internalWrongChannel(action, meta)
|
|
972
|
+
this.wrongChannels[meta.id] = true
|
|
973
973
|
}
|
|
974
974
|
}
|