Git Product home page Git Product logo

server's Introduction

Logux

Logux contains multiple projects: core, Node.js server, Redux, and Vuex bindings, syntax sugar for Ruby and Django. This repository is the central hub to track big epics affecting several projects and provide support for end-users.

If you found a bug or need help in Logux best practices, this is the best place to create an issue.

server's People

Contributors

ai avatar aleksandrsl avatar ambientlight avatar andriibieriezhnoi avatar artmnv avatar bijela-gora avatar chafilin avatar dependabot[bot] avatar devgru avatar dsalahutdinov avatar erictheswift avatar ertrzyiks avatar euaaaio avatar gbezyuk avatar mattomanka avatar mavrin avatar morozred avatar neruchev avatar ngalaiko avatar nickmitin avatar okonet avatar pavlokovalov avatar polemius avatar pustovalov avatar sairus2k avatar semenovdl avatar stereobooster avatar vladbrok avatar xamgore avatar yevs avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

server's Issues

Should client be able to control re-sending actions to any node/client/user/channel?

Small example.

server:

app.type('bang', {
  access: () => {
    return clientCanSayBangToServer();
  },
  process() {
    processBang();
  },
});

user1:

client.log.on('add', (action) => {
  if (action.type === 'bang') {
    console.log('received bang from server');
  }
});

user2:

client.log.add({type: 'bang'}, {users: ['user1'], sync: true});

user1 can expect only the server will send him bang. But with re-sending it's not true. So we need to add extra checks to client and/or verify resending meta-fields in access() hook on server.
From server side it is very easy to forget to check resend fields. From client we can assume server always send true actions. It may be dangerous.

Another problem is flooding:

client.log.add({type: 'anyActionUserHaveAccessTo'}, {channels: ['billionUsersChannel1', 'billionUsersChannel2', ...], sync: true});

I'm not sure this behavior is a good idea from security side. IMO recipient should control re-sending not sender. It can be ok for P2P (client should always make security checks) but not for client/server architecture.

Use async/await

Logux Server 0.3 requires Node.js >= 10, so we are safe to use async/await instead of Promise

Tests fails on Linux

OS: Ubuntu 16.04 xenial x86_64
Kernel: 4.8.0-56-generic

I have cloned the repo, install packages and test.

$ yarn test
yarn test v0.24.5
$ jest --coverage && yarn run lint && yarn run spellcheck
PASS test/human-reporter.test.js
PASS test/base-server.test.js
PASS test/bunyan-reporter.test.js
FAIL test/server-client.test.js
● sends old actions by node ID

expect(received).toEqual(expected)

Expected value to equal:
  ["sync", 2, {"type": "FOO"}, {"id": [2, "server:uuid", 0], "time": 2}]
Received:
  ["sync", 2, {"type": "FOO"}, {"id": [2, "server:uuid", 0], "time": 2}, {"type": "FOO"}, {"id": [1, "server:uuid", 0], "time": 1}]

Difference:

- Expected
+ Received

@@ -10,6 +10,17 @@
       "server:uuid",
       0,
     ],
     "time": 2,
   },
+  Object {
+    "type": "FOO",
+  },
+  Object {
+    "id": Array [
+      1,
+      "server:uuid",
+      0,
+    ],
+    "time": 1,
+  },
 ]
  
  at client.sync.waitFor.then (test/server-client.test.js:463:31)
      at <anonymous>

● sends old actions by user

expect(received).toEqual(expected)

Expected value to equal:
  ["sync", 2, {"type": "FOO"}, {"id": [2, "server:uuid", 0], "time": 2}]
Received:
  ["sync", 2, {"type": "FOO"}, {"id": [2, "server:uuid", 0], "time": 2}, {"type": "FOO"}, {"id": [1, "server:uuid", 0], "time": 1}]                                                                                                                       

Difference:

- Expected
+ Received

@@ -10,6 +10,17 @@
       "server:uuid",
       0,
     ],
     "time": 2,
   },
+  Object {
+    "type": "FOO",
+  },
+  Object {
+    "id": Array [
+      1,
+      "server:uuid",
+      0,
+    ],
+    "time": 1,
+  },
 ]
  
  at client.sync.waitFor.then (test/server-client.test.js:505:31)
      at <anonymous>

PASS test/force-promise.test.js
PASS test/promisify.test.js
PASS test/index.test.js
FAIL test/filtered-sync.test.js (5.167s)
● does not sync actions on add

Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
  
  at pTimeout (node_modules/jest-jasmine2/build/queueRunner.js:53:21)
  at Timeout.callback [as _onTimeout] (node_modules/jsdom/lib/jsdom/browser/Window.js:523:19)
  at ontimeout (timers.js:488:11)
  at tryOnTimeout (timers.js:323:5)
  at Timer.listOnTimeout (timers.js:283:5)

● synchronizes only node-specific actions on connection

expect(received).toEqual(expected)

Expected value to equal:
  [{"type": "B"}]
Received:
  [{"type": "C"}, {"type": "B"}, {"type": "A"}]

