spec-3nweb-server 1.8.8 → 1.8.10

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.
@@ -33,9 +33,9 @@ export interface InboxParams {
33
33
  "authenticated/invites": AuthSenderInvites;
34
34
  }
35
35
  export declare class Inbox extends UserFiles<InboxParams> {
36
- private metas;
37
- private objFiles;
38
36
  private mailEventsSink;
37
+ private readonly metas;
38
+ private readonly objFiles;
39
39
  constructor(userId: string, path: string, mailEventsSink: MailEventsSink, writeBufferSize?: string | number, readBufferSize?: string | number);
40
40
  static make(userFolder: string, userId: string, mailEventsSink: MailEventsSink, writeBufferSize?: string | number, readBufferSize?: string | number): Promise<Inbox>;
41
41
  /**
@@ -85,9 +85,9 @@ async function genMsgIdAndMakeFolder(delivPath, msgsFolder) {
85
85
  class Inbox extends user_files_1.UserFiles {
86
86
  constructor(userId, path, mailEventsSink, writeBufferSize, readBufferSize) {
87
87
  super(userId, path, writeBufferSize, readBufferSize);
88
+ this.mailEventsSink = mailEventsSink;
88
89
  this.metas = new MsgMetas(5 * 60 * 1000, (0, path_1.join)(this.path, 'delivery'), (0, path_1.join)(this.path, 'messages'));
89
90
  this.objFiles = new ObjFiles(5 * 60 * 1000, this.metas.deliveryFolder, this.metas.readyMsgsFolder);
90
- this.mailEventsSink = mailEventsSink;
91
91
  Object.freeze(this);
92
92
  }
93
93
  static async make(userFolder, userId, mailEventsSink, writeBufferSize, readBufferSize) {
@@ -23,7 +23,7 @@ const default_confs_1 = require("./default-confs");
23
23
  function readYamlConfFile(path) {
24
24
  const txt = (0, fs_1.readFileSync)(path, 'utf8');
25
25
  const conf = yaml.load(txt);
26
- if (conf.rootFolder) {
26
+ if (!conf.rootFolder) {
27
27
  conf.rootFolder = default_confs_1.DEFAULT_DATA_ROOT_PATH;
28
28
  }
29
29
  return conf;
@@ -17,7 +17,7 @@
17
17
  */
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
19
  exports.checkAndTransformAddress = exports.areAddressesEqual = exports.toCanonicalAddress = void 0;
20
- const punycode_1 = require("punycode");
20
+ const punycode_1 = require("punycode/");
21
21
  /**
22
22
  * @param address
23
23
  * @return a canonical form of a given address.
package/build/main.js CHANGED
@@ -24,6 +24,7 @@ const lib_1 = require("./lib");
24
24
  const signup_1 = require("./config/signup");
25
25
  const default_confs_1 = require("./config/default-confs");
26
26
  const file_1 = require("./lib-common/exceptions/file");
27
+ const qrcode = require("qrcode-terminal");
27
28
  async function run(conf) {
28
29
  if (!conf.servicesConnect) {
29
30
  console.error(`❌ No connection settings found in configuration.`);
@@ -142,6 +143,7 @@ async function displaySignupInfo(conf) {
142
143
  const signupLink = signupLinkFrom(conf, undefined);
143
144
  if (signupLink) {
144
145
  console.log(`Signup link:\n`, signupLink, `\n`);
146
+ displayAsQR(signupLink);
145
147
  }
146
148
  }
147
149
  else {
@@ -155,6 +157,10 @@ async function displaySignupInfo(conf) {
155
157
  console.log(`There are ${multiUserTokens.length} multi-user tokens.\n`);
156
158
  console.log(`There are ${singleUserTokens.length} single-user tokens.\n`);
157
159
  }
160
+ function displayAsQR(str) {
161
+ qrcode.setErrorLevel('Q');
162
+ qrcode.generate(str, { small: true });
163
+ }
158
164
  async function displayTokensList(conf) {
159
165
  const { multiUserTokens, singleUserTokens } = await (0, signup_1.readAllSignupTokens)(conf.rootFolder);
160
166
  if (multiUserTokens.length > 0) {
@@ -181,10 +187,6 @@ async function displayTokensList(conf) {
181
187
  async function displayTokenValue(conf, tokenId) {
182
188
  const ctx = await (0, signup_1.readTokenFile)(conf.rootFolder, tokenId);
183
189
  if (ctx) {
184
- const signupLink = signupLinkFrom(conf, ctx.token);
185
- if (signupLink) {
186
- console.log(`\nSignup link:\n ${signupLink}`);
187
- }
188
190
  if (ctx.type === 'multi-domain') {
189
191
  console.log(`\nMulti-user signup context:`);
190
192
  console.log(` domains:`, ctx.domains);
@@ -201,6 +203,11 @@ async function displayTokenValue(conf, tokenId) {
201
203
  console.log(` valid till: ${(new Date(ctx.validTill)).toString()}`);
202
204
  }
203
205
  }
206
+ const signupLink = signupLinkFrom(conf, ctx.token);
207
+ if (signupLink) {
208
+ console.log(`\nSignup link:\n ${signupLink}`);
209
+ displayAsQR(signupLink);
210
+ }
204
211
  console.log(``);
205
212
  }
206
213
  else {
@@ -241,6 +248,12 @@ async function createSingleUserToken(conf, userId) {
241
248
  console.log(``);
242
249
  }
243
250
  const cmd = (0, cli_1.parseProcessArgv)();
251
+ process.on('unhandledRejection', (err, p) => {
252
+ console.error(' -- Unhandled rejection of promise:', err);
253
+ });
254
+ process.on('uncaughtException', err => {
255
+ console.error(' -- Unhandled exception:', err);
256
+ });
244
257
  if (cmd.runCmd) {
245
258
  const conf = assembleConfig(cmd.runCmd.config);
246
259
  run(conf);
@@ -33,6 +33,7 @@ exports.specs.definition = (setup) => (() => {
33
33
  let deliveryUrl;
34
34
  let user1;
35
35
  let sessionId;
36
+ let session2;
36
37
  (0, async_jasmine_1.beforeEachAsync)(async () => {
37
38
  asmailServer = setup().asmailServer;
38
39
  const retrievalUrl = await asmailServer.getRetrievalUrl();
@@ -40,23 +41,33 @@ exports.specs.definition = (setup) => (() => {
40
41
  deliveryUrl = await asmailServer.getDeliveryUrl();
41
42
  user1 = setup().user1;
42
43
  sessionId = await (0, test_utils_1.startSession)(user1, retrievalUrl);
44
+ session2 = await (0, test_utils_1.startSession)(user1, retrievalUrl);
43
45
  });
44
46
  (0, async_jasmine_1.itAsync)(`requires session to open web socket`, async () => {
45
47
  let rep = await (0, ws_utils_1.openSocket)(wsUrl, 'invalid session id');
46
48
  expect(rep.status).withContext('status for missing authorized session').toBe(retrieval_1.ERR_SC.needAuth);
47
49
  });
48
50
  (0, async_jasmine_1.itAsync)(`emits event on completion of message reception`, async () => {
49
- let rep = await (0, ws_utils_1.openSocket)(wsUrl, sessionId);
50
- expect(rep.status).toBe(retrieval_1.wsEventChannel.SC.ok);
51
- const eventSrc = (0, ws_ipc_1.makeSubscriber)(rep.data, undefined);
52
- const eventPromise = rxjs_1.Observable.create(obs => eventSrc.subscribe(retrieval_1.msgRecievedCompletely.EVENT_NAME, obs))
53
- .take(1)
54
- .toPromise();
51
+ globalThis.logTest = true;
52
+ function subscribeAndWaitInSession(rep) {
53
+ expect(rep.status).toBe(retrieval_1.wsEventChannel.SC.ok);
54
+ const eventSrc = (0, ws_ipc_1.makeSubscriber)(rep.data, undefined);
55
+ return rxjs_1.Observable.create(obs => eventSrc.subscribe(retrieval_1.msgRecievedCompletely.EVENT_NAME, obs))
56
+ .take(1)
57
+ .toPromise();
58
+ }
59
+ const eventPromises = [
60
+ subscribeAndWaitInSession(await (0, ws_utils_1.openSocket)(wsUrl, sessionId)),
61
+ subscribeAndWaitInSession(await (0, ws_utils_1.openSocket)(wsUrl, session2)),
62
+ ];
55
63
  // give some time for subscription to occur
56
64
  await (0, processes_1.sleep)(10);
57
65
  const msgId = await (0, test_utils_1.sendMsg)(deliveryUrl, user1.id, test_utils_1.msg);
58
- const event = await eventPromise;
59
- expect(typeof event).toBe('object');
60
- expect(event.msgId).withContext(`message reception completion event should give a respective message id`).toBe(msgId);
66
+ for (const eventPromise of eventPromises) {
67
+ const event = await eventPromise;
68
+ expect(typeof event).toBe('object');
69
+ expect(event.msgId).withContext(`message reception completion event should give a respective message id`).toBe(msgId);
70
+ }
71
+ globalThis.logTest = false;
61
72
  });
62
73
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spec-3nweb-server",
3
- "version": "1.8.8",
3
+ "version": "1.8.10",
4
4
  "description": "3NWeb spec server.",
5
5
  "main": "build/lib.js",
6
6
  "types": "build/lib.d.ts",
@@ -41,7 +41,8 @@
41
41
  "express": "^4.22.2",
42
42
  "js-yaml": "^4.3.0",
43
43
  "prompts": "^2.4.2",
44
- "punycode": "^2.1.1",
44
+ "punycode": "^2.3.1",
45
+ "qrcode-terminal": "^0.12.0",
45
46
  "ws": "^7.5.11",
46
47
  "xsp-files": "^2.0.0"
47
48
  },
@@ -49,6 +50,7 @@
49
50
  "@types/express": "4.17.13",
50
51
  "@types/jasmine": "^3.9.1",
51
52
  "@types/node": "^16.11.7",
53
+ "@types/punycode": "^2.1.4",
52
54
  "@types/ws": "^4.0.0",
53
55
  "jasmine": "^3.9.0",
54
56
  "rxjs": "^5.3.0",
@@ -56,8 +56,7 @@ type AuthSenderInvites = configApi.p.authSenderInvites.List;
56
56
  type AnonSenderInvites = configApi.p.anonSenderInvites.List;
57
57
 
58
58
  type MsgMeta = retrievalApi.MsgMeta;
59
- export type MsgEvents = retrievalApi.msgMainObjRecieved.Event |
60
- retrievalApi.msgRecievedCompletely.Event;
59
+ export type MsgEvents = retrievalApi.msgMainObjRecieved.Event | retrievalApi.msgRecievedCompletely.Event;
61
60
 
62
61
  export type MailEventsSink = (
63
62
  userId: string, channel: string, event: MsgEvents
@@ -117,20 +116,16 @@ export interface InboxParams {
117
116
 
118
117
  export class Inbox extends UserFiles<InboxParams> {
119
118
 
120
- private metas = new MsgMetas(
121
- 5*60*1000, join(this.path, 'delivery'), join(this.path, 'messages'));
119
+ private readonly metas = new MsgMetas(5*60*1000, join(this.path, 'delivery'), join(this.path, 'messages'));
122
120
 
123
- private objFiles = new ObjFiles(
124
- 5*60*1000, this.metas.deliveryFolder, this.metas.readyMsgsFolder);
121
+ private readonly objFiles = new ObjFiles(5*60*1000, this.metas.deliveryFolder, this.metas.readyMsgsFolder);
125
122
 
126
- private mailEventsSink: MailEventsSink;
127
-
128
123
  constructor(
129
- userId: string, path: string, mailEventsSink: MailEventsSink,
124
+ userId: string, path: string,
125
+ private mailEventsSink: MailEventsSink,
130
126
  writeBufferSize?: string|number, readBufferSize?: string|number
131
127
  ) {
132
128
  super(userId, path, writeBufferSize, readBufferSize);
133
- this.mailEventsSink = mailEventsSink;
134
129
  Object.freeze(this);
135
130
  }
136
131
 
@@ -139,9 +134,7 @@ export class Inbox extends UserFiles<InboxParams> {
139
134
  writeBufferSize?: string|number, readBufferSize?: string|number
140
135
  ): Promise<Inbox> {
141
136
  const path = join(userFolder, 'mail');
142
- const inbox = new Inbox(
143
- userId, path, mailEventsSink, writeBufferSize, readBufferSize
144
- );
137
+ const inbox = new Inbox(userId, path, mailEventsSink, writeBufferSize, readBufferSize);
145
138
  await inbox.ensureUserExistsOnDisk();
146
139
  return inbox;
147
140
  }
@@ -285,9 +278,7 @@ export class Inbox extends UserFiles<InboxParams> {
285
278
  this.objFiles.changeToReadingPathsInMsg(msgId);
286
279
 
287
280
  // raise event about completion of receiving a message
288
- this.mailEventsSink(this.userId,
289
- retrievalApi.msgRecievedCompletely.EVENT_NAME,
290
- { msgId });
281
+ this.mailEventsSink(this.userId, retrievalApi.msgRecievedCompletely.EVENT_NAME, { msgId });
291
282
  }
292
283
 
293
284
  private areAllMsgObjsComplete(
@@ -317,9 +317,7 @@ export function makeRecipients(
317
317
  }
318
318
  } else {
319
319
  const userFolder = userDataInRootFolder(rootFolder, userId);
320
- inbox = await Inbox.make(
321
- userFolder, userId, mailEventsSink, writeBufferSize, readBufferSize
322
- );
320
+ inbox = await Inbox.make(userFolder, userId, mailEventsSink, writeBufferSize, readBufferSize);
323
321
  boxes.set(userId, inbox);
324
322
  return inbox;
325
323
  }
@@ -24,7 +24,7 @@ export function readYamlConfFile(path: string): Configurations {
24
24
  const txt = readFileSync(path, 'utf8');
25
25
  const conf = yaml.load(txt) as Configurations;
26
26
 
27
- if (conf.rootFolder) {
27
+ if (!conf.rootFolder) {
28
28
  conf.rootFolder = DEFAULT_DATA_ROOT_PATH;
29
29
  }
30
30
 
@@ -15,7 +15,7 @@
15
15
  this program. If not, see <http://www.gnu.org/licenses/>.
16
16
  */
17
17
 
18
- import { toASCII } from 'punycode';
18
+ import { toASCII } from 'punycode/';
19
19
 
20
20
  /**
21
21
  * @param address
package/ts-code/main.ts CHANGED
@@ -29,6 +29,7 @@ import { Configurations, servicesApp, accountsApp, AppWithWSs } from './lib';
29
29
  import { addMultiDomainSignup, addSingleUserSignup, readAllSignupTokens, readNoTokensFile, readTokenFile } from './config/signup';
30
30
  import { DEFAULT_CONFIG_PATH } from './config/default-confs';
31
31
  import { Code } from './lib-common/exceptions/file';
32
+ import * as qrcode from 'qrcode-terminal';
32
33
 
33
34
  async function run(conf: Configurations): Promise<void> {
34
35
 
@@ -159,6 +160,7 @@ async function displaySignupInfo(conf: Configurations): Promise<void> {
159
160
  const signupLink = signupLinkFrom(conf, undefined);
160
161
  if (signupLink) {
161
162
  console.log(`Signup link:\n`, signupLink, `\n`);
163
+ displayAsQR(signupLink);
162
164
  }
163
165
  } else {
164
166
  console.log(`Signup of users is allowed only with valid signup tokens.\n`);
@@ -174,6 +176,11 @@ async function displaySignupInfo(conf: Configurations): Promise<void> {
174
176
  console.log(`There are ${singleUserTokens.length} single-user tokens.\n`);
175
177
  }
176
178
 
179
+ function displayAsQR(str: string): void {
180
+ qrcode.setErrorLevel('Q');
181
+ qrcode.generate(str, { small: true });
182
+ }
183
+
177
184
  async function displayTokensList(conf: Configurations): Promise<void> {
178
185
  const {
179
186
  multiUserTokens, singleUserTokens
@@ -203,10 +210,6 @@ async function displayTokenValue(
203
210
  ): Promise<void> {
204
211
  const ctx = await readTokenFile(conf.rootFolder, tokenId);
205
212
  if (ctx) {
206
- const signupLink = signupLinkFrom(conf, ctx.token);
207
- if (signupLink) {
208
- console.log(`\nSignup link:\n ${signupLink}`);
209
- }
210
213
  if (ctx.type === 'multi-domain') {
211
214
  console.log(`\nMulti-user signup context:`);
212
215
  console.log(` domains:`, ctx.domains);
@@ -222,6 +225,11 @@ async function displayTokenValue(
222
225
  console.log(` valid till: ${(new Date(ctx.validTill)).toString()}`);
223
226
  }
224
227
  }
228
+ const signupLink = signupLinkFrom(conf, ctx.token);
229
+ if (signupLink) {
230
+ console.log(`\nSignup link:\n ${signupLink}`);
231
+ displayAsQR(signupLink);
232
+ }
225
233
  console.log(``);
226
234
  } else {
227
235
  console.error(`Token ${tokenId} is not found.`);
@@ -269,6 +277,13 @@ async function createSingleUserToken(
269
277
 
270
278
  const cmd = parseProcessArgv();
271
279
 
280
+ process.on('unhandledRejection', (err, p) => {
281
+ console.error(' -- Unhandled rejection of promise:', err);
282
+ });
283
+ process.on('uncaughtException', err => {
284
+ console.error(' -- Unhandled exception:', err);
285
+ });
286
+
272
287
  if (cmd.runCmd) {
273
288
  const conf = assembleConfig(cmd.runCmd.config!);
274
289
  run(conf);
@@ -22,8 +22,7 @@ import { defer } from '../../lib-common/processes';
22
22
  import { makeConnectionException } from '../../lib-common/exceptions/http';
23
23
  import { globalAgent as agent } from 'https';
24
24
 
25
- export function openSocket(url: string, sessionId: string):
26
- Promise<Reply<WebSocket>> {
25
+ export function openSocket(url: string, sessionId: string): Promise<Reply<WebSocket>> {
27
26
  if (!url.startsWith('wss://')) { throw new Error(
28
27
  `Url protocol must be wss`); }
29
28
  const headers: any = {};
@@ -34,6 +34,7 @@ specs.definition = (setup: () => TestSetup) => (() => {
34
34
  let deliveryUrl: string;
35
35
  let user1: User;
36
36
  let sessionId: string;
37
+ let session2: string;
37
38
 
38
39
  beforeEachAsync(async () => {
39
40
  asmailServer = setup().asmailServer;
@@ -42,6 +43,7 @@ specs.definition = (setup: () => TestSetup) => (() => {
42
43
  deliveryUrl = await asmailServer.getDeliveryUrl();
43
44
  user1 = setup().user1;
44
45
  sessionId = await startSession(user1, retrievalUrl);
46
+ session2 = await startSession(user1, retrievalUrl);
45
47
  });
46
48
 
47
49
  itAsync(`requires session to open web socket`, async () => {
@@ -53,26 +55,39 @@ specs.definition = (setup: () => TestSetup) => (() => {
53
55
 
54
56
  itAsync(`emits event on completion of message reception`, async () => {
55
57
 
56
- let rep = await openSocket(wsUrl, sessionId);
57
- expect(rep.status).toBe(api.SC.ok);
58
-
59
- const eventSrc = makeSubscriber(rep.data, undefined);
58
+ globalThis.logTest = true;
59
+
60
+ function subscribeAndWaitInSession(rep: Awaited<ReturnType<typeof openSocket>>) {
61
+ expect(rep.status).toBe(api.SC.ok);
62
+
63
+ const eventSrc = makeSubscriber(rep.data, undefined);
64
+
65
+ return (Observable.create(obs => eventSrc.subscribe<msgRecievedCompletely.Event>(
66
+ msgRecievedCompletely.EVENT_NAME, obs
67
+ )) as Observable<msgRecievedCompletely.Event>)
68
+ .take(1)
69
+ .toPromise();
70
+ }
60
71
 
61
- const eventPromise = (Observable.create(
62
- obs => eventSrc.subscribe<msgRecievedCompletely.Event>(
63
- msgRecievedCompletely.EVENT_NAME, obs)) as Observable<msgRecievedCompletely.Event>)
64
- .take(1)
65
- .toPromise();
72
+ const eventPromises = [
73
+ subscribeAndWaitInSession(await openSocket(wsUrl, sessionId)),
74
+ subscribeAndWaitInSession(await openSocket(wsUrl, session2)),
75
+ ];
66
76
 
67
77
  // give some time for subscription to occur
68
78
  await sleep(10);
69
79
 
70
80
  const msgId = await sendMsg(deliveryUrl, user1.id, msg);
71
81
 
72
- const event = await eventPromise;
73
- expect(typeof event).toBe('object');
74
- expect(event.msgId).withContext(`message reception completion event should give a respective message id`).toBe(msgId);
82
+ for (const eventPromise of eventPromises) {
83
+ const event = await eventPromise;
84
+ expect(typeof event).toBe('object');
85
+ expect(event.msgId).withContext(
86
+ `message reception completion event should give a respective message id`
87
+ ).toBe(msgId);
88
+ }
75
89
 
90
+ globalThis.logTest = false;
76
91
  });
77
92
 
78
93
  });