async function run()

in experimental/traffic-portal/server.ts [198:346]


async function run(): Promise<number> {
	const version = await getVersion();
	const parser = new ArgumentParser({
		// Nothing I can do about this, library specifies its interface.
		/* eslint-disable @typescript-eslint/naming-convention */
		add_help: true,
		/* eslint-enable @typescript-eslint/naming-convention */
		description: "Traffic Portal re-written in modern Angular"
	});
	parser.add_argument("-t", "--traffic-ops", {
		dest: "trafficOps",
		help: "Specify the Traffic Ops host/URL, including port. (Default: uses the `TO_URL` environment variable)",
		type: (arg: string) => {
			try {
				return new URL(arg);
			} catch (e) {
				if (e instanceof TypeError) {
					return new URL(`https://${arg}`);
				}
				throw e;
			}
		}
	});
	parser.add_argument("-u", "--tpv1-url", {
		dest: "tpv1Url",
		help: "Specify the Traffic Portal v1 URL. (Default: uses the `TP_V1_URL` environment variable)",
		type: (arg: string) => {
			try {
				return new URL(arg);
			} catch (e) {
				if (e instanceof TypeError) {
					return new URL(`https://${arg}`);
				}
				throw e;
			}
		}
	});
	parser.add_argument("-k", "--insecure", {
		action: "store_true",
		help: "Skip Traffic Ops server certificate validation. This affects requests from Traffic Portal to Traffic Ops AND signature" +
			" verification of any passed SSL keys/certificates"
	});
	parser.add_argument("-p", "--port", {
		default: defaultConfig.port,
		help: "Specify the port on which Traffic Portal will listen (Default: 4200)",
		type: "int"
	});
	parser.add_argument("-c", "--cert-path", {
		dest: "certPath",
		help: "Specify a location for an SSL certificate to be used by Traffic Portal. (Requires `-K`/`--key-path`. If both are omitted," +
			" will serve using HTTP)",
		type: "str"
	});
	parser.add_argument("-d", "--browser-folder", {
		default: defaultConfig.browserFolder,
		dest: "browserFolder",
		help: "Specify location for the folder that holds the browser files",
		type: "str"
	});
	parser.add_argument("-K", "--key-path", {
		dest: "keyPath",
		help: "Specify a location for an SSL certificate to be used by Traffic Portal. (Requires `-c`/`--cert-path`. If both are omitted," +
			" will serve using HTTP)",
		type: "str"
	});
	parser.add_argument("-C", "--config-file", {
		default: defaultConfigFile,
		dest: "configFile",
		help: "Specify a path to a configuration file - options are overridden by command-line flags.",
		type: "str"
	});
	parser.add_argument("-v", "--version", {
		action: "version",
		version: versionToString(version)
	});

	let config: ServerConfig;
	try {
		config = await getConfig(parser.parse_args(), version);
	} catch (e) {
		// Logger cannot be initialized before reading server configuration
		// eslint-disable-next-line no-console
		console.error(`Failed to initialize server configuration: ${e}`);
		return 1;
	}

	const logger = new Logger(console, environment.production ? LogLevel.INFO : LogLevel.DEBUG);

	// Start up the Node server
	const server = await app(config);

	if (config.useSSL) {
		let cert: string;
		let key: string;
		let ca: Array<string>;
		try {
			cert = readFileSync(config.certPath, {encoding: "utf8"});
			key = readFileSync(config.keyPath, {encoding: "utf8"});
			ca = config.certificateAuthPaths.map(c => readFileSync(c, {encoding: "utf8"}));
		} catch (e) {
			logger.error("reading SSL key/cert:", String(e));
			if (!environment.production) {
				console.trace(e);
			}
			return 1;
		}
		createServer(
			{
				ca,
				cert,
				key,
				rejectUnauthorized: !config.insecure,
			},
			server
		).listen(config.port, () => {
			logger.debug(`Node Express server listening on port ${config.port}`);
		});
		try {
			const redirectServer = createRedirectServer(
				(req, res) => {
					if (!req.url) {
						res.statusCode = 500;
						logger.error("got HTTP request for redirect that had no URL");
						res.end();
						return;
					}
					res.statusCode = 308;
					res.setHeader("Location", req.url.replace(/^[hH][tT][tT][pP]:/, "https:"));
					res.end();
				}
			);
			redirectServer.listen(80);
			redirectServer.on("error", e => {
				logger.error("redirect server encountered error:", String(e));
				if (hasProperty(e, "code", "string") && e.code === "EACCES") {
					logger.warn("access to port 80 not allowed; closing redirect server");
					redirectServer.close();
				}
			});
		} catch (e) {
			logger.warn("Failed to initialize HTTP-to-HTTPS redirect listener:", e);
		}
	} else {
		server.listen(config.port, () => {
			logger.debug(`Node Express server listening on port ${config.port}`);
		});
	}
	return 0;
}