Difference:

- Expected
+ Received

 Array [
   Object {
+    "type": "C",
+  },
+  Object {
     "type": "B",
   },
+  Object {
+    "type": "A",
+  },
 ]
  
  at Promise.all.then.then.then (test/filtered-sync.test.js:56:34)
      at <anonymous>

● synchronizes only user-specific actions on connection

expect(received).toEqual(expected)

Expected value to equal:
  [{"type": "B"}]
Received:
  [{"type": "C"}, {"type": "B"}, {"type": "A"}]

Difference:

- Expected
+ Received

 Array [
   Object {
+    "type": "C",
+  },
+  Object {
     "type": "B",
   },
+  Object {
+    "type": "A",
+  },
 ]
  
  at Promise.all.then.then.then (test/filtered-sync.test.js:71:34)
      at <anonymous>

PASS test/server.test.js (6.438s)

Test Suites: 2 failed, 7 passed, 9 total
Tests: 5 failed, 153 passed, 158 total
Snapshots: 65 passed, 65 total
Time: 10.578s

handle action order/dependency

Goal

Allow the business logic to identify dependencies between actions if any and apply them in any order.

Problem

When there is a reliable connection to the server the actions are applied in order they are received. But when the connection is intermittent, the client send a batch of actions and the server handles them in reverse order (newest action first), which has great benefits but doesn't count for the business logic having any preference.
For example:
on the client the following actions are created

  1. C1: create user
  2. C2: update user.name to "john"

in case of a good connection, the server handles C1 then C2, resulting in a valid user object.

in case of a bad connection the server receives a batch of [C2, C1] and handles C2, then C1 which might result in invalid user object.

Expectation

Either the server allows the for specifying the order to process actions, or it supplies the batch of actions to the handler and shift the responsibility to the business logic to figure out.

TypeError: Server.loadOptions is not a function

I've just installed [email protected] and wanted to use the snippet from the README

const Server = require('logux-server').Server

const app = new Server(
  Server.loadOptions(process, {
    subprotocol: '1.0.0',
    supports: '1.x',
    root: __dirname
  })
)

app.auth((userId, token) => {
  // TODO Check token and return a Promise with true or false.
})

app.listen()

It throws an error as in the title

TypeError: Server.loadOptions is not a function

Looks like the current master defines loadOptions function on Server, but the code from npm does not.

A complete example app?

Is there a complete example app available? I am trying to update https://github.com/Mavrin/logux-example to the current version of Logux and failing. This is the only complete example I have been able to find.

With the documentation scattered amongst all the different modules it is difficult to get a complete understanding of all the Logux workings, sufficiently enough to get something working. At least for me.

Any help greatly appreciated.

It sure would be nice to see Logux become more widely used. If I understand its capabilities correctly it certainly has excellent potential in the offline, decentralized app world.

Do not attach 'request' listener if custom http server is provided

When I'm passing a custom HTTP server to Logux I would like to have full control over my routes.

import { Server as LoguxServer } from '@logux/server'
import http from 'http'
import polka from 'polka'

const server = http.createServer()

const app = polka({ server })
app.get('/status', (req, res) => res.end('OK'))

const loguxServer = new LoguxServer()

app.listen()
await loguxServer.listen()

This doesn't work and I have to detach it manually.

await loguxServer.listen()

let [polkaListener, loguxListener] = server.listeners('request')
server.off('request', loguxListener as any)

https://github.com/logux/server/blob/main/base-server/index.js#L400

Safer shutdown

Change shutdown behavior to:

  1. Stop accepting new clients
  2. Close connection if there is no action in processing status
  3. Do not accept new actions from clients.
  4. Wait until all client’s action will be processed, then close connection
  5. When all clients will be processed, close WS server.

Brute-force protcetion for control server

As an extra protection, we require the Logux server and web server to share the same password and we use it in requests.

But it is impossible to brute-force this password. So we need to have protection against it.

Tests fails on Windows

OS: Windows 10 Ent build 1803 v17134.706
Node: v10.15.3
Yarn: v1.12.3

