spec-3nweb-server 1.8.0 → 1.8.2

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,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- Copyright (C) 2015 - 2017, 2022, 2024 3NSoft Inc.
3
+ Copyright (C) 2015 - 2017, 2022, 2024 - 2025 3NSoft Inc.
4
4
 
5
5
  This program is free software: you can redistribute it and/or modify it under
6
6
  the terms of the GNU General Public License as published by the Free Software
@@ -33,10 +33,10 @@ const archive_obj_version_1 = require("./routes/owner/archive-obj-version");
33
33
  const get_obj_status_1 = require("./routes/owner/get-obj-status");
34
34
  const api = require("../lib-common/service-api/3nstorage/owner");
35
35
  const mid_access_1 = require("../lib-server/mid-access");
36
+ const WS_PING_PERIOD_MILLIS = 2 * 60 * 1000;
36
37
  function makeOwnerStorageApp(domain, sessions, users, midAuthorizer) {
37
38
  const app = new app_1.AppWithWSs();
38
- const setupLoginRoutes = (app, urlPrefix) => (0, mid_access_1.addMailerIdLoginRoutes)(app, domain, urlPrefix, sessions, users.exists, midAuthorizer);
39
- setHttpPart(app.http, setupLoginRoutes, sessions, users);
39
+ setHttpPart(app.http, domain, sessions, users, midAuthorizer);
40
40
  setWSPart(app, sessions, users);
41
41
  return app;
42
42
  }
@@ -55,7 +55,7 @@ function makeOwnerStorageForLocker(sessions, users) {
55
55
  }
56
56
  exports.makeOwnerStorageForLocker = makeOwnerStorageForLocker;
