@logux/server 0.12.3 → 0.12.5

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,8 +1,8 @@
1
1
  import { LoguxError } from '@logux/core'
2
+ import http from 'http'
3
+ import https from 'https'
2
4
  import JSONStream from 'JSONStream'
3
5
  import { nanoid } from 'nanoid'
4
- import https from 'https'
5
- import http from 'http'
6
6
 
7
7
  const VERSION = 4
8
8
 
@@ -19,21 +19,21 @@ const RESEND_KEYS = [
19
19
 
20
20
  function send(backend, command, events) {
21
21
  let body = JSON.stringify({
22
- version: VERSION,
23
22
  commands: [command],
24
- secret: backend.secret
23
+ secret: backend.secret,
24
+ version: VERSION
25
25
  })
26
26
  let protocol = backend.protocol === 'https:' ? https : http
27
27
  let req = protocol.request(
28
28
  {
29
- method: 'POST',
29
+ headers: {
30
+ 'Content-Length': Buffer.byteLength(body),
31
+ 'Content-Type': 'application/json'
32
+ },
30
33
  host: backend.hostname,
31
- port: backend.port,
34
+ method: 'POST',
32
35
  path: backend.pathname + backend.search,
33
- headers: {
34
- 'Content-Type': 'application/json',
35
- 'Content-Length': Buffer.byteLength(body)
36
- }
36
+ port: backend.port
37
37
  },
38
38
  res => {
39
39
  if (res.statusCode < 200 || res.statusCode > 299) {
@@ -83,20 +83,20 @@ function send(backend, command, events) {
83
83
  export function bindBackendProxy(app) {
84
84
  if (app.options.controlSecret) {
85
85
  app.controls['POST /'] = {
86
- isValid({ command, action, meta }) {
87
- return (
88
- command === 'action' &&
89
- typeof action === 'object' &&
90
- typeof action.type === 'string' &&
91
- typeof meta === 'object'
92
- )
93
- },
94
86
  command({ action, meta }, req) {
95
87
  if (!app.types[action.type] && !app.getRegexProcessor(action.type)) {
96
88
  meta.status = 'processed'
97
89
  }
98
90
  meta.backend = req.connection.remoteAddress
99
91
  return app.log.add(action, meta)
92
+ },
93
+ isValid({ action, command, meta }) {
94
+ return (
95
+ command === 'action' &&
96
+ typeof action === 'object' &&
97
+ typeof action.type === 'string' &&
98
+ typeof meta === 'object'
99
+ )
100
100
  }
101
101
  }
102
102
  }
@@ -159,28 +159,8 @@ export function bindBackendProxy(app) {
159
159
  app.emitter.emit('backendSent', action, meta)
160
160
  send(
161
161
  backend,
162
- { command: 'action', action, meta, headers },
162
+ { action, command: 'action', headers, meta },
163
163
  {
164
- filter({ id }) {
165
- return id === meta.id
166
- },
167
- resend(answer) {
168
- if (checked) {
169
- error = true
170
- currentReject(new Error('Resend answer was sent after access'))
171
- } else if (action.type === 'logux/subscribe') {
172
- error = true
173
- accessReject(new Error('Resend can be called on subscription'))
174
- } else {
175
- let resend = {}
176
- for (let key of RESEND_KEYS) {
177
- if (typeof answer[key] !== 'undefined') {
178
- resend[key] = answer[key]
179
- }
180
- }
181
- resendResolve(resend)
182
- }
183
- },
184
164
  action(data) {
185
165
  let promise = app.log.add(data.action, {
186
166
  status: 'processed',
@@ -198,6 +178,18 @@ export function bindBackendProxy(app) {
198
178
  checked = true
199
179
  accessResolve(true)
200
180
  },
181
+ end() {
182
+ if (!error && (!checked || !processed)) {
183
+ currentReject(new Error('Back-end do not send required answers'))
184
+ }
185
+ },
186
+ error(e) {
187
+ error = true
188
+ currentReject(e)
189
+ },
190
+ filter({ id }) {
191
+ return id === meta.id
192
+ },
201
193
  forbidden() {
202
194
  if (resendResolve) resendResolve()
203
195
  error = true
@@ -219,6 +211,23 @@ export function bindBackendProxy(app) {
219
211
  processResolve()
220
212
  }
221
213
  },
214
+ resend(answer) {
215
+ if (checked) {
216
+ error = true
217
+ currentReject(new Error('Resend answer was sent after access'))
218
+ } else if (action.type === 'logux/subscribe') {
219
+ error = true
220
+ accessReject(new Error('Resend can be called on subscription'))
221
+ } else {
222
+ let resend = {}
223
+ for (let key of RESEND_KEYS) {
224
+ if (typeof answer[key] !== 'undefined') {
225
+ resend[key] = answer[key]
226
+ }
227
+ }
228
+ resendResolve(resend)
229
+ }
230
+ },
222
231
  unknownAction() {
223
232
  resendResolve()
224
233
  error = true
@@ -229,47 +238,27 @@ export function bindBackendProxy(app) {
229
238
  error = true
230
239
  app.wrongChannel(action, meta)
231
240
  accessResolve(false)
232
- },
233
- error(e) {
234
- error = true
235
- currentReject(e)
236
- },
237
- end() {
238
- if (!error && (!checked || !processed)) {
239
- currentReject(new Error('Back-end do not send required answers'))
240
- }
241
241
  }
242
242
  }
243
243
  )
244
244
  }
245
245
 
246
246
  app.auth(
247
- ({ userId, headers, cookie, token, client }) =>
247
+ ({ client, cookie, headers, token, userId }) =>
248
248
  new Promise((resolve, reject) => {
249
249
  let random = nanoid()
250
250
  send(
251
251
  backend,
252
252
  {
253
- command: 'auth',
254
253
  authId: random,
255
- userId,
256
- token,
257
- headers,
254
+ command: 'auth',
258
255
  cookie,
259
- subprotocol: client.node.remoteSubprotocol
256
+ headers,
257
+ subprotocol: client.node.remoteSubprotocol,
258
+ token,
259
+ userId
260
260
  },
261
261
  {
262
- filter({ authId }) {
263
- return random === authId
264
- },
265
- wrongSubprotocol({ supported }) {
266
- reject(
267
- new LoguxError('wrong-subprotocol', {
268
- used: client.node.remoteSubprotocol,
269
- supported
270
- })
271
- )
272
- },
273
262
  authenticated({ subprotocol }) {
274
263
  if (subprotocol) {
275
264
  app.options.subprotocol = subprotocol
@@ -282,6 +271,17 @@ export function bindBackendProxy(app) {
282
271
  },
283
272
  error(e) {
284
273
  reject(e)
274
+ },
275
+ filter({ authId }) {
276
+ return random === authId
277
+ },
278
+ wrongSubprotocol({ supported }) {
279
+ reject(
280
+ new LoguxError('wrong-subprotocol', {
281
+ supported,
282
+ used: client.node.remoteSubprotocol
283
+ })
284
+ )
285
285
  }
286
286
  }
287
287
  )
@@ -292,20 +292,20 @@ export function bindBackendProxy(app) {
292
292
  sendAction(action, meta, ctx.headers)
293
293
  return accessing.get(meta.id)
294
294
  },
295
- resend(ctx, action, meta) {
296
- if (!resending.has(meta.id)) {
297
- sendAction(action, meta, ctx.headers)
298
- }
299
- return resending.get(meta.id)
300
- },
301
- process(ctx, action, meta) {
302
- return processing.get(meta.id)
303
- },
304
295
  finally(ctx, action, meta) {
305
296
  actions.delete(meta.id)
306
297
  resending.delete(meta.id)
307
298
  accessing.delete(meta.id)
308
299
  processing.delete(meta.id)
300
+ },
301
+ process(ctx, action, meta) {
302
+ return processing.get(meta.id)
303
+ },
304
+ resend(ctx, action, meta) {
305
+ if (!resending.has(meta.id)) {
306
+ sendAction(action, meta, ctx.headers)
307
+ }
308
+ return resending.get(meta.id)
309
309
  }
310
310
  })
311
311
  app.otherChannel({
@@ -313,13 +313,13 @@ export function bindBackendProxy(app) {
313
313
  sendAction(action, meta, ctx.headers)
314
314
  return accessing.get(meta.id)
315
315
  },
316
- load(ctx, action, meta) {
317
- return processing.get(meta.id)
318
- },
319
316
  finally(ctx, action, meta) {
320
317
  actions.delete(meta.id)
321
318
  accessing.delete(meta.id)
322
319
  processing.delete(meta.id)
320
+ },
321
+ load(ctx, action, meta) {
322
+ return processing.get(meta.id)
323
323
  }
324
324
  })
325
325
  }
@@ -20,7 +20,7 @@ export function bindControlServer(app, custom) {
20
20
  if (app.options.disableHttpServer) {
21
21
  if (app.options.controlSecret) {
22
22
  let err = new Error(
23
- '`controlSecret` can be set together with `disableHttpServer` option'
23
+ '`controlSecret` can not be set together with `disableHttpServer` option'
24
24
  )
25
25
  err.logux = true
26
26
  throw err
@@ -1,4 +1,5 @@
1
1
  import type { AnyAction } from '@logux/core'
2
+
2
3
  import type { ServerMeta } from '../base-server/index.js'
3
4
  import type { Server } from '../server/index.js'
4
5
 
@@ -15,14 +16,13 @@ import type { Server } from '../server/index.js'
15
16
  */
16
17
  export class Context<Data extends object = {}, Headers extends object = {}> {
17
18
  /**
18
- * @param nodeId Unique node ID.
19
- * @param clientId Unique persistence client ID.
20
- * @param userId User ID taken node ID.
21
- * @param subprotocol Action creator application subprotocol version
22
- * in SemVer format.
23
- * @param server Logux server
19
+ * Unique persistence client ID.
20
+ *
21
+ * ```js
22
+ * server.clientIds.get(node.clientId)
23
+ * ```
24
24
  */
25
- constructor(server: Server, meta: ServerMeta)
25
+ clientId: string
26
26
 
27
27
  /**
28
28
  * Open structure to save some data between different steps of processing.
@@ -54,43 +54,27 @@ export class Context<Data extends object = {}, Headers extends object = {}> {
54
54
  headers: Headers
55
55
 
56
56
  /**
57
- * Unique node ID.
58
- *
59
- * ```js
60
- * server.nodeIds.get(node.nodeId)
61
- * ```
62
- */
63
- nodeId: string
64
-
65
- /**
66
- * User ID taken node ID.
57
+ * Was action created by Logux server.
67
58
  *
68
59
  * ```js
69
- * async access (ctx, action, meta) {
70
- * const user = await db.getUser(ctx.userId)
71
- * return user.admin
72
- * }
60
+ * access: (ctx, action, meta) => ctx.isServer
73
61
  * ```
74
62
  */
75
- userId: 'server' | string
63
+ isServer: boolean
76
64
 
77
65
  /**
78
- * Unique persistence client ID.
66
+ * Unique node ID.
79
67
  *
80
68
  * ```js
81
- * server.clientIds.get(node.clientId)
69
+ * server.nodeIds.get(node.nodeId)
82
70
  * ```
83
71
  */
84
- clientId: string
72
+ nodeId: string
85
73
 
86
74
  /**
87
- * Was action created by Logux server.
88
- *
89
- * ```js
90
- * access: (ctx, action, meta) => ctx.isServer
91
- * ```
75
+ * Logux server
92
76
  */
93
- isServer: boolean
77
+ server: Server
94
78
 
95
79
  /**
96
80
  * Action creator application subprotocol version in SemVer format.
@@ -99,9 +83,26 @@ export class Context<Data extends object = {}, Headers extends object = {}> {
99
83
  subprotocol: string
100
84
 
101
85
  /**
102
- * Logux server
86
+ * User ID taken node ID.
87
+ *
88
+ * ```js
89
+ * async access (ctx, action, meta) {
90
+ * const user = await db.getUser(ctx.userId)
91
+ * return user.admin
92
+ * }
93
+ * ```
103
94
  */
104
- server: Server
95
+ userId: 'server' | string
96
+
97
+ /**
98
+ * @param nodeId Unique node ID.
99
+ * @param clientId Unique persistence client ID.
100
+ * @param userId User ID taken node ID.
101
+ * @param subprotocol Action creator application subprotocol version
102
+ * in SemVer format.
103
+ * @param server Logux server
104
+ */
105
+ constructor(server: Server, meta: ServerMeta)
105
106
 
106
107
  /**
107
108
  * Check creator subprotocol version. It uses `semver` npm package
package/context/index.js CHANGED
@@ -33,8 +33,8 @@ export class Context {
33
33
 
34
34
  sendBack(action, meta = {}) {
35
35
  return this.server.log.add(action, {
36
- status: 'processed',
37
36
  clients: [this.clientId],
37
+ status: 'processed',
38
38
  ...meta
39
39
  })
40
40
  }
@@ -1,7 +1,7 @@
1
- import { isAbsolute, join } from 'path'
2
1
  import { promises as fs } from 'fs'
3
- import https from 'https'
4
2
  import http from 'http'
3
+ import https from 'https'
4
+ import { isAbsolute, join } from 'path'
5
5
 
6
6
  const PEM_PREAMBLE = '-----BEGIN'
7
7
 
@@ -30,9 +30,9 @@ export async function createHttpServer(opts) {
30
30
  if (cert && !isPem(cert)) cert = await readFrom(opts.root, cert)
31
31
 
32
32
  if (key && key.pem) {
33
- server = https.createServer({ key: key.pem, cert })
33
+ server = https.createServer({ cert, key: key.pem })
34
34
  } else if (key) {
35
- server = https.createServer({ key, cert })
35
+ server = https.createServer({ cert, key })
36
36
  } else {
37
37
  server = http.createServer()
38
38
  }
@@ -1,7 +1,7 @@
1
- import pino from 'pino'
2
1
  import os from 'os'
3
2
  import { dirname, join, sep } from 'path'
4
3
  import pico from 'picocolors'
4
+ import pino from 'pino'
5
5
  import { fileURLToPath } from 'url'
6
6
 
7
7
  const __dirname = dirname(fileURLToPath(import.meta.url))
@@ -11,19 +11,34 @@ export const PATH_TO_PRETTIFYING_PINO_TRANSPORT = join(
11
11
  )
12
12
 
13
13
  const ERROR_CODES = {
14
+ EACCES: (e, environment) => {
15
+ let wayToFix = {
16
+ development:
17
+ 'In dev mode it can be done with sudo:\n' + '$ sudo npm start',
18
+ production: '$ su - `<username>`\n' + `$ npm start -p ${e.port}`
19
+ }
20
+
21
+ return {
22
+ msg: `You are not allowed to run server on port \`${e.port}\``,
23
+ note:
24
+ "Non-privileged users can't start a listening socket on ports " +
25
+ 'below 1024. Try to change user or take another port.\n\n' +
26
+ (wayToFix[environment] || wayToFix.production)
27
+ }
28
+ },
14
29
  EADDRINUSE: e => {
15
30
  let wayToFix = {
16
- win32:
17
- 'Run `cmd.exe` as an administrator\n' +
18
- 'C:\\> netstat -a -b -n -o\n' +
19
- 'C:\\> taskkill /F /PID `<processid>`',
20
31
  darwin: `$ sudo lsof -i ${e.port}\n` + '$ sudo kill -9 `<processid>`',
21
32
  linux:
22
33
  '$ su - root\n' +
23
34
  `# netstat -nlp | grep ${e.port}\n` +
24
35
  'Proto Local Address State PID/Program name\n' +
25
36
  `tcp 0.0.0.0:${e.port} LISTEN \`777\`/node\n` +
26
- '# sudo kill -9 `777`'
37
+ '# sudo kill -9 `777`',
38
+ win32:
39
+ 'Run `cmd.exe` as an administrator\n' +
40
+ 'C:\\> netstat -a -b -n -o\n' +
41
+ 'C:\\> taskkill /F /PID `<processid>`'
27
42
  }
28
43
 
29
44
  return {
@@ -35,21 +50,6 @@ const ERROR_CODES = {
35
50
  (wayToFix[os.platform()] || '')
36
51
  }
37
52
  },
38
- EACCES: (e, environment) => {
39
- let wayToFix = {
40
- development:
41
- 'In dev mode it can be done with sudo:\n' + '$ sudo npm start',
42
- production: '$ su - `<username>`\n' + `$ npm start -p ${e.port}`
43
- }
44
-
45
- return {
46
- msg: `You are not allowed to run server on port \`${e.port}\``,
47
- note:
48
- "Non-privileged users can't start a listening socket on ports " +
49
- 'below 1024. Try to change user or take another port.\n\n' +
50
- (wayToFix[environment] || wayToFix.production)
51
- }
52
- },
53
53
  LOGUX_NO_CONTROL_SECRET: e => ({
54
54
  msg: e.message,
55
55
  note:
@@ -59,10 +59,81 @@ const ERROR_CODES = {
59
59
  }
60
60
 
61
61
  const REPORTERS = {
62
+ add: () => ({ msg: 'Action was added' }),
63
+
64
+ addClean: () => ({ msg: 'Action was added and cleaned' }),
65
+
66
+ authenticated: () => ({ msg: 'User was authenticated' }),
67
+
68
+ clean: () => ({ msg: 'Action was cleaned' }),
69
+
70
+ clientError: record => {
71
+ let result = {
72
+ details: {},
73
+ level: 'warn'
74
+ }
75
+ if (record.err.received) {
76
+ result.msg = `Client error: ${record.err.description}`
77
+ } else {
78
+ result.msg = `Sync error: ${record.err.description}`
79
+ }
80
+ for (let i in record) {
81
+ if (i !== 'err') {
82
+ result.details[i] = record[i]
83
+ }
84
+ }
85
+ return result
86
+ },
87
+
88
+ connect: () => ({ msg: 'Client was connected' }),
89
+
90
+ denied: () => ({ level: 'warn', msg: 'Action was denied' }),
91
+
92
+ destroy: () => ({ msg: 'Shutting down Logux server' }),
93
+
94
+ disconnect: () => ({ msg: 'Client was disconnected' }),
95
+
96
+ error: record => {
97
+ let result = {
98
+ details: {
99
+ err: {
100
+ message: record.err.message,
101
+ name: record.err.name,
102
+ stack: record.err.stack
103
+ }
104
+ },
105
+ level: record.fatal ? 'fatal' : 'error',
106
+ msg: record.err.message
107
+ }
108
+
109
+ let helper = ERROR_CODES[record.err.code]
110
+ if (helper) {
111
+ let help = helper(record.err, record.environment)
112
+ result.msg = help.msg
113
+ result.details.note = help.note
114
+ delete result.details.err.stack
115
+ } else if (record.err.logux) {
116
+ result.details.note = record.err.note
117
+ delete result.details.err
118
+ }
119
+
120
+ if (record.err.name === 'LoguxError') {
121
+ delete result.details.err.stack
122
+ }
123
+
124
+ for (let i in record) {
125
+ if (i !== 'err' && i !== 'fatal') {
126
+ result.details[i] = record[i]
127
+ }
128
+ }
129
+
130
+ return result
131
+ },
132
+
62
133
  listen: r => {
63
134
  let details = {
64
- loguxServer: r.loguxServer,
65
135
  environment: r.environment,
136
+ loguxServer: r.loguxServer,
66
137
  nodeId: r.nodeId,
67
138
  subprotocol: r.subprotocol,
68
139
  supports: r.supports
@@ -99,38 +170,25 @@ const REPORTERS = {
99
170
 
100
171
  for (let i in r.notes) details[i] = r.notes[i]
101
172
 
102
- return { msg: 'Logux server is listening', details }
173
+ return { details, msg: 'Logux server is listening' }
103
174
  },
104
175
 
105
- connect: () => ({ msg: 'Client was connected' }),
106
-
107
- authenticated: () => ({ msg: 'User was authenticated' }),
108
-
109
- disconnect: () => ({ msg: 'Client was disconnected' }),
110
-
111
- destroy: () => ({ msg: 'Shutting down Logux server' }),
112
-
113
- add: () => ({ msg: 'Action was added' }),
114
-
115
- addClean: () => ({ msg: 'Action was added and cleaned' }),
116
-
117
- clean: () => ({ msg: 'Action was cleaned' }),
118
-
119
176
  subscribed: () => ({ msg: 'Client was subscribed' }),
120
177
 
121
- unsubscribed: () => ({ msg: 'Client was unsubscribed' }),
122
-
123
178
  unauthenticated: () => ({ level: 'warn', msg: 'Bad authentication' }),
124
179
 
125
- useless: () => ({ level: 'warn', msg: 'Useless action' }),
180
+ unknownType: record => ({
181
+ level: /^ server(:| )/.test(record.actionId) ? 'error' : 'warn',
182
+ msg: 'Action with unknown type'
183
+ }),
126
184
 
127
- denied: () => ({ level: 'warn', msg: 'Action was denied' }),
185
+ unsubscribed: () => ({ msg: 'Client was unsubscribed' }),
128
186
 
129
- zombie: () => ({ level: 'warn', msg: 'Zombie client was disconnected' }),
187
+ useless: () => ({ level: 'warn', msg: 'Useless action' }),
130
188
 
131
- wrongControlSecret: () => ({
189
+ wrongChannel: () => ({
132
190
  level: 'warn',
133
- msg: 'Wrong secret in control request'
191
+ msg: 'Wrong channel name'
134
192
  }),
135
193
 
136
194
  wrongControlIp: () => ({
@@ -138,70 +196,12 @@ const REPORTERS = {
138
196
  msg: 'IP address of control request do not pass the mask'
139
197
  }),
140
198
 
141
- unknownType: record => ({
142
- level: /^ server(:| )/.test(record.actionId) ? 'error' : 'warn',
143
- msg: 'Action with unknown type'
144
- }),
145
-
146
- wrongChannel: () => ({
199
+ wrongControlSecret: () => ({
147
200
  level: 'warn',
148
- msg: 'Wrong channel name'
201
+ msg: 'Wrong secret in control request'
149
202
  }),
150
203
 
151
- clientError: record => {
152
- let result = {
153
- level: 'warn',
154
- details: {}
155
- }
156
- if (record.err.received) {
157
- result.msg = `Client error: ${record.err.description}`
158
- } else {
159
- result.msg = `Sync error: ${record.err.description}`
160
- }
161
- for (let i in record) {
162
- if (i !== 'err') {
163
- result.details[i] = record[i]
164
- }
165
- }
166
- return result
167
- },
168
-
169
- error: record => {
170
- let result = {
171
- level: record.fatal ? 'fatal' : 'error',
172
- msg: record.err.message,
173
- details: {
174
- err: {
175
- message: record.err.message,
176
- name: record.err.name,
177
- stack: record.err.stack
178
- }
179
- }
180
- }
181
-
182
- let helper = ERROR_CODES[record.err.code]
183
- if (helper) {
184
- let help = helper(record.err, record.environment)
185
- result.msg = help.msg
186
- result.details.note = help.note
187
- delete result.details.err.stack
188
- } else if (record.err.logux) {
189
- result.details.note = record.err.note
190
- delete result.details.err
191
- }
192
-
193
- if (record.err.name === 'LoguxError') {
194
- delete result.details.err.stack
195
- }
196
-
197
- for (let i in record) {
198
- if (i !== 'err' && i !== 'fatal') {
199
- result.details[i] = record[i]
200
- }
201
- }
202
-
203
- return result
204
- }
204
+ zombie: () => ({ level: 'warn', msg: 'Zombie client was disconnected' })
205
205
  }
206
206
 
207
207
  function createLogger(options) {
@@ -214,12 +214,12 @@ function createLogger(options) {
214
214
 
215
215
  let logger = pino(
216
216
  pino.transport({
217
- target: PATH_TO_PRETTIFYING_PINO_TRANSPORT,
218
217
  options: {
219
218
  basepath,
220
219
  color,
221
220
  destination: options.logger.destination
222
- }
221
+ },
222
+ target: PATH_TO_PRETTIFYING_PINO_TRANSPORT
223
223
  })
224
224
  )
225
225