@interopio/gateway-server
Version:
[](https://www.npmjs.com/package/@interopio/gateway-server)
898 lines (802 loc) • 31.9 kB
JavaScript
;
const { parseArgs } = require('node:util');
const { configure } = require('@interopio/gateway/logging/core');
// import { GatewayServer } from './src/index.ts';
// import { mkcert, argon2, manage } from './src/tools/index.ts';
const { mkcert, argon2, manage, build } = require('@interopio/gateway-server/tools');
const { GatewayServer } = require('@interopio/gateway-server');
// ---------------------------------------------------------------------------
// Declarative command registry — drives both help text and parseArgs calls
// ---------------------------------------------------------------------------
const commands = {
run: {
description: 'Start the gateway server',
options: {
port: { type: 'string', short: 'p', help: 'Port or port range (default: 0 i.e. random)', example: '8385, 8000-8100, 3000,4000-4050' },
host: { type: 'string', short: 'H', help: 'Network address to bind to', example: 'localhost, 127.0.0.1, 0.0.0.0, ::1' },
user: { type: 'string', short: 'u', help: 'Enable basic auth and set admin username' },
auth: { type: 'boolean', help: 'Enable/disable authentication (--no-auth overrides config)' },
ssl: { type: 'boolean', short: 'S', help: 'Enable HTTPS (auto-generates certs if needed)' },
tls: { type: 'boolean', help: 'Alias for --ssl' },
cert: { type: 'string', help: 'SSL/TLS certificate file (default: ./gateway-server.crt)' },
key: { type: 'string', help: 'SSL/TLS private key file (default: ./gateway-server.key)' },
ca: { type: 'string', help: 'CA certificate file (default: ./gateway-ca.crt)' },
'ca-key': { type: 'string', help: 'CA private key file (default: ./gateway-ca.key)' },
gateway: { type: 'boolean', short: 'g', help: 'Enable/disable gateway endpoint (--no-gateway)' },
static: { type: 'string', short: 's', multiple: true, help: 'Serve static files from location' },
config: { type: 'string', short: 'c', help: 'Server configuration file (JSON)' },
debug: { type: 'boolean', help: 'Enable debug logging (default: info)' },
},
allowNegative: true,
examples: [
'gateway-server run -p 3000',
'gateway-server run -u admin --port 8385,8388 --debug',
'gateway-server run -p 8443 --ssl --user admin --gateway',
'gateway-server run --port 42443 --tls --ca-key ./ssl/ca.key --host example.com --gateway',
'gateway-server run -p 3000 --static ./public --config ./server-config.json',
'gateway-server run --config ./server-config.json --no-gateway --no-ssl --no-auth',
],
},
build: {
description: 'Build a Single Executable Application (SEA)',
positionals: '[entryPoints...]',
options: {
'app-name': { type: 'string', help: 'Base name for the executable (default: package.json name)' },
'app-version': { type: 'string', help: 'App version in output dir name (default: package.json version)' },
executable: { type: 'string', help: 'Path to the Node.js binary (default: current node)' },
outdir: { type: 'string', short: 'o', help: 'Output directory for build artifacts (default: ./build)' },
},
examples: [
'gateway-server build',
'gateway-server build --app-version 1.2.3',
'gateway-server build ./src/main.cjs --app-name io-bridge --executable /usr/local/bin/node22 --app-version 2.0.0',
],
},
manage: {
description: 'Send management commands to a running gateway server',
positionals: '<command> [key=value... or json]',
options: {
path: { type: 'string', help: 'Gateway control socket (named pipe / Unix socket)', example: '\\\\\\\\.\\\\pipe\\\\glue42-gateway-xxx, /tmp/gateway.sock' },
port: { type: 'string', short: 'p', help: 'TCP port of the management server' },
timeout: { type: 'string', help: 'Connection timeout in ms (default: 5000)' },
},
examples: [
'gateway-server manage --path \\\\.\\pipe\\glue42-gateway-xxx info',
'gateway-server manage --path \\\\.\\pipe\\glue42-gateway-xxx shutdown',
'gateway-server manage --path \\\\.\\pipe\\glue42-gateway-xxx custom-cmd key1=value1 count=42',
'gateway-server manage --path \\\\.\\pipe\\glue42-gateway-xxx \'{"command":"update-auth","type":"basic"}\'',
],
},
passwd: {
description: 'Generate password hash (default: Argon2)',
options: {
stdin: { type: 'boolean', help: 'Read password from stdin (for piping)' },
},
examples: [
'gateway-server passwd',
'echo "mySecret123" | gateway-server passwd --stdin',
],
},
mkcert: {
description: 'Generate client/server certificate signed by Dev CA',
positionals: '[name...]',
options: {
client: { type: 'boolean', help: 'Generate client certificate (default: server)' },
user: { type: 'string', short: 'u', help: 'Common Name (default: dev-user, --client only)' },
ca: { type: 'string', help: 'CA certificate file (default: ./gateway-ca.crt)' },
'ca-key': { type: 'string', help: 'CA private key file (default: ./gateway-ca.key)' },
key: { type: 'string', help: 'Output private key file' },
cert: { type: 'string', help: 'Output certificate file' },
},
examples: [
'gateway-server mkcert',
'gateway-server mkcert localhost 127.0.0.1 IP:192.168.1.100',
'gateway-server mkcert --client EMAIL:john.doe@example.com',
'gateway-server mkcert example.com *.example.com --key ./gateway-server.key --cert ./gateway-server.crt',
],
},
};
// ---------------------------------------------------------------------------
// Help text generator
// ---------------------------------------------------------------------------
function formatOption(name, opt, negatable, col) {
const shortFlag = opt.short ? `-${opt.short}, ` : ' ';
const flag = (opt.type === 'boolean' && negatable) ? `--[no-]${name}` : `--${name}`;
const argHint = opt.type === 'string' ? ` <${name.replace(/-/g, '_')}>` : '';
const left = ` ${shortFlag}${flag}${argHint}`;
const pad = Math.max(2, col - left.length);
let line = left + ' '.repeat(pad) + (opt.help || '');
if (opt.example) {
line += `\n${' '.repeat(col)}examples: ${opt.example}`;
}
return line;
}
/** Compute column width from a set of options. */
function optionColumnWidth(options, negatable) {
let max = 0;
for (const [name, opt] of Object.entries(options)) {
const shortFlag = opt.short ? `-X, ` : ' ';
const flag = (opt.type === 'boolean' && negatable) ? `--[no-]${name}` : `--${name}`;
const argHint = opt.type === 'string' ? ` <${name.replace(/-/g, '_')}>` : '';
max = Math.max(max, ` ${shortFlag}${flag}${argHint}`.length);
}
return max + 2; // 2 chars minimum padding
}
function showHelp() {
const lines = [
'',
'Usage: gateway-server <command> [options]',
'',
'Commands:',
];
// Command list
for (const [name, cmd] of Object.entries(commands)) {
lines.push(` ${name.padEnd(24)} ${cmd.description}`);
}
// Per-command options
for (const [name, cmd] of Object.entries(commands)) {
lines.push('');
const positionals = cmd.positionals ? ` ${cmd.positionals}` : '';
lines.push(`${name}${positionals} options:`);
const col = optionColumnWidth(cmd.options, cmd.allowNegative);
for (const [optName, opt] of Object.entries(cmd.options)) {
lines.push(formatOption(optName, opt, cmd.allowNegative, col));
}
}
// Global options
lines.push('');
lines.push('Global options:');
lines.push(' -v, --version Show version information and exit');
lines.push(' -h, --help Show this help message and exit');
// Examples
lines.push('');
lines.push('Examples:');
for (const cmd of Object.values(commands)) {
for (const ex of (cmd.examples || [])) {
lines.push(` ${ex}`);
}
}
console.log(lines.join('\n'));
process.exit(0);
}
/** Extract parseArgs-compatible options from a command definition (strips help/example). */
function argsOptions(name) {
const cmd = commands[name];
const opts = { help: { type: 'boolean', short: 'h' } };
for (const [k, v] of Object.entries(cmd.options)) {
const o = { type: v.type };
if (v.short) o.short = v.short;
if (v.multiple) o.multiple = true;
opts[k] = o;
}
return opts;
}
/** Show help for a single command and exit. */
function showCommandHelp(name) {
const cmd = commands[name];
const positionals = cmd.positionals ? ` ${cmd.positionals}` : '';
const lines = [
'',
`Usage: gateway-server ${name}${positionals} [options]`,
'',
cmd.description,
'',
'Options:',
];
const col = optionColumnWidth(cmd.options, cmd.allowNegative);
for (const [optName, opt] of Object.entries(cmd.options)) {
lines.push(formatOption(optName, opt, cmd.allowNegative, col));
}
const helpLeft = ' -h, --help';
lines.push(helpLeft + ' '.repeat(Math.max(2, col - helpLeft.length)) + 'Show this help message');
if (cmd.examples && cmd.examples.length > 0) {
lines.push('');
lines.push('Examples:');
for (const ex of cmd.examples) {
lines.push(` ${ex}`);
}
}
console.log(lines.join('\n'));
process.exit(0);
}
function showVersion() {
console.log(GatewayServer.VERSION);
process.exit(0);
}
/** Read the nearest package.json starting from cwd, walking up. */
function readPackageJson() {
const { readFileSync } = require('node:fs');
const { join, dirname } = require('node:path');
let dir = process.cwd();
while (true) {
try {
return JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8'));
} catch {
const parent = dirname(dir);
if (parent === dir) return {};
dir = parent;
}
}
}
// Parse command-line arguments
const { values: globalValues, positionals } = parseArgs({
args: process.argv.slice(2),
options: {
version: { type: 'boolean', short: 'v' },
help: { type: 'boolean', short: 'h' },
},
strict: false,
allowPositionals: true,
});
// Check for global flags first
if (globalValues.version) {
showVersion();
}
if (globalValues.help && positionals.length === 0) {
showHelp();
}
// Determine command (required)
if (positionals.length === 0) {
console.error('Error: Command is required');
console.log('Use --help to see available commands');
process.exit(1);
}
const command = positionals[0];
// Route to appropriate command handler
switch (command) {
case 'run':
runServer().catch(e => {console.error(e); process.exit(1);});
break;
case 'build':
buildCommand().catch(e => {console.error(e); process.exit(1);});
break;
case 'manage':
manageCommand().catch(e => {console.error(e); process.exit(1);});
break;
case 'passwd':
passwdCommand().catch(e => {console.error(e); process.exit(1);});
break;
case 'mkcert':
mkcertCommand().catch(e => {console.error(e); process.exit(1);});
break;
default:
console.error(`Error: Unknown command "${command}"`);
console.log('Use --help to see available commands');
process.exit(1);
}
// Command: build - Build a Single Executable Application (SEA)
async function buildCommand() {
const { values, positionals } = parseArgs({
args: process.argv.slice(3),
options: argsOptions('build'),
strict: true,
allowPositionals: true,
});
if (values.help) showCommandHelp('build');
// Default app-name and app-version from the nearest package.json
const pkg = readPackageJson();
const name = values['app-name'] || (pkg.name ? pkg.name.replace(/^@.*\//, '') : 'gateway-server');
const entryPoints = positionals.length > 0 ? positionals : [`./bin/${name}.cjs`];
const executable = values.executable;
if (!executable) {
try {
if (require('node:sea').isSea()) {
console.error('Error: --executable is required when running from a single executable application.');
console.error('Provide the path to a Node.js binary, e.g. --executable /usr/local/bin/node');
process.exit(1);
}
} catch {
// not a SEA build
}
}
const targetPath = await build.buildSea(name, {
version: values['app-version'] || pkg.version,
outDir: values.outdir,
esbuild: {
entryPoints,
define: {
'process.env.NODE_ENV': '"production"',
'process.env.WS_NO_UTF_8_VALIDATE': 'true',
'process.env.WS_NO_BUFFER_UTIL': 'true'
},
},
config: {
executable,
execArgv: ['--experimental-websocket'],
execArgvExtension: 'cli',
},
});
console.log(`SEA executable built: ${targetPath}`);
process.exit(0);
}
// Command: manage - Send management commands to a running gateway server
async function manageCommand() {
const { values, positionals } = parseArgs({
args: process.argv.slice(3),
options: argsOptions('manage'),
strict: true,
allowPositionals: true,
});
if (values.help) showCommandHelp('manage');
const socketPath = values.path;
const port = values.port ? parseInt(values.port, 10) : undefined;
const timeout = values.timeout ? parseInt(values.timeout, 10) : undefined;
const firstArg = positionals[0];
if (!socketPath && !port) {
console.error('Error: --path or --port is required');
console.error('Example: gateway-server manage --path \\\\.\\pipe\\glue42-gateway-xxx info');
process.exit(1);
}
if (!firstArg) {
console.error('Error: management command is required');
console.error('Available commands: info, shutdown');
console.error('Or pass a JSON object: gateway-server manage --path ... \'{"command":"info"}\'');
process.exit(1);
}
// Try to parse first argument as JSON (entire command object)
let commandParams;
if (firstArg.startsWith('{')) {
try {
commandParams = JSON.parse(firstArg);
if (!commandParams.command) {
console.error('Error: JSON command must have a "command" property');
process.exit(1);
}
}
catch (e) {
console.error(`Error: invalid JSON: ${e.message}`);
process.exit(1);
}
}
else {
// Parse as command name + key=value pairs
commandParams = { command: firstArg };
for (let i = 1; i < positionals.length; i++) {
const arg = positionals[i];
const eqIndex = arg.indexOf('=');
if (eqIndex === -1) {
console.error(`Error: invalid parameter "${arg}". Expected format: key=value`);
process.exit(1);
}
const key = arg.slice(0, eqIndex);
let value = arg.slice(eqIndex + 1);
// Try to parse as JSON for numbers, booleans, arrays, objects
try {
value = JSON.parse(value);
} catch {
// Keep as string if not valid JSON
}
commandParams[key] = value;
}
}
const server = socketPath ? { path: socketPath } : { port };
try {
const result = await manage.sendCommand(server, commandParams, { timeout });
if (result === undefined) {
console.log('<no response>');
}
else if (typeof result === 'string') {
console.log(result);
}
else {
console.log(JSON.stringify(result, null, 2));
}
process.exit(0);
}
catch (e) {
console.error(`Error: ${e.message || e}`);
process.exit(1);
}
}
// Command: passwd - Generate password hash
async function passwdCommand() {
const { values } = parseArgs({
args: process.argv.slice(3),
options: argsOptions('passwd'),
strict: true,
allowPositionals: false,
});
if (values.help) showCommandHelp('passwd');
let password/*: string = undefined!*/;
// Read password from stdin if specified
if (values.stdin) {
password = await readPasswordFromStdin();
}
// If no password provided, prompt interactively
if (!password) {
password = await promptPassword('Enter password: ');
}
if (!password) {
console.error('Error: password is required');
process.exit(1);
}
const hashValue = await argon2.hash(password);
const hash = `{argon2id}${hashValue}`;
console.log(hash);
process.exit(0);
}
// Read password from piped stdin (non-interactive)
async function readPasswordFromStdin() {
// Check if stdin is a TTY (interactive terminal)
if (process.stdin.isTTY) {
// Interactive mode - use readline with hidden input
return promptPassword('Password: ');
}
// Piped mode - read until EOF or newline
const chunks/*: Uint8Array[]*/ = [];
for await (const chunk of process.stdin) {
chunks.push(chunk);
}
const content = Buffer.concat(chunks).toString('utf8');
// Strip trailing newline
return content.replace(/\r?\n$/, '');
}
// Prompt for password with masked input
async function promptPassword(prompt/*: string*/) {
return new Promise/*<string>*/((resolve) => {
process.stdout.write(prompt);
// Only set raw mode if stdin is a TTY
if (!process.stdin.isTTY) {
console.error('Error: Interactive password prompt requires a terminal');
process.exit(1);
}
process.stdin.setRawMode(true);
process.stdin.resume();
process.stdin.setEncoding('utf8');
let password = '';
const cleanup = () => {
process.stdin.setRawMode(false);
process.stdin.pause();
process.stdin.removeListener('data', onData);
process.stdout.write('\n');
};
const onData = (char/*: string*/) => {
// Ctrl+C
if (char === '\u0003') {
cleanup();
process.exit(1);
}
// Enter
if (char === '\r' || char === '\n') {
cleanup();
resolve(password);
return;
}
// Backspace
if (char === '\u007f' || char === '\b') {
if (password.length > 0) {
password = password.slice(0, -1);
process.stdout.write('\b \b');
}
return;
}
// Regular character
password += char;
process.stdout.write('*');
};
process.stdin.on('data', onData);
});
}
// Command: mkcert - Generate client or server certificate
async function mkcertCommand() {
const { values, positionals } = parseArgs({
args: process.argv.slice(3),
options: argsOptions('mkcert'),
strict: true,
allowPositionals: true,
});
if (values.help) showCommandHelp('mkcert');
const isClientCert = values.client || false;
const user = values.user || 'dev-user';
const caCert = values.ca || './gateway-ca.crt';
const caKey = values['ca-key'] || './gateway-ca.key';
let keyPath = values.key;
let certPath = values.cert;
const sanEntries = positionals;
// Default output paths
// Users typically specify just --cert, and key should go in the same file
if (!keyPath && !certPath) {
// Neither specified: use defaults based on certificate type
if (isClientCert) {
// Client certificate: combined file by default
keyPath = './gateway-client.key';
certPath = './gateway-client.crt';
}
else {
// Server certificate: separate files by default
keyPath = './gateway-server.key';
certPath = './gateway-server.crt';
}
}
else if (keyPath && !certPath) {
// Only --key specified: cert goes in the same file (combined)
certPath = keyPath;
}
else if (!keyPath && certPath) {
// Only --cert specified: key goes in the same file (combined)
keyPath = certPath;
}
// else: both specified, use as-is
if (sanEntries.length === 0) {
if (isClientCert) {
sanEntries.push(user);
}
else {
// Default SAN entry for server certs
sanEntries.push('localhost');
}
}
const { readFile, writeFile } = await import('node:fs/promises');
const { KEYUTIL, X509 } = await import('jsrsasign');
// Load CA key
let caKeyObj/*: ReturnType<typeof KEYUTIL.getKey>*/;
try {
const caKeyPem = await readFile(caKey, 'utf8');
caKeyObj = KEYUTIL.getKey(caKeyPem);
} catch (error) {
console.error(`Error: Cannot read CA key from ${caKey}`);
console.error(error?.['message']);
process.exit(1);
}
// Extract issuer from CA certificate
let issuer/*: string*/;
try {
const caCertPem = await readFile(caCert, 'utf8');
const caCertObj = new X509();
caCertObj.readCertPEM(caCertPem);
issuer = caCertObj.getSubjectString();
} catch (error) {
console.error(`Error: Cannot read CA certificate from ${caCert}`);
console.error(error?.['message']);
process.exit(1);
}
// Generate certificate using SAN entries
const cert = mkcert.generateCert(caKeyObj, issuer, sanEntries, isClientCert);
// Write files
try {
if (keyPath === certPath) {
// Concatenate cert and key into single file (cert first, then key)
const combined = cert.cert + cert.key;
await writeFile(keyPath, combined, { mode: 0o600 });
console.log(`${isClientCert ? 'Client' : 'Server'} certificate generated successfully:`);
console.log(` Combined (cert+key): ${keyPath}`);
if (sanEntries.length > 0) {
console.log(` SAN: ${sanEntries.join(', ')}`);
}
}
else {
// Write separate files
await writeFile(keyPath, cert.key, { mode: 0o600 });
await writeFile(certPath, cert.cert, { mode: 0o644 });
console.log(`${isClientCert ? 'Client' : 'Server'} certificate generated successfully:`);
console.log(` Private key: ${keyPath}`);
console.log(` Certificate: ${certPath}`);
if (sanEntries.length > 0) {
console.log(` SAN: ${sanEntries.join(', ')}`);
}
}
process.exit(0);
}
catch (error) {
console.error(`Error: Cannot write certificate files`);
console.error(error?.['message']);
process.exit(1);
}
}
// Command: run - Start the server
async function runServer() {
const { values } = parseArgs({
args: process.argv.slice(3),
options: argsOptions('run'),
strict: true,
allowNegative: true,
allowPositionals: false,
});
if (values.help) showCommandHelp('run');
const options = {
port: values.port,
host: values.host,
user: values.user,
debug: values.debug || false,
ssl: values.ssl || values.tls || false,
noSsl: values.ssl === false, // --no-ssl explicitly set
cert: values.cert,
key: values.key,
ca: values.ca,
caKey: values['ca-key'],
config: values.config,
gateway: values.gateway,
noGateway: values.gateway === false, // --no-gateway explicitly set
static: values.static,
};
configure({
level: options.debug ? {
gateway: 'debug',
"gateway.node": 'info',
} : 'info',
});
// Load server configuration from file if specified
let serverConfig/*: GatewayServer.ServerConfig*/ = {};
const { access, constants } = await import('node:fs/promises');
const { resolve, extname } = await import('node:path');
let configPath/*: string*/;
for (const fileName of
[
'gateway-server.config.js',
'gateway-server.config.mjs',
'gateway-server.config.cjs',
'gateway-server.config.ts',
'gateway-server.config.mts',
'gateway-server.config.cts',
'gateway-server.config.json'
]) {
configPath = resolve(process.cwd(), fileName);
try {
await access(configPath, constants.R_OK);
// found readable config file, load it
break;
}
catch {
configPath = undefined;
// continue to next candidate
}
}
if (options.config) {
configPath = options.config;
}
if (configPath) {
try {
if (['.json'].includes(extname(configPath))) {
// JSON config file - read and parse manually
const { readFile } = await import('node:fs/promises');
const configContent = await readFile(configPath, 'utf8');
serverConfig = JSON.parse(configContent);
}
else {
// JS/TS config file - import dynamically
const { pathToFileURL } = await import('node:url');
const importedConfig = await import(pathToFileURL(configPath));
serverConfig = importedConfig.default || importedConfig;
}
}
catch (error) {
console.error(`Error: Cannot read server config from ${configPath}`);
console.error(error?.['message']);
process.exit(1);
}
}
// Apply CLI overrides to server config
// CLI arguments take precedence over config file
// Override port if specified
if (values.port !== undefined) {
serverConfig.port = options.port;
}
// Override host if specified
if (values.host !== undefined) {
serverConfig.host = options.host;
}
// Override SSL configuration
if (options.noSsl) {
// --no-ssl: delete SSL config completely
delete serverConfig.ssl;
options.ssl = false;
} else if (options.ssl) {
// --ssl specified: merge CLI args with existing config
if (!serverConfig.ssl) {
serverConfig.ssl = {};
}
// Only set properties if CLI options are explicitly provided
if (options.key !== undefined) {
serverConfig.ssl.key = options.key;
}
if (options.cert !== undefined) {
serverConfig.ssl.cert = options.cert;
}
if (options.ca !== undefined) {
serverConfig.ssl.ca = options.ca;
}
}
// else: use whatever is in serverConfig (if any)
// Determine effective SSL state
const effectiveSsl = options.ssl || (!options.noSsl && serverConfig?.ssl !== undefined);
// Configure authentication
let auth/*: GatewayServer.AuthConfig*/;
if (values.auth === false) {
// --no-auth: delete auth config from server config and set to 'none'
delete serverConfig.auth;
auth = { type: 'none' };
}
else if (serverConfig.auth) {
// Use auth from config file if present
auth = serverConfig.auth;
// Allow --user CLI option to override auth.user.name from config
if (options.user) {
if (!auth.user) {
auth.user = {name: options.user};
}
auth.user.name = options.user;
}
}
else {
// No auth in config file, determine from CLI args or defaults
auth = {
type: options.user ? 'basic' : effectiveSsl ? 'x509' : 'none'
};
if (options.user) {
auth.user = { name: options.user };
}
if (effectiveSsl && auth.type === 'x509') {
auth.x509 = {
key: options.caKey || './gateway-ca.key',
// principalAltName: 'email',
};
// Ensure SSL config exists and has proper mutual TLS settings
if (!serverConfig.ssl) {
serverConfig.ssl = {};
}
// Enable client certificate verification with proper mutual TLS
serverConfig.ssl.requestCert = true;
// Enforce trust verification to prevent certificate spoofing
// rejectUnauthorized must be true to ensure only trusted client certs are accepted
serverConfig.ssl.rejectUnauthorized = true;
// Set CA for client certificate verification (required for mutual TLS)
// Use the same CA that signs client certificates
if (!serverConfig.ssl.ca) {
serverConfig.ssl.ca = options.ca || './gateway-ca.crt';
}
}
}
// Handle SSL certificate auto-generation when --ssl is specified via CLI
// but config file doesn't have server certificates
if (options.ssl && effectiveSsl && serverConfig.ssl) {
const hasCert = serverConfig.ssl.cert || serverConfig.ssl.key;
if (!hasCert) {
// SSL enabled but no server cert/key provided
// We need a CA key for auto-generating server certificates
// Set default CA key path if not already configured
if (!auth.x509) {
auth.x509 = {};
}
if (!auth.x509.key) {
// Use CLI option, or default to ./gateway-ca.key
auth.x509.key = options.caKey || './gateway-ca.key';
}
}
}
// Override gateway configuration
if (options.noGateway) {
// --no-gateway: delete gateway config completely
delete serverConfig.gateway;
} else if (options.gateway) {
// --gateway specified: enable gateway endpoint, respect existing config
if (!serverConfig.gateway) {
serverConfig.gateway = {};
}
// Set defaults only if not already present in config
if (!serverConfig.gateway.route) {
serverConfig.gateway.route = '/';
}
if (!serverConfig.gateway.authorize) {
serverConfig.gateway.authorize = { access: auth.type !== 'none' ? 'authenticated' : 'permitted' };
}
}
// else: use whatever is in serverConfig (if any)
// Override static resources if specified
if (options.static && options.static.length > 0) {
if (!serverConfig.resources) {
serverConfig.resources = {locations: options.static};
}
serverConfig.resources.locations = options.static;
}
// Set auth in server config
serverConfig.auth = auth;
const server = await GatewayServer.Factory({
...(serverConfig)
});
process.on('SIGINT', async () => {
await server.close();
process.exit(0);
});
// process.title = `${GatewayServer.VERSION} ${server.address.port}`;
}