57
57
  function setWSPart(app, sessions, users) {
58
- const sockets = new app_1.UserSockets(sessions.ensureAuthorizedSessionForSocketStart());
58
+ const sockets = new app_1.UserSockets(sessions.ensureAuthorizedSessionForSocketStart(), WS_PING_PERIOD_MILLIS);
59
59
  const storageEvents = new server_events_1.ServerEvents(undefined, api.events.all, sockets.socketGetter);
60
60
  // give events ipc to both ends
61
61
  sockets.addSocketIPC(storageEvents);
@@ -63,10 +63,10 @@ function setWSPart(app, sessions, users) {
63
63
  app.addWS(api.wsEventChannel.URL_END, sockets);
64
64
  }
65
65
  const MAX_CHUNK_SIZE = '0.5mb';
66
- function setHttpPart(app, setupLoginRoutes, sessions, users) {
66
+ function setHttpPart(app, domain, sessions, users, midAuthorizer) {
67
67
  app.disable('etag');
68
68
  // Login
69
- setupLoginRoutes(app, '/' + api.midLogin.URL_PART);
69
+ (0, mid_access_1.addMailerIdLoginRoutes)(app, domain, '/' + api.midLogin.URL_PART, sessions, users.exists, midAuthorizer);
70
70
  // *** Require authorized session for everything below ***
71
71
  app.use(sessions.ensureAuthorizedSession());
72
72
  app.post('/' + api.closeSession.URL_END, (0, body_parsers_1.emptyBody)(), (0, close_1.closeSession)());
@@ -27,6 +27,7 @@ const remove_message_1 = require("./routes/retrieval/remove-message");
27
27
  const get_message_obj_1 = require("./routes/retrieval/get-message-obj");
28
28
  const api = require("../lib-common/service-api/asmail/retrieval");
29
29
  const mid_access_1 = require("../lib-server/mid-access");
30
+ const WS_PING_PERIOD_MILLIS = 2 * 60 * 1000;
30
31
  function makeApp(domain, sessions, recipients, midAuthorizer) {
31
32
  const app = new app_1.AppWithWSs();
32
33
  setHttpPart(app.http, domain, sessions, recipients, midAuthorizer);
@@ -35,9 +36,8 @@ function makeApp(domain, sessions, recipients, midAuthorizer) {
35
36
  }
36
37
  exports.makeApp = makeApp;
37
38
  function setWSPart(app, sessions, recipients) {
38
- const sockets = new app_1.UserSockets(sessions.ensureAuthorizedSessionForSocketStart());
39
- const mailEvents = new server_events_1.ServerEvents(undefined, [api.msgRecievedCompletely.EVENT_NAME,
40
- api.msgMainObjRecieved.EVENT_NAME], sockets.socketGetter);
39
+ const sockets = new app_1.UserSockets(sessions.ensureAuthorizedSessionForSocketStart(), WS_PING_PERIOD_MILLIS);
40
+ const mailEvents = new server_events_1.ServerEvents(undefined, [api.msgRecievedCompletely.EVENT_NAME, api.msgMainObjRecieved.EVENT_NAME], sockets.socketGetter);
41
41
  // give events ipc to both ends
42
42
  sockets.addSocketIPC(mailEvents);
43
43
  recipients.setMailEventsSink(mailEvents.eventsSink);
@@ -432,9 +432,9 @@ class EventsReceivingSide extends RequestingSide {
432
432
  * name, to which listener is attached.
433
433
  */
434
434
  this.listeners = new map_of_sets_1.MapOfSets();
435
- this.channels = new IpcEventChannels((ipcChannel) => this.makeRequest(ipc_api_1.events.subscribe.REQ_NAME, ipcChannel)
436
- .catch(err => this.completeEvent(ipcChannel, err)), (ipcChannel) => this.makeRequest(ipc_api_1.events.unsubscribe.REQ_NAME, ipcChannel)
437
- .catch(err => { }));
435
+ this.channels = new IpcEventChannels(ipcChannel => this.makeRequest(ipc_api_1.events.subscribe.REQ_NAME, ipcChannel)
436
+ .catch(err => this.completeEvent(ipcChannel, err)), ipcChannel => this.makeRequest(ipc_api_1.events.unsubscribe.REQ_NAME, ipcChannel)
437
+ .catch(noop));
438
438
  }
439
439
  handleMsg(env) {
440
440
  if (env.type === 'event') {
@@ -581,6 +581,7 @@ class IpcEventChannels {
581
581
  }
582
582
  Object.freeze(IpcEventChannels.prototype);
583
583
  Object.freeze(IpcEventChannels);
584
+ function noop() { }
584
585
  function makeSubscribingClient(channel, comm) {
585
586
  return (new EventsReceivingSide(channel, comm)).wrap();
586
587
  }
@@ -8,13 +8,14 @@ import { CheckSession, SessionParams } from '../resources/sessions';
8
8
  export type HttpServer = http.Server | https.Server;
9
9
  export declare class UserSockets {
10
10
  checkSession: CheckSession<SessionParams>;
11
+ private readonly pingMillis;
11
12
  /**
12
13
  * Only connections that match this path are accepted.
13
14
  */
14
15
  path: string;
15
16
  private clients;
16
17
  private ipcs;
17
- constructor(checkSession: CheckSession<SessionParams>);
18
+ constructor(checkSession: CheckSession<SessionParams>, pingMillis: number);
18
19
  disconnect(): void;
19
20
  handleConnection(userId: string, client: WebSocket): void;
20
21
  addSocketIPC(ipc: SocketIPCs): void;
@@ -124,8 +124,9 @@ function ensureSlashesOnFront(path) {
124
124
  return path;
125
125
  }
126
126
  class UserSockets {
127
- constructor(checkSession) {
127
+ constructor(checkSession, pingMillis) {
128
128
  this.checkSession = checkSession;
129
+ this.pingMillis = pingMillis;
129
130
  /**
130
131
  * Only connections that match this path are accepted.
131
132
  */
@@ -140,7 +141,7 @@ class UserSockets {
140
141
  for (const ipc of this.ipcs) {
141
142
  ipc.attachIPC(userId, client);
142
143
  }
143
- let pinger = new PingSender(client);
144
+ let pinger = new PingSender(client, this.pingMillis);
144
145
  client.on('close', () => {
145
146
  this.clients.remove(userId, client);
146
147
  pinger.close();
@@ -162,10 +163,12 @@ exports.UserSockets = UserSockets;
162
163
  Object.freeze(UserSockets.prototype);
163
164
  Object.freeze(UserSockets);
164
165
  class PingSender {
165
- constructor(client) {
166
+ constructor(client, pingMillis) {
167
+ this.pingMillis = pingMillis;
166
168
  this.outstandingPong = 0;
167
169
  this.client = client;
168
- this.fourMinCheck = setInterval(() => this.recurrentPing(), 4 * 60 * 1000);
170
+ this.pongTimeoutMillis = Math.round(this.pingMillis / 2);
171
+ this.fourMinCheck = setInterval(() => this.recurrentPing(), this.pingMillis);
169
172
  this.fourMinCheck.unref();
170
173
  }
171
174
  recurrentPing() {
@@ -175,7 +178,7 @@ class PingSender {
175
178
  if (this.outstandingPong === 0) {
176
179
  }
177
180
  else if (this.outstandingPong === 1) {
178
- setTimeout(() => this.additionalPing(), 2 * 60 * 1000).unref();
181
+ setTimeout(() => this.additionalPing(), this.pongTimeoutMillis).unref();
179
182
  }
180
183
  else {
181
184
  this.client.close();
package/build/main.js CHANGED
@@ -139,9 +139,16 @@ async function displaySignupInfo(conf) {
139
139
  if (conf.signup.noTokenFile) {
140
140
  const ctx = await (0, signup_1.readNoTokensFile)(conf.signup.noTokenFile);
141
141
  console.log(`Signup of user without token is allowed in domains:\n`, ctx.domains, `\n`);
142
+ const signupLink = signupLinkFrom(conf, undefined);
143
+ if (signupLink) {
144
+ console.log(`Signup link:\n`, signupLink, `\n`);
145
+ }
142
146
  }
143
147
  else {
144
148
  console.log(`Signup of users is allowed only with valid signup tokens.\n`);
149
+ if (conf.signup.serviceUrl) {
150
+ console.log(`Signup service url (from configuration file):\n`, conf.signup.serviceUrl, `\n`);
151
+ }
145
152
  }
146
153
  }
147
154
  const { multiUserTokens, singleUserTokens } = await (0, signup_1.readAllSignupTokens)(conf.rootFolder);
@@ -173,31 +180,50 @@ async function displayTokensList(conf) {
173
180
  }
174
181
  async function displayTokenValue(conf, tokenId) {
175
182
  const ctx = await (0, signup_1.readTokenFile)(conf.rootFolder, tokenId);
176
- if (!ctx) {
183
+ if (ctx) {
184
+ const signupLink = signupLinkFrom(conf, ctx.token);
185
+ if (signupLink) {
186
+ console.log(`\nSignup link: ${signupLink}`);
187
+ }
188
+ if (ctx.type === 'multi-domain') {
189
+ console.log(`\nMulti-user signup context:`);
190
+ console.log(` domains:`, ctx.domains);
191
+ console.log(` value:`, ctx.token);
192
+ if (ctx.validTill) {
193
+ console.log(` valid till: ${(new Date(ctx.validTill)).toString()}\n`);
194
+ }
195
+ else {
196
+ console.log(``);
197
+ }
198
+ }
199
+ else if (ctx.type === 'single-user') {
200
+ console.log(`\nSingle-user signup context:`);
201
+ console.log(` address:`, ctx.userId);
202
+ console.log(` value:`, ctx.token);
203
+ if (ctx.validTill) {
204
+ console.log(` valid till: ${(new Date(ctx.validTill)).toString()}\n`);
205
+ }
206
+ else {
207
+ console.log(``);
208
+ }
209
+ }
210
+ }
211
+ else {
177
212
  console.error(`Token ${tokenId} is not found.`);
178
213
  process.exit(-1);
179
214
  }
180
- else if (ctx.type === 'multi-domain') {
181
- console.log(`\nMulti-user signup context:`);
182
- console.log(` domains:`, ctx.domains);
183
- console.log(` value:`, ctx.token);
184
- if (ctx.validTill) {
185
- console.log(` valid till: ${(new Date(ctx.validTill)).toString()}\n`);
186
- }
187
- else {
188
- console.log(``);
189
- }
215
+ }
216
+ function signupLinkFrom(conf, token = '') {
217
+ var _a;
218
+ if (!((_a = conf.signup) === null || _a === void 0 ? void 0 : _a.serviceUrl)) {
219
+ return;
190
220
  }
191
- else if (ctx.type === 'single-user') {
192
- console.log(`\nSingle-user signup context:`);
193
- console.log(` address:`, ctx.userId);
194
- console.log(` value:`, ctx.token);
195
- if (ctx.validTill) {
196
- console.log(` valid till: ${(new Date(ctx.validTill)).toString()}\n`);
197
- }
198
- else {
199
- console.log(``);
200
- }
221
+ try {
222
+ const url = new URL(conf.signup.serviceUrl);
223
+ return `3nweb://signup/${url.host}${url.pathname}${url.pathname.endsWith('/') ? '' : '/'}${token}`;
224
+ }
225
+ catch (err) {
226
+ return;
201
227
  }
202
228
  }
203
229
  async function createMultiUserToken(conf, domains) {
@@ -25,6 +25,7 @@ export interface Configurations {
25
25
  };
26
26
  signup?: {
27
27
  noTokenFile?: string;
28
+ serviceUrl?: string;
28
29
  };
29
30
  }
30
31
  export declare function servicesApp(conf: Configurations, errLogger?: ErrLogger | 'console', logSetup?: 'console'): AppWithWSs;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spec-3nweb-server",
3
- "version": "1.8.0",
3
+ "version": "1.8.2",
4
4
  "description": "3NWeb spec server.",
5
5
  "main": "build/lib.js",
6
6
  "types": "build/lib.d.ts",