@interopio/gateway-server 0.21.0 → 0.23.0

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,16 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- `use strict`;
3
+ 'use strict';
4
4
 
5
- import { writeFileSync } from 'node:fs';
6
- import { parseArgs } from 'node:util';
7
- import { configure } from '@interopio/gateway/logging/core';
5
+ const { parseArgs } = require('node:util');
6
+ const { configure } = require('@interopio/gateway/logging/core');
8
7
 
9
8
  // import { GatewayServer } from './src/index.ts';
10
- // import { mkcert, argon2 } from './src/tools/index.ts';
9
+ // import { mkcert, argon2, manage } from './src/tools/index.ts';
11
10
 
12
- import { GatewayServer } from './dist/index.js';
13
- import { mkcert, argon2 } from './dist/tools/index.js';
11
+ const { mkcert, argon2, manage } = require('@interopio/gateway-server/tools');
12
+ const { GatewayServer } = require('@interopio/gateway-server');
14
13
 
15
14
  function showHelp() {
16
15
  console.log(`
@@ -18,6 +17,7 @@ Usage: npx @interopio/gateway-server <command> [options]
18
17
 
19
18
  Commands:
20
19
  run Start the gateway server
20
+ manage Send management commands to a running gateway server
21
21
  passwd Generate password hash (default using Argon2)
22
22
  mkcert Generate client and/or server certificate signed by Dev CA
23
23
 
@@ -27,7 +27,7 @@ run options:
27
27
  -H, --host <host> Network address to bind to (default: unspecified, listens on all)
28
28
  examples: localhost, 127.0.0.1, 0.0.0.0, ::1
29
29
  -u, --user <name> Enables basic authentication and sets the admin username
30
- --no-auth Disable authentication (overrides config, sets auth.type to 'none')
30
+ --no-auth Disable authentication (overrides config, sets auth type to 'none')
31
31
  -S, --ssl, --tls Enable HTTPS. Auto-generates Dev CA and/or server certificates if not
32
32
  present. Enables x509 client cert auth if --user not specified.
33
33
  --cert <file> File with SSL/TLS certificate (default: ./gateway-server.crt)
@@ -41,6 +41,18 @@ run options:
41
41
  -c, --config <file> Server configuration file (JSON format)
42
42
  --debug Enable debug logging (default: info level)
43
43
 
44
+ manage options:
45
+ --path <path> Path to the gateway control socket (named pipe on Windows, Unix socket)
46
+ examples: \\\\.\\pipe\\glue42-gateway-xxx, /tmp/gateway.sock
47
+ -p, --port <port> TCP port of the management server
48
+ --timeout <ms> Connection timeout in milliseconds (default: 5000)
49
+ <command> Management command to execute: info, shutdown
50
+ [key=value...] Additional command parameters as key=value pairs
51
+ Values are parsed as JSON (numbers, booleans, arrays, objects)
52
+ or kept as strings if not valid JSON
53
+ <json> Alternatively, pass entire command as a JSON object
54
+ example: '{"command":"update-auth","type":"basic"}'
55
+
44
56
  passwd options:
45
57
  (no args) Prompt for password interactively (masked input)
46
58
  --stdin Read password from stdin (for piping, e.g., echo "pass" | ...)
@@ -66,9 +78,13 @@ Examples:
66
78
  gateway-server run -p 3000
67
79
  gateway-server run -u admin --port 8385,8388 --debug
68
80
  gateway-server run -p 8443 --ssl --user admin --gateway
69
- gateway-server run --port 42443 --tls --ca-key ./ssl/ca.key --host medes --gateway
81
+ gateway-server run --port 42443 --tls --ca-key ./ssl/ca.key --host example.com --gateway
70
82
  gateway-server run -p 3000 --static ./public --config ./server-config.json
71
83
  gateway-server run --config ./server-config.json --no-gateway --no-ssl --no-auth
84
+ gateway-server manage --path \\\\.\\pipe\\glue42-gateway-xxx info
85
+ gateway-server manage --path \\\\.\\pipe\\glue42-gateway-xxx shutdown
86
+ gateway-server manage --path \\\\.\\pipe\\glue42-gateway-xxx custom-cmd key1=value1 count=42 'data={"user":"test"}' --timeout 10000
87
+ gateway-server manage --path \\\\.\\pipe\\glue42-gateway-xxx '{"command":"update-auth","auth":{"token":"my-token"}}}'
72
88
  gateway-server passwd
73
89
  echo "mySecret123" | gateway-server passwd --stdin
74
90
  gateway-server mkcert
@@ -115,13 +131,16 @@ const command = positionals[0];
115
131
  // Route to appropriate command handler
116
132
  switch (command) {
117
133
  case 'run':
118
- await runServer();
134
+ runServer().catch(e => {console.error(e); process.exit(1);});
135
+ break;
136
+ case 'manage':
137
+ manageCommand().catch(e => {console.error(e); process.exit(1);});
119
138
  break;
120
139
  case 'passwd':
121
- await passwdCommand();
140
+ passwdCommand().catch(e => {console.error(e); process.exit(1);});
122
141
  break;
123
142
  case 'mkcert':
124
- await mkcertCommand();
143
+ mkcertCommand().catch(e => {console.error(e); process.exit(1);});
125
144
  break;
126
145
  default:
127
146
  console.error(`Error: Unknown command "${command}"`);
@@ -129,6 +148,96 @@ switch (command) {
129
148
  process.exit(1);
130
149
  }
131
150
 
151
+ // Command: manage - Send management commands to a running gateway server
152
+ async function manageCommand() {
153
+ const { values, positionals } = parseArgs({
154
+ args: process.argv.slice(3), // Skip 'node', 'gateway-server', and 'manage'
155
+ options: {
156
+ path: { type: 'string' },
157
+ port: { type: 'string', short: 'p' },
158
+ timeout: { type: 'string' },
159
+ },
160
+ strict: true,
161
+ allowPositionals: true,
162
+ });
163
+
164
+ const socketPath = values.path;
165
+ const port = values.port ? parseInt(values.port, 10) : undefined;
166
+ const timeout = values.timeout ? parseInt(values.timeout, 10) : undefined;
167
+ const firstArg = positionals[0];
168
+
169
+ if (!socketPath && !port) {
170
+ console.error('Error: --path or --port is required');
171
+ console.error('Example: gateway-server manage --path \\\\.\\pipe\\glue42-gateway-xxx info');
172
+ process.exit(1);
173
+ }
174
+
175
+ if (!firstArg) {
176
+ console.error('Error: management command is required');
177
+ console.error('Available commands: info, shutdown');
178
+ console.error('Or pass a JSON object: gateway-server manage --path ... \'{"command":"info"}\'');
179
+ process.exit(1);
180
+ }
181
+
182
+ // Try to parse first argument as JSON (entire command object)
183
+ let commandParams;
184
+ if (firstArg.startsWith('{')) {
185
+ try {
186
+ commandParams = JSON.parse(firstArg);
187
+ if (!commandParams.command) {
188
+ console.error('Error: JSON command must have a "command" property');
189
+ process.exit(1);
190
+ }
191
+ }
192
+ catch (e) {
193
+ console.error(`Error: invalid JSON: ${e.message}`);
194
+ process.exit(1);
195
+ }
196
+ }
197
+ else {
198
+ // Parse as command name + key=value pairs
199
+ commandParams = { command: firstArg };
200
+ for (let i = 1; i < positionals.length; i++) {
201
+ const arg = positionals[i];
202
+ const eqIndex = arg.indexOf('=');
203
+ if (eqIndex === -1) {
204
+ console.error(`Error: invalid parameter "${arg}". Expected format: key=value`);
205
+ process.exit(1);
206
+ }
207
+ const key = arg.slice(0, eqIndex);
208
+ let value = arg.slice(eqIndex + 1);
209
+
210
+ // Try to parse as JSON for numbers, booleans, arrays, objects
211
+ try {
212
+ value = JSON.parse(value);
213
+ } catch {
214
+ // Keep as string if not valid JSON
215
+ }
216
+
217
+ commandParams[key] = value;
218
+ }
219
+ }
220
+
221
+ const server = socketPath ? { path: socketPath } : { port };
222
+ try {
223
+ const result = await manage.sendCommand(server, commandParams, { timeout });
224
+ if (result === undefined) {
225
+ console.log('<no response>');
226
+ }
227
+ else if (typeof result === 'string') {
228
+ console.log(result);
229
+ }
230
+ else {
231
+ console.log(JSON.stringify(result, null, 2));
232
+ }
233
+ process.exit(0);
234
+ }
235
+ catch (e) {
236
+ console.error(`Error: ${e.message || e}`);
237
+ process.exit(1);
238
+ }
239
+ }
240
+
132
241
  // Command: passwd - Generate password hash
133
242
  async function passwdCommand() {
134
243
  const { values } = parseArgs({
@@ -140,7 +249,7 @@ async function passwdCommand() {
140
249
  allowPositionals: false,
141
250
  });
142
251
 
143
- let password = undefined;
252
+ let password/*: string = undefined!*/;
144
253
 
145
254
  // Read password from stdin if specified
146
255
  if (values.stdin) {
@@ -172,7 +281,7 @@ async function readPasswordFromStdin() {
172
281
  }
173
282
 
174
283
  // Piped mode - read until EOF or newline
175
- const chunks = [];
284
+ const chunks/*: Uint8Array[]*/ = [];
176
285
  for await (const chunk of process.stdin) {
177
286
  chunks.push(chunk);
178
287
  }
@@ -182,8 +291,8 @@ async function readPasswordFromStdin() {
182
291
  }
183
292
 
184
293
  // Prompt for password with masked input
185
- async function promptPassword(prompt) {
186
- return new Promise((resolve) => {
294
+ async function promptPassword(prompt/*: string*/) {
295
+ return new Promise/*<string>*/((resolve) => {
187
296
  process.stdout.write(prompt);
188
297
 
189
298
  // Only set raw mode if stdin is a TTY
@@ -205,7 +314,7 @@ async function promptPassword(prompt) {
205
314
  process.stdout.write('\n');
206
315
  };
207
316
 
208
- const onData = (char) => {
317
+ const onData = (char/*: string*/) => {
209
318
  // Ctrl+C
210
319
  if (char === '\u0003') {
211
320
  cleanup();
@@ -269,15 +378,18 @@ async function mkcertCommand() {
269
378
  // Client certificate: combined file by default
270
379
  keyPath = './gateway-client.key';
271
380
  certPath = './gateway-client.crt';
272
- } else {
381
+ }
382
+ else {
273
383
  // Server certificate: separate files by default
274
384
  keyPath = './gateway-server.key';
275
385
  certPath = './gateway-server.crt';
276
386
  }
277
- } else if (keyPath && !certPath) {
387
+ }
388
+ else if (keyPath && !certPath) {
278
389
  // Only --key specified: cert goes in the same file (combined)
279
390
  certPath = keyPath;
280
- } else if (!keyPath && certPath) {
391
+ }
392
+ else if (!keyPath && certPath) {
281
393
  // Only --cert specified: key goes in the same file (combined)
282
394
  keyPath = certPath;
283
395
  }
@@ -293,30 +405,30 @@ async function mkcertCommand() {
293
405
  }
294
406
  }
295
407
 
296
- const { readFileSync } = await import('node:fs');
408
+ const { readFile, writeFile } = await import('node:fs/promises');
297
409
  const { KEYUTIL, X509 } = await import('jsrsasign');
298
410
 
299
411
  // Load CA key
300
- let caKeyObj;
412
+ let caKeyObj/*: ReturnType<typeof KEYUTIL.getKey>*/;
301
413
  try {
302
- const caKeyPem = readFileSync(caKey, 'utf8');
414
+ const caKeyPem = await readFile(caKey, 'utf8');
303
415
  caKeyObj = KEYUTIL.getKey(caKeyPem);
304
416
  } catch (error) {
305
417
  console.error(`Error: Cannot read CA key from ${caKey}`);
306
- console.error(error.message);
418
+ console.error(error?.['message']);
307
419
  process.exit(1);
308
420
  }
309
421
 
310
422
  // Extract issuer from CA certificate
311
- let issuer;
423
+ let issuer/*: string*/;
312
424
  try {
313
- const caCertPem = readFileSync(caCert, 'utf8');
425
+ const caCertPem = await readFile(caCert, 'utf8');
314
426
  const caCertObj = new X509();
315
427
  caCertObj.readCertPEM(caCertPem);
316
428
  issuer = caCertObj.getSubjectString();
317
429
  } catch (error) {
318
430
  console.error(`Error: Cannot read CA certificate from ${caCert}`);
319
- console.error(error.message);
431
+ console.error(error?.['message']);
320
432
  process.exit(1);
321
433
  }
322
434
 
@@ -328,16 +440,17 @@ async function mkcertCommand() {
328
440
  if (keyPath === certPath) {
329
441
  // Concatenate cert and key into single file (cert first, then key)
330
442
  const combined = cert.cert + cert.key;
331
- writeFileSync(keyPath, combined, { mode: 0o600 });
443
+ await writeFile(keyPath, combined, { mode: 0o600 });
332
444
  console.log(`${isClientCert ? 'Client' : 'Server'} certificate generated successfully:`);
333
445
  console.log(` Combined (cert+key): ${keyPath}`);
334
446
  if (sanEntries.length > 0) {
335
447
  console.log(` SAN: ${sanEntries.join(', ')}`);
336
448
  }
337
- } else {
449
+ }
450
+ else {
338
451
  // Write separate files
339
- writeFileSync(keyPath, cert.key, { mode: 0o600 });
340
- writeFileSync(certPath, cert.cert, { mode: 0o644 });
452
+ await writeFile(keyPath, cert.key, { mode: 0o600 });
453
+ await writeFile(certPath, cert.cert, { mode: 0o644 });
341
454
  console.log(`${isClientCert ? 'Client' : 'Server'} certificate generated successfully:`);
342
455
  console.log(` Private key: ${keyPath}`);
343
456
  console.log(` Certificate: ${certPath}`);
@@ -347,9 +460,10 @@ async function mkcertCommand() {
347
460
  }
348
461
 
349
462
  process.exit(0);
350
- } catch (error) {
463
+ }
464
+ catch (error) {
351
465
  console.error(`Error: Cannot write certificate files`);
352
- console.error(error.message);
466
+ console.error(error?.['message']);
353
467
  process.exit(1);
354
468
  }
355
469
  }
@@ -397,19 +511,62 @@ async function runServer() {
397
511
  };
398
512
 
399
513
  configure({
400
- level: options.debug ? 'debug' : 'info',
514
+ level: options.debug ? {
515
+ gateway: 'debug',
516
+ "gateway.node": 'info',
517
+ } : 'info',
401
518
  });
402
519
 
403
520
  // Load server configuration from file if specified
404
- let serverConfig = {};
521
+ let serverConfig/*: GatewayServer.ServerConfig*/ = {};
522
+
523
+ const { access, constants } = await import('node:fs/promises');
524
+ const { resolve, extname } = await import('node:path');
525
+
526
+ let configPath/*: string*/;
527
+ for (const fileName of
528
+ [
529
+ 'gateway-server.config.js',
530
+ 'gateway-server.config.mjs',
531
+ 'gateway-server.config.cjs',
532
+ 'gateway-server.config.ts',
533
+ 'gateway-server.config.mts',
534
+ 'gateway-server.config.cts',
535
+ 'gateway-server.config.json'
536
+ ]) {
537
+ configPath = resolve(process.cwd(), fileName);
538
+ try {
539
+ await access(configPath, constants.R_OK);
540
+ // found readable config file, load it
541
+ break;
542
+ }
543
+ catch {
544
+ configPath = undefined;
545
+ // continue to next candidate
546
+ }
547
+ }
548
+
405
549
  if (options.config) {
550
+ configPath = options.config;
551
+ }
552
+ if (configPath) {
406
553
  try {
407
- const { readFileSync } = await import('node:fs');
408
- const configContent = readFileSync(options.config, 'utf8');
409
- serverConfig = JSON.parse(configContent);
410
- } catch (error) {
411
- console.error(`Error: Cannot read server config from ${options.config}`);
412
- console.error(error.message);
554
+ if (['.json'].includes(extname(configPath))) {
555
+ // JSON config file - read and parse manually
556
+ const { readFile } = await import('node:fs/promises');
557
+ const configContent = await readFile(configPath, 'utf8');
558
+ serverConfig = JSON.parse(configContent);
559
+ }
560
+ else {
561
+ // JS/TS config file - import dynamically
562
+ const { pathToFileURL } = await import('node:url');
563
+ const importedConfig = await import(pathToFileURL(configPath));
564
+ serverConfig = importedConfig.default || importedConfig;
565
+ }
566
+ }
567
+ catch (error) {
568
+ console.error(`Error: Cannot read server config from ${configPath}`);
569
+ console.error(error?.['message']);
413
570
  process.exit(1);
414
571
  }
415
572
  }
@@ -451,10 +608,10 @@ async function runServer() {
451
608
  // else: use whatever is in serverConfig (if any)
452
609
 
453
610
  // Determine effective SSL state
454
- const effectiveSsl = options.ssl || (!options.noSsl && serverConfig.ssl !== undefined);
611
+ const effectiveSsl = options.ssl || (!options.noSsl && serverConfig?.ssl !== undefined);
455
612
 
456
613
  // Configure authentication
457
- let auth;
614
+ let auth/*: GatewayServer.AuthConfig*/;
458
615
  if (values.auth === false) {
459
616
  // --no-auth: delete auth config from server config and set to 'none'
460
617
  delete serverConfig.auth;
@@ -467,7 +624,7 @@ async function runServer() {
467
624
  // Allow --user CLI option to override auth.user.name from config
468
625
  if (options.user) {
469
626
  if (!auth.user) {
470
- auth.user = {};
627
+ auth.user = {name: options.user};
471
628
  }
472
629
  auth.user.name = options.user;
473
630
  }
@@ -548,7 +705,7 @@ async function runServer() {
548
705
  // Override static resources if specified
549
706
  if (options.static && options.static.length > 0) {
550
707
  if (!serverConfig.resources) {
551
- serverConfig.resources = {};
708
+ serverConfig.resources = {locations: options.static};
552
709
  }
553
710
  serverConfig.resources.locations = options.static;
554
711
  }
@@ -557,10 +714,7 @@ async function runServer() {
557
714
  serverConfig.auth = auth;
558
715
 
559
716
  const server = await GatewayServer.Factory({
560
- ...(serverConfig),
561
- app: async (_configurer) => {
562
- // No custom app logic for now
563
- },
717
+ ...(serverConfig)
564
718
  });
565
719
 
566
720
  process.on('SIGINT', async () => {
package/changelog.md CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  # Change Log
4
4
 
5
+ ## 0.23.0 (2026-02-23)
6
+
7
+ ### Fixed
8
+ - fix: config exports
9
+ - fix: MacOS SEA build
10
+ ### Changed
11
+ - chore: bump @interopio/gateway to 0.25.0
12
+
13
+ ## 0.22.0 (2026-02-20)
14
+
15
+ ### Added
16
+ - feat: support config from gateway-server.config.{js,mjs,cjs,ts,mts,cts,json} files (with auto config file detection)
17
+ - feat: management of the server via ipc (info and shutdown commands implemented for now, more to come)
18
+ - feat: cli manage command
19
+ ### Changed
20
+ - chore: bump @interopio/gateway to 0.24.0
21
+ - feat: config is now in separate ts.d
22
+ ### Fixed
23
+ - fix: malformed http host/x-forwarded-host header handling
24
+
5
25
  ## 0.21.0 (2026-01-27)
6
26
 
7
27
  ### Added
@@ -0,0 +1,2 @@
1
+ "use strict";var f=Object.defineProperty;var g=Object.getOwnPropertyDescriptor;var t=Object.getOwnPropertyNames;var C=Object.prototype.hasOwnProperty;var v=(r,e)=>{for(var o in e)f(r,o,{get:e[o],enumerable:!0})},S=(r,e,o,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of t(e))!C.call(r,n)&&n!==o&&f(r,n,{get:()=>e[n],enumerable:!(i=g(e,n))||i.enumerable});return r};var m=r=>S(f({},"__esModule",{value:!0}),r);var u={};v(u,{defineConfig:()=>p});module.exports=m(u);function p(r){return r}0&&(module.exports={defineConfig});
2
+ //# sourceMappingURL=config.cjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/config.ts"],
4
+ "sourcesContent": ["import { ServerConfig } from '../types/config';\n\nexport function defineConfig(config: ServerConfig): ServerConfig {\n return config;\n}\n"],
5
+ "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,kBAAAE,IAAA,eAAAC,EAAAH,GAEO,SAASE,EAAaE,EAAoC,CAC7D,OAAOA,CACX",
6
+ "names": ["config_exports", "__export", "defineConfig", "__toCommonJS", "config"]
7
+ }
package/dist/config.js ADDED
@@ -0,0 +1,2 @@
1
+ function e(r){return r}export{e as defineConfig};
2
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/config.ts"],
4
+ "sourcesContent": ["import { ServerConfig } from '../types/config';\n\nexport function defineConfig(config: ServerConfig): ServerConfig {\n return config;\n}\n"],
5
+ "mappings": "AAEO,SAASA,EAAaC,EAAoC,CAC7D,OAAOA,CACX",
6
+ "names": ["defineConfig", "config"]
7
+ }