@kapeta/local-cluster-service 0.64.0 → 0.64.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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## [0.64.2](https://github.com/kapetacom/local-cluster-service/compare/v0.64.1...v0.64.2) (2024-08-22)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * handle port detection on multiple interfaces ([#222](https://github.com/kapetacom/local-cluster-service/issues/222)) ([6635cc2](https://github.com/kapetacom/local-cluster-service/commit/6635cc207355b2083edbb380f0ba83227c6a16e1))
7
+
8
+ ## [0.64.1](https://github.com/kapetacom/local-cluster-service/compare/v0.64.0...v0.64.1) (2024-08-21)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * tweak port detection code ([#221](https://github.com/kapetacom/local-cluster-service/issues/221)) ([84000ea](https://github.com/kapetacom/local-cluster-service/commit/84000eae5eaec0d7ca975029b6cb7fcefcb87340))
14
+
1
15
  # [0.64.0](https://github.com/kapetacom/local-cluster-service/compare/v0.63.1...v0.64.0) (2024-08-21)
2
16
 
3
17
 
@@ -16,7 +16,7 @@ declare class ClusterService {
16
16
  * Gets next available port
17
17
  */
18
18
  getNextAvailablePort(startPort?: number): Promise<number>;
19
- _checkIfPortIsUsed(port: number, host?: string): Promise<unknown>;
19
+ private resolveWithAvailablePort;
20
20
  /**
21
21
  * The port of this local cluster service itself
22
22
  */
@@ -3,10 +3,13 @@
3
3
  * Copyright 2023 Kapeta Inc.
4
4
  * SPDX-License-Identifier: BUSL-1.1
5
5
  */
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
6
9
  Object.defineProperty(exports, "__esModule", { value: true });
7
10
  exports.clusterService = void 0;
8
11
  const nodejs_utils_1 = require("@kapeta/nodejs-utils");
9
- const net = require('net');
12
+ const net_1 = __importDefault(require("net"));
10
13
  const DEFAULT_SERVER_PORT = 35100;
11
14
  const DEFAULT_START_PORT = 40000;
12
15
  const DEFAULT_HOST = '127.0.0.1';
@@ -38,56 +41,51 @@ class ClusterService {
38
41
  await this._findClusterServicePort();
39
42
  }
40
43
  async _findClusterServicePort() {
41
- while (true) {
42
- const isUsed = await this._checkIfPortIsUsed(this._port);
43
- if (!isUsed) {
44
- break;
44
+ for (; this._port <= 65535; this._port++) {
45
+ try {
46
+ await this.resolveWithAvailablePort(this._port);
47
+ return;
48
+ }
49
+ catch (e) {
50
+ // try again
45
51
  }
46
- this._port++;
47
52
  }
53
+ throw new Error('No available ports');
48
54
  }
49
55
  /**
50
56
  * Gets next available port
51
57
  */
52
58
  async getNextAvailablePort(startPort = -1) {
53
- let receivedStartPort = startPort > 0;
54
- if (!receivedStartPort) {
55
- startPort = this._currentPort;
56
- }
57
- while (true) {
58
- while (this._reservedPorts.indexOf(startPort) > -1) {
59
- startPort++;
60
- if (!receivedStartPort) {
61
- this._currentPort = startPort;
62
- }
63
- }
64
- const nextPort = startPort++;
65
- if (!receivedStartPort) {
66
- this._currentPort = startPort;
59
+ for (let nextPort = startPort > 0 ? startPort : this._currentPort; nextPort <= 65535; nextPort++) {
60
+ if (this._reservedPorts.indexOf(nextPort) > -1) {
61
+ continue;
67
62
  }
68
- const isUsed = await this._checkIfPortIsUsed(nextPort);
69
- if (!isUsed) {
63
+ try {
64
+ // Try both IPv4 and IPv6 addresses
65
+ await this.resolveWithAvailablePort(nextPort);
66
+ await this.resolveWithAvailablePort(nextPort, '0.0.0.0');
67
+ // Save the state if we're looking for a system port for the cluster itself
68
+ if (startPort <= 0) {
69
+ this._currentPort = nextPort;
70
+ }
70
71
  return nextPort;
71
72
  }
73
+ catch (e) {
74
+ // try again }
75
+ }
72
76
  }
77
+ throw new Error('No available ports');
73
78
  }
74
- _checkIfPortIsUsed(port, host = this._host) {
79
+ async resolveWithAvailablePort(port, host = this._host) {
75
80
  return new Promise((resolve, reject) => {
76
- const server = net.createServer();
77
- server.once('error', function (err) {
78
- if (err.code === 'EADDRINUSE') {
79
- server.close();
80
- resolve(true);
81
- return;
82
- }
83
- server.close();
84
- reject(err);
85
- });
86
- server.once('listening', function () {
87
- server.close();
88
- resolve(false);
81
+ const server = net_1.default.createServer();
82
+ server.unref();
83
+ server.on('error', reject);
84
+ server.listen({ port, host }, (...args) => {
85
+ server.close(() => {
86
+ resolve(port);
87
+ });
89
88
  });
90
- server.listen(port, host);
91
89
  });
92
90
  }
93
91
  /**
@@ -1,13 +1,5 @@
1
- /// <reference types="node" />
2
- import { Server } from 'http';
3
1
  import { StormEventPage } from './events';
4
- declare module 'express-serve-static-core' {
5
- interface Application {
6
- listen(port: number, callback?: (err?: Error) => void): Server;
7
- }
8
- }
9
2
  export declare class UIServer {
10
- private readonly express;
11
3
  private readonly systemId;
12
4
  private port;
13
5
  private server;
@@ -11,34 +11,31 @@ exports.UIServer = void 0;
11
11
  const express_1 = __importDefault(require("express"));
12
12
  const page_utils_1 = require("./page-utils");
13
13
  const clusterService_1 = require("../clusterService");
14
+ const http_1 = require("http");
14
15
  class UIServer {
15
- express;
16
16
  systemId;
17
17
  port = 50000;
18
18
  server;
19
19
  constructor(systemId) {
20
20
  this.systemId = systemId;
21
- this.express = (0, express_1.default)();
22
21
  }
23
22
  async start() {
24
- this.port = await clusterService_1.clusterService.getNextAvailablePort(this.port);
25
- this.express.get('/_reset', (req, res) => {
23
+ const app = (0, express_1.default)();
24
+ app.get('/_reset', (req, res) => {
26
25
  res.send(`
27
26
  <script>
28
27
  window.localStorage.clear();
29
28
  window.sessionStorage.clear();
30
29
  </script>`);
31
30
  });
32
- this.express.all('/*', (req, res) => {
31
+ app.all('/*', (req, res) => {
33
32
  (0, page_utils_1.readPageFromDisk)(this.systemId, req.params[0], req.method, res);
34
33
  });
34
+ this.port = await clusterService_1.clusterService.getNextAvailablePort(this.port);
35
35
  return new Promise((resolve, reject) => {
36
- this.server = this.express.listen(this.port, (err) => {
37
- if (err) {
38
- console.error('Failed to start UI server', err);
39
- reject(err);
40
- return;
41
- }
36
+ this.server = (0, http_1.createServer)(app);
37
+ this.server.on('error', reject);
38
+ this.server.listen(this.port, () => {
42
39
  console.log(`UI Server started on port ${this.port}`);
43
40
  resolve();
44
41
  });
@@ -16,7 +16,7 @@ declare class ClusterService {
16
16
  * Gets next available port
17
17
  */
18
18
  getNextAvailablePort(startPort?: number): Promise<number>;
19
- _checkIfPortIsUsed(port: number, host?: string): Promise<unknown>;
19
+ private resolveWithAvailablePort;
20
20
  /**
21
21
  * The port of this local cluster service itself
22
22
  */
@@ -3,10 +3,13 @@
3
3
  * Copyright 2023 Kapeta Inc.
4
4
  * SPDX-License-Identifier: BUSL-1.1
5
5
  */
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
6
9
  Object.defineProperty(exports, "__esModule", { value: true });
7
10
  exports.clusterService = void 0;
8
11
  const nodejs_utils_1 = require("@kapeta/nodejs-utils");
9
- const net = require('net');
12
+ const net_1 = __importDefault(require("net"));
10
13
  const DEFAULT_SERVER_PORT = 35100;
11
14
  const DEFAULT_START_PORT = 40000;
12
15
  const DEFAULT_HOST = '127.0.0.1';
@@ -38,56 +41,51 @@ class ClusterService {
38
41
  await this._findClusterServicePort();
39
42
  }
40
43
  async _findClusterServicePort() {
41
- while (true) {
42
- const isUsed = await this._checkIfPortIsUsed(this._port);
43
- if (!isUsed) {
44
- break;
44
+ for (; this._port <= 65535; this._port++) {
45
+ try {
46
+ await this.resolveWithAvailablePort(this._port);
47
+ return;
48
+ }
49
+ catch (e) {
50
+ // try again
45
51
  }
46
- this._port++;
47
52
  }
53
+ throw new Error('No available ports');
48
54
  }
49
55
  /**
50
56
  * Gets next available port
51
57
  */
52
58
  async getNextAvailablePort(startPort = -1) {
53
- let receivedStartPort = startPort > 0;
54
- if (!receivedStartPort) {
55
- startPort = this._currentPort;
56
- }
57
- while (true) {
58
- while (this._reservedPorts.indexOf(startPort) > -1) {
59
- startPort++;
60
- if (!receivedStartPort) {
61
- this._currentPort = startPort;
62
- }
63
- }
64
- const nextPort = startPort++;
65
- if (!receivedStartPort) {
66
- this._currentPort = startPort;
59
+ for (let nextPort = startPort > 0 ? startPort : this._currentPort; nextPort <= 65535; nextPort++) {
60
+ if (this._reservedPorts.indexOf(nextPort) > -1) {
61
+ continue;
67
62
  }
68
- const isUsed = await this._checkIfPortIsUsed(nextPort);
69
- if (!isUsed) {
63
+ try {
64
+ // Try both IPv4 and IPv6 addresses
65
+ await this.resolveWithAvailablePort(nextPort);
66
+ await this.resolveWithAvailablePort(nextPort, '0.0.0.0');
67
+ // Save the state if we're looking for a system port for the cluster itself
68
+ if (startPort <= 0) {
69
+ this._currentPort = nextPort;
70
+ }
70
71
  return nextPort;
71
72
  }
73
+ catch (e) {
74
+ // try again }
75
+ }
72
76
  }
77
+ throw new Error('No available ports');
73
78
  }
74
- _checkIfPortIsUsed(port, host = this._host) {
79
+ async resolveWithAvailablePort(port, host = this._host) {
75
80
  return new Promise((resolve, reject) => {
76
- const server = net.createServer();
77
- server.once('error', function (err) {
78
- if (err.code === 'EADDRINUSE') {
79
- server.close();
80
- resolve(true);
81
- return;
82
- }
83
- server.close();
84
- reject(err);
85
- });
86
- server.once('listening', function () {
87
- server.close();
88
- resolve(false);
81
+ const server = net_1.default.createServer();
82
+ server.unref();
83
+ server.on('error', reject);
84
+ server.listen({ port, host }, (...args) => {
85
+ server.close(() => {
86
+ resolve(port);
87
+ });
89
88
  });
90
- server.listen(port, host);
91
89
  });
92
90
  }
93
91
  /**
@@ -1,13 +1,5 @@
1
- /// <reference types="node" />
2
- import { Server } from 'http';
3
1
  import { StormEventPage } from './events';
4
- declare module 'express-serve-static-core' {
5
- interface Application {
6
- listen(port: number, callback?: (err?: Error) => void): Server;
7
- }
8
- }
9
2
  export declare class UIServer {
10
- private readonly express;
11
3
  private readonly systemId;
12
4
  private port;
13
5
  private server;
@@ -11,34 +11,31 @@ exports.UIServer = void 0;
11
11
  const express_1 = __importDefault(require("express"));
12
12
  const page_utils_1 = require("./page-utils");
13
13
  const clusterService_1 = require("../clusterService");
14
+ const http_1 = require("http");
14
15
  class UIServer {
15
- express;
16
16
  systemId;
17
17
  port = 50000;
18
18
  server;
19
19
  constructor(systemId) {
20
20
  this.systemId = systemId;
21
- this.express = (0, express_1.default)();
22
21
  }
23
22
  async start() {
24
- this.port = await clusterService_1.clusterService.getNextAvailablePort(this.port);
25
- this.express.get('/_reset', (req, res) => {
23
+ const app = (0, express_1.default)();
24
+ app.get('/_reset', (req, res) => {
26
25
  res.send(`
27
26
  <script>
28
27
  window.localStorage.clear();
29
28
  window.sessionStorage.clear();
30
29
  </script>`);
31
30
  });
32
- this.express.all('/*', (req, res) => {
31
+ app.all('/*', (req, res) => {
33
32
  (0, page_utils_1.readPageFromDisk)(this.systemId, req.params[0], req.method, res);
34
33
  });
34
+ this.port = await clusterService_1.clusterService.getNextAvailablePort(this.port);
35
35
  return new Promise((resolve, reject) => {
36
- this.server = this.express.listen(this.port, (err) => {
37
- if (err) {
38
- console.error('Failed to start UI server', err);
39
- reject(err);
40
- return;
41
- }
36
+ this.server = (0, http_1.createServer)(app);
37
+ this.server.on('error', reject);
38
+ this.server.listen(this.port, () => {
42
39
  console.log(`UI Server started on port ${this.port}`);
43
40
  resolve();
44
41
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kapeta/local-cluster-service",
3
- "version": "0.64.0",
3
+ "version": "0.64.2",
4
4
  "description": "Manages configuration, ports and service discovery for locally running Kapeta systems",
5
5
  "type": "commonjs",
6
6
  "exports": {
@@ -5,7 +5,7 @@
5
5
 
6
6
  import { normalizeKapetaUri } from '@kapeta/nodejs-utils';
7
7
 
8
- const net = require('net');
8
+ import net from 'net';
9
9
  const DEFAULT_SERVER_PORT = 35100;
10
10
  const DEFAULT_START_PORT = 40000;
11
11
  const DEFAULT_HOST = '127.0.0.1';
@@ -44,64 +44,53 @@ class ClusterService {
44
44
  }
45
45
 
46
46
  async _findClusterServicePort() {
47
- while (true) {
48
- const isUsed = await this._checkIfPortIsUsed(this._port);
49
- if (!isUsed) {
50
- break;
47
+ for (; this._port <= 65535; this._port++) {
48
+ try {
49
+ await this.resolveWithAvailablePort(this._port);
50
+ return;
51
+ } catch (e) {
52
+ // try again
51
53
  }
52
-
53
- this._port++;
54
54
  }
55
+ throw new Error('No available ports');
55
56
  }
56
57
 
57
58
  /**
58
59
  * Gets next available port
59
60
  */
60
61
  public async getNextAvailablePort(startPort: number = -1) {
61
- let receivedStartPort = startPort > 0;
62
- if (!receivedStartPort) {
63
- startPort = this._currentPort;
64
- }
65
- while (true) {
66
- while (this._reservedPorts.indexOf(startPort) > -1) {
67
- startPort++;
68
- if (!receivedStartPort) {
69
- this._currentPort = startPort;
70
- }
62
+ for (let nextPort = startPort > 0 ? startPort : this._currentPort; nextPort <= 65535; nextPort++) {
63
+ if (this._reservedPorts.indexOf(nextPort) > -1) {
64
+ continue;
71
65
  }
72
66
 
73
- const nextPort = startPort++;
74
- if (!receivedStartPort) {
75
- this._currentPort = startPort;
76
- }
77
- const isUsed = await this._checkIfPortIsUsed(nextPort);
78
- if (!isUsed) {
67
+ try {
68
+ // Try both IPv4 and IPv6 addresses
69
+ await this.resolveWithAvailablePort(nextPort);
70
+ await this.resolveWithAvailablePort(nextPort, '0.0.0.0');
71
+ // Save the state if we're looking for a system port for the cluster itself
72
+ if (startPort <= 0) {
73
+ this._currentPort = nextPort;
74
+ }
79
75
  return nextPort;
76
+ } catch (e) {
77
+ // try again }
80
78
  }
81
79
  }
80
+ throw new Error('No available ports');
82
81
  }
83
82
 
84
- _checkIfPortIsUsed(port: number, host: string = this._host) {
83
+ private async resolveWithAvailablePort(port: number, host: string = this._host) {
85
84
  return new Promise((resolve, reject) => {
86
85
  const server = net.createServer();
86
+ server.unref();
87
+ server.on('error', reject);
87
88
 
88
- server.once('error', function (err: any) {
89
- if (err.code === 'EADDRINUSE') {
90
- server.close();
91
- resolve(true);
92
- return;
93
- }
94
-
95
- server.close();
96
- reject(err);
89
+ server.listen({ port, host }, (...args) => {
90
+ server.close(() => {
91
+ resolve(port);
92
+ });
97
93
  });
98
-
99
- server.once('listening', function () {
100
- server.close();
101
- resolve(false);
102
- });
103
-
104
- server.listen(port, host);
105
94
  });
106
95
  }
107
96
 
@@ -5,20 +5,10 @@
5
5
  import express, { Express, Request, Response } from 'express';
6
6
  import { readPageFromDisk } from './page-utils';
7
7
  import { clusterService } from '../clusterService';
8
- import { Server } from 'http';
8
+ import { createServer, Server } from 'http';
9
9
  import { StormEventPage } from './events';
10
10
 
11
- declare module 'express-serve-static-core' {
12
- interface Application {
13
- // Adds error callback support
14
- // From the docs:
15
- // All the forms of Node’s http.Server.listen() method are in fact actually supported.
16
- listen(port: number, callback?: (err?: Error) => void): Server;
17
- }
18
- }
19
-
20
11
  export class UIServer {
21
- private readonly express: Express;
22
12
  private readonly systemId: string;
23
13
 
24
14
  private port: number = 50000;
@@ -26,13 +16,11 @@ export class UIServer {
26
16
 
27
17
  constructor(systemId: string) {
28
18
  this.systemId = systemId;
29
- this.express = express();
30
19
  }
31
20
 
32
21
  public async start() {
33
- this.port = await clusterService.getNextAvailablePort(this.port);
34
-
35
- this.express.get('/_reset', (req: Request, res: Response) => {
22
+ const app = express();
23
+ app.get('/_reset', (req: Request, res: Response) => {
36
24
  res.send(
37
25
  `
38
26
  <script>
@@ -42,17 +30,16 @@ export class UIServer {
42
30
  );
43
31
  });
44
32
 
45
- this.express.all('/*', (req: Request, res: Response) => {
33
+ app.all('/*', (req: Request, res: Response) => {
46
34
  readPageFromDisk(this.systemId, req.params[0], req.method, res);
47
35
  });
48
36
 
37
+ this.port = await clusterService.getNextAvailablePort(this.port);
49
38
  return new Promise<void>((resolve, reject) => {
50
- this.server = this.express.listen(this.port, (err) => {
51
- if (err) {
52
- console.error('Failed to start UI server', err);
53
- reject(err);
54
- return;
55
- }
39
+ this.server = createServer(app);
40
+ this.server.on('error', reject);
41
+
42
+ this.server.listen(this.port, () => {
56
43
  console.log(`UI Server started on port ${this.port}`);
57
44
  resolve();
58
45
  });
@@ -440,7 +440,7 @@ router.post('/ui/edit', async (req: KapetaBodyRequest, res: Response) => {
440
440
 
441
441
  sendDone(res);
442
442
  } catch (err: any) {
443
- sendError(err, res);
443
+ sendError(err as Error, res);
444
444
  if (!res.closed) {
445
445
  res.end();
446
446
  }