UNPKG

@verdaccio/node-api

Version:
204 lines (188 loc) 6.36 kB
/* eslint-disable */ import constants from 'constants'; import buildDebug from 'debug'; import fs from 'fs'; import http from 'http'; import https from 'https'; import _, { assign, isFunction } from 'lodash'; import url from 'url'; import { findConfigFile, parseConfigFile } from '@verdaccio/config'; import { API_ERROR } from '@verdaccio/core'; import { setup } from '@verdaccio/logger'; import expressServer from '@verdaccio/server'; import fastifyServer from '@verdaccio/server-fastify'; import { ConfigYaml, HttpsConfKeyCert, HttpsConfPfx } from '@verdaccio/types'; import { getListListenAddresses } from './cli-utils'; import { displayExperimentsInfoBox } from './experiments'; const debug = buildDebug('verdaccio:node-api'); function unlinkAddressPath(addr) { if (addr.path && fs.existsSync(addr.path)) { fs.unlinkSync(addr.path); } } /** * Return a native HTTP/HTTPS server instance * @param config * @param addr * @param app */ export function createServerFactory(config: ConfigYaml, addr, app) { let serverFactory; if (addr.proto === 'https') { debug('https enabled'); try { let httpsOptions = { // disable insecure SSLv2 and SSLv3 secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3, }; const keyCertConfig = config.https as HttpsConfKeyCert; const pfxConfig = config.https as HttpsConfPfx; // https must either have key and cert or a pfx and (optionally) a passphrase if (!((keyCertConfig.key && keyCertConfig.cert) || pfxConfig.pfx)) { // logHTTPSError(configPath); throw Error('bad format https configuration'); } if (pfxConfig.pfx) { const { pfx, passphrase } = pfxConfig; httpsOptions = assign(httpsOptions, { pfx: fs.readFileSync(pfx), passphrase: passphrase || '', }); } else { const { key, cert, ca } = keyCertConfig; httpsOptions = assign(httpsOptions, { key: fs.readFileSync(key), cert: fs.readFileSync(cert), ...(ca && { ca: fs.readFileSync(ca), }), }); } // TODO: enable http2 as feature // if (config.server.http2) <-- check if force http2 serverFactory = https.createServer(httpsOptions, app); } catch (err: any) { throw new Error(`cannot create https server: ${err.message}`); } } else { // http debug('http enabled'); serverFactory = http.createServer(app); } if ( config.server && typeof config.server.keepAliveTimeout !== 'undefined' && // @ts-ignore config.server.keepAliveTimeout !== 'null' ) { // library definition for node is not up to date (doesn't contain recent 8.0 changes) serverFactory.keepAliveTimeout = config.server.keepAliveTimeout * 1000; } // FIXE: I could not find the reason of this code. unlinkAddressPath(addr); return serverFactory; } /** * Start the server on the port defined * @param config * @param port * @param version * @param pkgName */ export async function initServer( config: ConfigYaml, port: string | void, version: string, pkgName: string ): Promise<void> { return new Promise(async (resolve, reject) => { // FIXME: get only the first match, the multiple address will be removed const [addr] = getListListenAddresses(port, config.listen); const logger = setup(config?.log as any); displayExperimentsInfoBox(config.flags); let app; if (process.env.VERDACCIO_SERVER === 'fastify') { app = await fastifyServer(config); app.listen({ port: addr.port, host: addr.host }, (err) => { if (err) { reject(err); } else { resolve(); } }); } else { app = await expressServer(config); const serverFactory = createServerFactory(config, addr, app); serverFactory .listen(addr.port || addr.path, addr.host, (): void => { // send a message for test if (isFunction(process.send)) { process.send({ verdaccio_started: true, }); } const addressServer = `${ addr.path ? url.format({ protocol: 'unix', pathname: addr.path, }) : url.format({ protocol: addr.proto, hostname: addr.host, port: addr.port, pathname: '/', }) }`; logger.info(`http address ${addressServer}`); logger.info(`version: ${version}`); resolve(); }) .on('error', function (err): void { reject(err); process.exitCode = 1; }); function handleShutdownGracefully() { logger.info('received shutdown signal - closing server gracefully...'); serverFactory.close(() => { logger.info('server closed.'); process.exit(0); }); } for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP']) { // Use once() so that receiving double signals exit the app. process.once(signal, handleShutdownGracefully); } } }); } /** * Exposes a server factory to be instantiated programmatically. * ```ts const app = await runServer(); // default configuration const app = await runServer('./config/config.yaml'); const app = await runServer({ configuration }); app.listen(4000, (event) => { // do something }); ``` * @param config */ export async function runServer(config?: string | ConfigYaml): Promise<any> { let configurationParsed: ConfigYaml; if (config === undefined || typeof config === 'string') { const configPathLocation = findConfigFile(config); configurationParsed = parseConfigFile(configPathLocation); } else if (_.isObject(config)) { configurationParsed = config; } else { throw new Error(API_ERROR.CONFIG_BAD_FORMAT); } setup(configurationParsed.log as any); displayExperimentsInfoBox(configurationParsed.flags); // FIXME: get only the first match, the multiple address will be removed const [addr] = getListListenAddresses(undefined, configurationParsed.listen); const app = await expressServer(configurationParsed); return createServerFactory(configurationParsed, addr, app); }