$ jest --forceExit --runInBand --coverage && yarn lint && yarn spell
 FAIL  test/server.test.js (15.788s)
  ● destroys everything on exit

    Fall with:
     INFO   Logux server is listening at 1970-01-01 00:00:00
            PID:          21384
            Logux server: 0.0.0
            Environment:  test
            Node ID:      server:FnXaqDxY
            Subprotocol:  1.0.0
            Supports:     1.x
            Health check: http://127.0.0.1:31338/status
            Listen:       ws://127.0.0.1:2000/

      62 |     let exit = result[1]
      63 |     if (exit !== 0) {
    > 64 |       throw new Error(`Fall with:\n${ out }`)
         |             ^
      65 |     }
      66 |     expect(out).toMatchSnapshot()
      67 |   })

      
      at check.then.result (test/server.test.js:64:13)

  ● uses environment variables for config

    Fall with:
    {"name":"logux-server","hostname":"localhost","pid":21384,"level":30,"loguxServer":"0.0.0","environment":"test","nodeId":"server:FnXaqDxY","subprotocol":"1.0.0","supports":"1.x","listen":"ws://127.0.0.1:31337/","healthCheck":"http://127.0.0.1:31338/status","msg":"Logux server is listening","time":"1970-01-01T00:00:00.000Z","v":0}

      62 |     let exit = result[1]
      63 |     if (exit !== 0) {
    > 64 |       throw new Error(`Fall with:\n${ out }`)
         |             ^
      65 |     }
      66 |     expect(out).toMatchSnapshot()
      67 |   })

      at check.then.result (test/server.test.js:64:13)

  ● uses reporter param

    Fall with:
    {"name":"logux-server","hostname":"localhost","pid":21384,"level":30,"loguxServer":"0.0.0","environment":"test","nodeId":"server:FnXaqDxY","subprotocol":"1.0.0","supports":"1.x","listen":"ws://127.0.0.1:31337/","healthCheck":"http://127.0.0.1:31338/status","msg":"Logux server is listening","time":"1970-01-01T00:00:00.000Z","v":0}

      62 |     let exit = result[1]
      63 |     if (exit !== 0) {
    > 64 |       throw new Error(`Fall with:\n${ out }`)
         |             ^
      65 |     }
      66 |     expect(out).toMatchSnapshot()
      67 |   })

      at check.then.result (test/server.test.js:64:13)

  ● shows help about privileged port

    Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.Error: 

      196 | })
      197 | 
    > 198 | it('shows help about privileged port', () => checkError('eacces.js'))
          | ^
      199 | 
      200 | it('shows help about unknown option', () => checkError('unknown.js'))
      201 | 

      at new Spec (node_modules/jest-jasmine2/build/jasmine/Spec.js:116:22)
      at Object.it (test/server.test.js:198:1)

  ● writes to bunyan log

    Fall with:
    {"name":"logux-server","hostname":"localhost","pid":21384,"level":50,"err":{"message":"listen EADDRINUSE: address already in use 127.0.0.1:31338","name":"Error"},"note":"Another Logux server or other app already running on this port. Maybe you didn’t not stop server from other project or previous version of this server was not killed.","msg":"Port `31338` already in use","time":"1970-01-01T00:00:00.000Z","v":0}

      62 |     let exit = result[1]
      63 |     if (exit !== 0) {
    > 64 |       throw new Error(`Fall with:\n${ out }`)
         |             ^
      65 |     }
      66 |     expect(out).toMatchSnapshot()
      67 |   })

      at check.then.result (test/server.test.js:64:13)

  ● writes to custom bunyan log

    Fall with:
    {"name":"logux-server-custom","customProp":"42","hostname":"localhost","pid":21384,"level":50,"err":{"message":"listen EADDRINUSE: address already in use 127.0.0.1:31338","name":"Error"},"note":"Another Logux server or other app already running on this port. Maybe you didn’t not stop server from other project or previous version of this server was not killed.","msg":"Port `31338` already in use","time":"1970-01-01T00:00:00.000Z","v":0}

      62 |     let exit = result[1]
      63 |     if (exit !== 0) {
    > 64 |       throw new Error(`Fall with:\n${ out }`)
         |             ^
      65 |     }
      66 |     expect(out).toMatchSnapshot()
      67 |   })

      at check.then.result (test/server.test.js:64:13)

  ● writes using custom reporter

    Fall with:
    Event: error
    Details: {"err":{"code":"EADDRINUSE","errno":"EADDRINUSE","syscall":"listen","address":"127.0.0.1","port":31338}}

      62 |     let exit = result[1]
      63 |     if (exit !== 0) {
    > 64 |       throw new Error(`Fall with:\n${ out }`)
         |             ^
      65 |     }
      66 |     expect(out).toMatchSnapshot()
      67 |   })

      at check.then.result (test/server.test.js:64:13)

 FAIL  test/bind-backend-proxy.test.js
  ● reports about network errors

    expect(received).toEqual(expected)

    Difference:

    - Expected
    + Received

    - Array [
    -   "ECONNREFUSED",
    - ]
    + Array []

      256 |     return delay(100)
      257 |   }).then(() => {
    > 258 |     expect(errors).toEqual(['ECONNREFUSED'])
          |                    ^
      259 |     expect(app.log.actions()).toEqual([
      260 |       { type: 'logux/undo', reason: 'error', id: '1 10:uuid 0' }
      261 |     ])

      at toEqual (test/bind-backend-proxy.test.js:258:20)

  ● reacts on wrong backend answer

    expect(received).toEqual(expected)

    Difference:

    - Expected
    + Received

    @@ -15,16 +15,16 @@
          "id": "1 10:uuid 0",
          "reason": "error",
          "type": "logux/undo",
        },
        Object {
    -     "id": "2 10:uuid 0",
    +     "id": "3 10:uuid 0",
          "reason": "error",
          "type": "logux/undo",
        },
        Object {
    -     "id": "3 10:uuid 0",
    +     "id": "2 10:uuid 0",
          "reason": "error",
          "type": "logux/undo",
        },
        Object {
          "id": "4 10:uuid 0",

      440 |     return delay(100)
      441 |   }).then(() => {
    > 442 |     expect(app.log.actions()).toEqual([
          |                               ^
      443 |       { type: 'BROKEN1' },
      444 |       { type: 'BROKEN2' },
      445 |       { type: 'BROKEN3' },

      at toEqual (test/bind-backend-proxy.test.js:442:31)

 FAIL  test/base-server.test.js
  ● uses HTTPS

    listen EADDRINUSE: address already in use 127.0.0.1:31338



  ● uses HTTPS

    listen EADDRINUSE: address already in use 127.0.0.1:31338



  ● loads keys by absolute path

    listen EADDRINUSE: address already in use 127.0.0.1:31337



  ● loads keys by absolute path

    listen EADDRINUSE: address already in use 127.0.0.1:31337



  ● loads keys by relative path

    listen EADDRINUSE: address already in use 127.0.0.1:31337



  ● loads keys by relative path

    listen EADDRINUSE: address already in use 127.0.0.1:31337



  ● supports object in SSL key

    listen EADDRINUSE: address already in use 127.0.0.1:31337



  ● supports object in SSL key

    listen EADDRINUSE: address already in use 127.0.0.1:31337



  ● reporters on start listening

    listen EADDRINUSE: address already in use 127.0.0.1:31337



  ● reporters on start listening

    listen EADDRINUSE: address already in use 127.0.0.1:31337



  ● creates a client on connection

    listen EADDRINUSE: address already in use 127.0.0.1:31337



  ● creates a client on connection

    listen EADDRINUSE: address already in use 127.0.0.1:31337



  ● accepts custom HTTP server

    listen EADDRINUSE: address already in use 127.0.0.1:31338



  ● accepts custom HTTP server

    listen EADDRINUSE: address already in use 127.0.0.1:31338



  ● marks actions with own node ID

    Error [ERR_SERVER_NOT_RUNNING]: Server is not running.



  ● marks actions with waiting status

    Error [ERR_SERVER_NOT_RUNNING]: Server is not running.



  ● defines actions types

    Error [ERR_SERVER_NOT_RUNNING]: Server is not running.



  ● does not allow to define type twice

    Error [ERR_SERVER_NOT_RUNNING]: Server is not running.



  ● requires access callback for type

    Error [ERR_SERVER_NOT_RUNNING]: Server is not running.



  ● reports about unknown action type

    Error [ERR_SERVER_NOT_RUNNING]: Server is not running.



  ● ignores unknown type for processed actions

    Error [ERR_SERVER_NOT_RUNNING]: Server is not running.



  ● reports about fatal error

    Error [ERR_SERVER_NOT_RUNNING]: Server is not running.



  ● sends errors to clients in development

    Error [ERR_SERVER_NOT_RUNNING]: Server is not running.



  ● does not send errors in non-development mode

    Error [ERR_SERVER_NOT_RUNNING]: Server is not running.



  ● processes actions

    Error [ERR_SERVER_NOT_RUNNING]: Server is not running.



  ● has full events API

    Error [ERR_SERVER_NOT_RUNNING]: Server is not running.



  ● waits for last processing before destroy

    Error [ERR_SERVER_NOT_RUNNING]: Server is not running.



  ● reports about error during action processing

    Error [ERR_SERVER_NOT_RUNNING]: Server is not running.



  ● undos actions on client

    Error [ERR_SERVER_NOT_RUNNING]: Server is not running.



  ● adds current subprotocol to meta

    Error [ERR_SERVER_NOT_RUNNING]: Server is not running.



  ● adds current subprotocol only to own actions

    Error [ERR_SERVER_NOT_RUNNING]: Server is not running.



  ● allows to override subprotocol in meta

    Error [ERR_SERVER_NOT_RUNNING]: Server is not running.



  ● checks channel definition

    Error [ERR_SERVER_NOT_RUNNING]: Server is not running.



  ● reports about wrong channel name

    Error [ERR_SERVER_NOT_RUNNING]: Server is not running.



  ● checks custom channel name subscriber

    Error [ERR_SERVER_NOT_RUNNING]: Server is not running.



  ● allows to have custom channel name check

    Error [ERR_SERVER_NOT_RUNNING]: Server is not running.



  ● ignores subscription for other servers

    Error [ERR_SERVER_NOT_RUNNING]: Server is not running.



  ● checks channel access

    Error [ERR_SERVER_NOT_RUNNING]: Server is not running.



  ● reports about errors during channel authorization

    Error [ERR_SERVER_NOT_RUNNING]: Server is not running.



  ● subscribes clients

    Error [ERR_SERVER_NOT_RUNNING]: Server is not running.



  ● cancels subscriptions on disconnect

    Error [ERR_SERVER_NOT_RUNNING]: Server is not running.



  ● reports about errors during channel initialization

    Error [ERR_SERVER_NOT_RUNNING]: Server is not running.



  ● loads initial actions during subscription

    Error [ERR_SERVER_NOT_RUNNING]: Server is not running.



  ● does not need type definition for own actions

    Error [ERR_SERVER_NOT_RUNNING]: Server is not running.



  ● checks callbacks in unknown type handler

    Error [ERR_SERVER_NOT_RUNNING]: Server is not running.



  ● reports about useless actions

    Error [ERR_SERVER_NOT_RUNNING]: Server is not running.



 FAIL  test/create-reporter.test.js
  ● creates human reporter

    expect(received).toEqual(expected)

    Difference:

    - Expected
    + Received

    - /dir/
    + /dir/\

      85 |   let stream = reporter.logger.streams[0].stream
      86 |   expect(stream instanceof HumanFormatter).toBeTruthy()
    > 87 |   expect(stream.basepath).toEqual('/dir/')
         |                           ^
      88 |   expect(stream.chalk.enabled).toBeFalsy()
      89 | })
      90 | 

      at Object.toEqual (test/create-reporter.test.js:87:27)

  ● adds trailing slash to path

    expect(received).toEqual(expected)

    Difference:

    - Expected
    + Received

    - /dir/
    + /dir\

      91 | it('adds trailing slash to path', () => {
      92 |   let reporter = createReporter({ reporter: 'human', root: '/dir' })
    > 93 |   expect(reporter.logger.streams[0].stream.basepath).toEqual('/dir/')
         |                                                      ^
      94 | })
      95 | 
      96 | it('uses color in development', () => {

      at Object.toEqual (test/create-reporter.test.js:93:54)

  ● reports error

    expect(received).toMatchSnapshot()

    Snapshot name: `reports error 2`

    - Snapshot
    + Received

      " FATAL  Some mistake at 1970-01-01 00:00:00
    -         at Object.<anonymous> (./index.js:28:13)
    +         at Object.<anonymous> (/dev/app/index.js:28:13)
              at Module._compile (module.js:573:32)
    -         at at runTest (./node_modules/jest/index.js:50:10)
    +         at at runTest (/dev/app/node_modules/jest/index.js:50:10)
              at process._tickCallback (internal/process/next_tick.js:103:7)
      
      "

      48 | 
      49 |   humanReporter(type, details)
    > 50 |   expect(clean(human.string)).toMatchSnapshot()
         |                               ^
      51 | }
      52 | 
      53 | function createError (name, message) {

      at toMatchSnapshot (test/create-reporter.test.js:50:31)
      at Object.check (test/create-reporter.test.js:316:3)

  ● reports error from action

    expect(received).toMatchSnapshot()

    Snapshot name: `reports error from action 2`

    - Snapshot
    + Received

      " ERROR  Some mistake at 1970-01-01 00:00:00
    -         at Object.<anonymous> (./index.js:28:13)
    +         at Object.<anonymous> (/dev/app/index.js:28:13)
              at Module._compile (module.js:573:32)
    -         at at runTest (./node_modules/jest/index.js:50:10)
    +         at at runTest (/dev/app/node_modules/jest/index.js:50:10)
              at process._tickCallback (internal/process/next_tick.js:103:7)
              Action ID: 1487805099387 100:uImkcF4z 0
      
      "

      48 | 
      49 |   humanReporter(type, details)
    > 50 |   expect(clean(human.string)).toMatchSnapshot()
         |                               ^
      51 | }
      52 | 
      53 | function createError (name, message) {

      at toMatchSnapshot (test/create-reporter.test.js:50:31)
      at Object.check (test/create-reporter.test.js:320:3)

 › 2 snapshots failed.
 PASS  test/server-client.test.js
 PASS  test/start-control-server.test.js
 PASS  test/index.test.js
 PASS  test/filtered-node.test.js
 PASS  test/context.test.js
 PASS  test/force-promise.test.js
 PASS  test/parse-node-id.test.js
 PASS  test/promisify.test.js
-------------------------|----------|----------|----------|----------|-------------------|
File                     |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
-------------------------|----------|----------|----------|----------|-------------------|
All files                |    99.27 |    97.19 |    99.47 |    99.23 |                   |
 allowed-meta.js         |      100 |      100 |      100 |      100 |                   |
 base-server.js          |    99.48 |    97.08 |    98.63 |    99.45 |           415,422 |
 bind-backend-proxy.js   |      100 |    98.18 |      100 |      100 |               132 |
 context.js              |      100 |      100 |      100 |      100 |                   |
 create-reporter.js      |      100 |    97.73 |      100 |      100 |                93 |
 filtered-node.js        |      100 |    90.91 |      100 |      100 |                28 |
 force-promise.js        |      100 |      100 |      100 |      100 |                   |
 human-formatter.js      |    96.03 |    92.75 |      100 |    95.73 |... 56,157,158,160 |
 index.js                |      100 |      100 |      100 |      100 |                   |
 parse-node-id.js        |      100 |      100 |      100 |      100 |                   |
 promisify.js            |      100 |      100 |      100 |      100 |                   |
 server-client.js        |      100 |      100 |      100 |      100 |                   |
 start-control-server.js |      100 |      100 |      100 |      100 |                   |
-------------------------|----------|----------|----------|----------|-------------------|
Jest: "global" coverage threshold for statements (100%) not met: 99.27%

Snapshot Summary
 › 2 snapshots failed from 1 test suite. Inspect your code changes or run `yarn test -u` to update them.

Test Suites: 4 failed, 8 passed, 12 total
Tests:       52 failed, 159 passed, 211 total
Snapshots:   2 failed, 70 passed, 72 total
Time:        29.616s, estimated 30s
Ran all test suites.
Force exiting Jest

Have you considered using `--detectOpenHandles` to detect async operations that kept running after all tests finished?
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

Process finished with exit code 1

reporter - human-formatter enhancement request

The Bunyan reporter output for arrays is currently one long string. It would be much, much better if it was nicely formatted.

So instead of:

items: [{ id: "6356af00-ca8c-423e-9a74-0fa3ca600e3d", title: "Gym workout", completed: "false" }, { id: "6d6e49b0-ba36-4a78-85f7-0f4023551d56", title: "Doodle", completed: "false" }, { 0: { id: "15e65afb-409a-4e7e-bbd7-e5d1b2d3db95", title: "Sauna", completed: true }, id: "15e65afb-409a-4e7e-bbd7-e5d1b2d3db95", title: "Sauna", completed: "false" }, { id: "10c9cce8-048b-4d38-b5cd-7ddb96c1a0cd", title: "Play the Old on the morrow", completed: "true" }, { id: "bf71e298-40e3-4466-bf66-20de71182724", title: "Swim", completed: "false" }, {
id: "a492941f-84c8-4cc9-88a0-d7cef9cc2451", title: "Red", completed: "false" }, { id: "30d0db7c-383e-4f76-9cb8-5b99bd7a7ded", title: "123", completed: "false" }]

you would see:

 items: [
    {
      id: "6356af00-ca8c-423e-9a74-0fa3ca600e3d",
      title: "Gym workout",
      completed: "false"
    },
    {
      id: "6d6e49b0-ba36-4a78-85f7-0f4023551d56",
      title: "Doodle",
      completed: "false"
    },
    {
      0: {
        id: "15e65afb-409a-4e7e-bbd7-e5d1b2d3db95",
        title: "Sauna",
        completed: true
      },
      id: "15e65afb-409a-4e7e-bbd7-e5d1b2d3db95",
      title: "Sauna",
      completed: "false"
    },

I've added https://www.npmjs.com/package/jsome and use that to format json before passing it to reporter and that works nicely. Maybe jsome could be used in human-formatter!

Multi-subscribe or Push subscribtions to client?

I trying to implement simple application with Logux. And I have some questions about implementing loading subresources. For example I have a simple blog with posts and related comments. On client I want to subscribe to post with id X, and to first 10 comments.

On server I created two channel: posts/:id and comments/:id.

On client I do this steps for loading:

  1. Send action { type: "logux/subscribe", channel: "posts/X" }
  2. Send action { type: "posts/get-comments", id: X, count: 10, from: 0 }
  3. After recive reply from second action I send 10 actions like this: { type: "logux/subscribe", channel: "comments/Y" }

For me 12 actions for it is overhead, isn't it?

Use Jest snapshots

I was wondering why did you write your own snapshots mechanism instead of just using Jest's built in?

Loadbalancer

How does this work in a load balanced environment?

Allow to provide a reporter to the Server

Hi,

I'm trying to add tests for my server class and to do that I need to pass a reporter mock. It's possible only with BaseServer, but Server class builds reporter on it's own.

class MyServer extends Server {
 ...
}

new MyServer(options, reporterMock) // it doesn't work

The solution could be to inherit from BaseServer and copy-paste the bootstrap code from Server

class MyServer extends BaseServer {
 ...copy paste Server code here
}

new MyServer(options, reporterMock) // it works!

What do you think about adding an option to override reporter in Server class as well? Or maybe compose Server with BaseServer instead of inherit from it?

Save context between steps

ctx should be saved between access/init/process steps.

But we need to add tests to avoid previous memory leaks.

Change the repo description

Build your own Logux server or make a proxy between a WebSocket and an HTTP backend in any language

P.S. We might still need a native speaker to review the final sentence.

Add verbose mode for the server to provides additional details

If we find a way to get detailed logs at server runtime, it will be much easier to develop some integration for Logux.

At my vision, for the start the server in the verbose mode should prints:

  • All requests to and from the proxy server (host, method, headers, etc)
  • All WS connections \ disconnections
  • Informative error messages

Problems with subscriptions

On the client side I use this:

client.log.add({ type: 'logux/subscribe', name: 'user/10' })

it fails and suggests to use sync, so I updated to:

client.log.add({ type: 'logux/subscribe', name: 'user/10' }, {sync: true})

and now it's rejected by the server as using unknown type here.

I thought it may be a bug, but there is spec treating this behavior as intended.

What am I doing wrong? Is there any example of successful subscription from client to server?

SQL storage

We need:

  1. sql-scheme.js to create/update tables and columns.
  2. bin/logux-sql to run sql-scheme.js in CLI
  3. sql-storage.js

Maximum call stack size exceeded

in this code

server.type('LOAD_NEXT_CANDLE', {
	async access(ctx) { return true },
	async resend(ctx) {
		console.log('resent')
		return 'candles-channel'
	},
	async process() {
		let candle = await CandleModel.findOne()

		server.log.add({ type: 'candles/new', candle }, { channels: ['candles-channel'] })
	}
})

when 'LOAD_NEXT_CANDLE' is dispatched on the client and the process is called on the server, I get this error:

 FATAL  Maximum call stack size exceeded at 2021-06-22 02:51:36
        at String.replace (<anonymous>)
        at cleanFromKeys (file:///path/to/project/node_modules/@logux/server/create-reporter/index.js:230:23)
        at cleanFromKeys (file:///path/to/project/node_modules/@logux/server/create-reporter/index.js:232:21)
        at cleanFromKeys (file:///path/to/project/node_modules/@logux/server/create-reporter/index.js:232:21)
        at cleanFromKeys (file:///path/to/project/node_modules/@logux/server/create-reporter/index.js:232:21)
        at cleanFromKeys (file:///path/to/project/node_modules/@logux/server/create-reporter/index.js:232:21)
        at cleanFromKeys (file:///path/to/project/node_modules/@logux/server/create-reporter/index.js:232:21)
        at cleanFromKeys (file:///path/to/project/node_modules/@logux/server/create-reporter/index.js:232:21)
        at cleanFromKeys (file:///path/to/project/node_modules/@logux/server/create-reporter/index.js:232:21)
        at cleanFromKeys (file:///path/to/project/node_modules/@logux/server/create-reporter/index.js:232:21)
        at cleanFromKeys (file:///path/to/project/node_modules/@logux/server/create-reporter/index.js:232:21)
        at cleanFromKeys (file:///path/to/project/node_modules/@logux/server/create-reporter/index.js:232:21)
        at cleanFromKeys (file:///path/to/project/node_modules/@logux/server/create-reporter/index.js:232:21)
        at cleanFromKeys (file:///path/to/project/node_modules/@logux/server/create-reporter/index.js:232:21)
        at cleanFromKeys (file:///path/to/project/node_modules/@logux/server/create-reporter/index.js:232:21)
        at cleanFromKeys (file:///path/to/project/node_modules/@logux/server/create-reporter/index.js:232:21)

If I comment out the line that is making a call to the Database and send a fake candle, there's no error

server.type('LOAD_NEXT_CANDLE', {
	async access(ctx) { return true },
	async resend(ctx) {
		console.log('resent')
		return 'candles-channel'

	},
	async process() {
		// let candle = await CandleModel.findOne()
		let candle = {
			"_id": "60d0fae173565825312222ca",
			"pair": "eurusd",
			"timeframe": "1h",
			"date": "2000.01.03",
			"time": "19:00",
			"o": 1.0205,
			"h": 1.024,
			"l": 1.015,
			"c": 1.025,
			"v": 355,
		}
		server.log.add({ type: 'candles/new', candle }, { channels: ['candles-channel'] })
	}
})

If I do make a query to the database but still send the fake candle to the client, there's no error:

server.type('LOAD_NEXT_CANDLE', {
	async access(ctx) { return true },
	async resend(ctx) {
		console.log('resent')
		return 'candles-channel'

	},
	async process() {
		let dbCandle = await CandleModel.findOne()
		let candle = {
			"_id": "60d0fae173565825312222ca",
			"pair": "eurusd",
			"timeframe": "1h",
			"date": "2000.01.03",
			"time": "19:00",
			"o": 1.0205,
			"h": 1.024,
			"l": 1.015,
			"c": 1.025,
			"v": 355,
		}
		server.log.add({ type: 'candles/new', candle }, { channels: ['candles-channel'] })
	}
})

how do I fix this?

HTTP proxy server rejects request with { "version": 3, ... }

Server info:

 INFO   Logux server is listening at 2020-04-16 15:34:20
        PID:            27948
        Logux server:   0.8.2
        Environment:    development
        Node ID:        server:-0z5HMNO
        Subprotocol:    1.0.0
        Supports:       1.x
        Control listen: http://127.0.0.1:31337/
        Control mask:   127.0.0.1/8
        Backend send:   http://localhost:8000/logux/
        Listen:         ws://127.0.0.1:31337/

Request:

{
  "version": 3,
  "secret": "secret",
  "commands": [
    [
      "action",
      {
        "type": "logux/subscribe",
        "channel": "user/38"
      },
      {
        "id": "1560954012858 38:Y7bysd:O0ETfc 0",
        "time": 1560954012858
      }
    ]
  ]
}

Answer: 400: Unknown version

Unstable acceptance test 'shows uncatch rejects'

Acceptance test 'shows uncatch rejects' in server.test.js has this snapshot:

 ERROR  Test Error at 1970-01-01 00:00:00
        fake stacktrace

Error event: Test Error
 INFO   Shutting down Logux server at 1970-01-01 00:00:00

But sometimes two last lines come in another order. That's because of race condition. We need to stabilise this test. Maybe with some timeout?

reporter enhancement request

I am using reporter.logger in my server code which accepts ( action, meta, msg ) params. I'd like to include other Objects in the Bunyan log.

Could an optional object param be added after msg for this purpose. Or is there some other way I can do this.

Right now I am merging my object with meta, which is not ideal.

Federatio support

Allow have multiple servers for different actions. Lik GraphQL federation.

Is logux good for real-time CRM & Asterisk integration?

Hi, Martians!
The question is in subject... logux looks very nice:

  1. I have cross tab communication by default. It's necessary when client gets incoming call, because user has many opened tabs, but I have to show only one notification or popup.

  2. I clearly separate actions by users. Users are just telephone extensions.

  3. Telephone events (Asterisk, AMI) push to client very often, but I want to know only last state, this option keepLast can help me

  4. Web Sockets inside, its is that i really needed

What disadvantages vs clear WS Server on NodeJS with custom logic + WS and React front? Speed? What else?
Thx

Hello from Antares

A friend of mine pointed me to your presentation at ReactConf. Your library and mine really try to solve similar problems.. https://github.com/deanius/antares

Some differences:

  • Antares actions sync by default, staying local with localOnly: true
  • I hide the dispatch inside announce to make this sync-by-default behavior clear
  • I bundle the Redux-Observable library to include Epics functionality via RxJS, and showcase with a WhatsApp-style chat app
  • You are more precise about timing and log sync, and I will likely borrow some of your ideas!

Let me know how you think I could help Logux - at the very least I will mention it as an alternative, much the way you mention Swarm.js. I think we have a lot of similar inspirations (yet, I'm jealous that you got to rap with Dan Abramov about it over drinks in St Petersberg - my visit there was fun but lacked that conversation ;) )

Dean

Allow to return void in addSyncMap

It may be necessary to skip actions under certain circumstances.

E.g. when creating sync map load() is called by the time it may be actually created in database.

addSyncMap({
  async load(ctx, id, since, action) {
    if (action.creating) {
      return;
    }

    // ....

    return {
      id: "foo",
      projectId: NoConflictResolution("bar"),
    };
  },
});

Add Server#autoloadModules

This method should be in Server, not BaseServer. It should:

  1. Take this.options.root and load all modules/* files.
  2. Each module will have server => void signature.

Allow keeping reasons of action from server

Mission

Synchronize data via channel and action with flow like:

  1. when client subscribed, send action in the channel method to send initial data to client
  2. having actions modifying the data and sync the action via the channel in 1

Problem

All the action send from server will got reasons filtered out before sending to client, so that client will not keep any log received from server.

This caused problem in a case that client and server simultaneously logging action in parallel.
e.g.
Client Sequence: C1, C2
Server Sequence: S1, S2
Absolute time sequence: C1 S1, S2, C2

The client got C1, C2 finished before receiving S1, S2 from server. If I understand correctly, when

  • S1 reach client, the client will replaceState to state at C1 and run S1, C2
  • S2 reach client, the client will replaceState to state at C1 and only run S2, C2

Since S1 has no any reasons in the client side, the log is not saved and the S1 change is missed.

Expectation

  • S2 reach client, the client will replaceState to state at S1 and run S2, C2

Notes

I am not sure if logux is designed to work like this, please tell if I have used it incorrectly.
(I found action send in channel method will not reach non-leader crossTab client, so maybe I am doing it wrong. Currently I patched to use Client instead of CrossTabClient)

Allowing reasons from server enable the server to hint the client how to manage the log, otherwise the client will need to define it themselves and it may not be the desired way to use the log.

For keeping clients log sync via channel, it require some reasoning too, we need some way to suggest how the log are kept.
Currently the sync server sendAction before the action is processed, so that we don't have chance to add any reasons in the action handler before sending out.
(as a workaround, I have locally temp enabled reasoning from client, since I am not sure if it is safe to move processAction before sendAction)

Since the above mission is common, if logux-server can do some automatic reasoning for channel, maybe we can keep custom reasons filtered out. Maybe related to this: #40

Security

It should be fine to allow reasons from server.
For allowing reasons from client, I can understand why it is filtered. If it is not filtered, client can flood the server storage.

Temp fix

I have locally do:

  1. Add reasons to ALLOWED_META both in logux-client and logux-server
  2. Add reasons and keepLast in server-client.js outMap
  3. Added outMap as config in logux-client/client.js
  4. Pass outMap to client config for filtering tab*, replay and waitForSync in logux-redux
    (4 sound problematic, I wish the reasons for log sycned via channel can be specify in server)

In my application:

  1. Send initial data in channel with channel name as keepLast
  2. Client send action to channel also with the channel name as reasons
    (maybe that can be generalized and do reason automatically)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.