Home Reference Source Repository

src/app/server/index.js

/* global USE_DEFAULT_KOA_MIDDLEWARES HAS_KOA_MIDDLEWARES KOA_MIDDLEWARES ROC_PATH __DEV__  */

import { readFileSync } from 'fs';
import debug from 'debug';
import http from 'http';
import https from 'https';
import koa from 'koa';
import serve from 'koa-static';
import mount from 'koa-mount';

import addTrailingSlash from 'koa-add-trailing-slashes';
import normalizePath from 'koa-normalize-path';
import lowercasePath from 'koa-lowercase-path';
import removeTrailingSlash from 'koa-remove-trailing-slashes';

import { merge, getSettings, getAbsolutePath } from 'roc';

/**
 * Creates a server instance.
 *
 * @example
 * import { createServer } from 'roc-web/app';
 *
 * const server = createServer({
 *    serve: 'static',
 *    favicon: 'static/favicon.png'
 * });
 *
 * server.start();
 *
 * @param {rocServerOptions} options - Options for the server. Will override configuration in roc.config.js.
 * @param {Function[]} beforeUserMiddlewares - Middlewares that should be added before the user middlewares.
 * @returns {rocServer} server - Roc server instace.
 */
export default function createServer(options = {}, beforeUserMiddlewares = []) {
    const logger = debug('roc:server');

    const server = koa();
    const settings = merge(getSettings('runtime'), options);

    if (USE_DEFAULT_KOA_MIDDLEWARES) {
        const middlewares = require('./middlewares')(settings);
        middlewares.forEach((middleware) => server.use(middleware));
    }

    if (beforeUserMiddlewares.length) {
        beforeUserMiddlewares.forEach((middleware) => server.use(middleware));
    }

    if (HAS_KOA_MIDDLEWARES) {
        const middlewares = require(KOA_MIDDLEWARES)(settings);
        middlewares.forEach((middleware) => server.use(middleware));
    }

    const makeServe = (toServe = []) => {
        toServe = [].concat(toServe);
        for (const path of toServe) {
            // We use defer here to no try to fetch a file if we have set something on body, something that is done in
            // redirect. This because https://github.com/koajs/send/issues/51
            server.use(serve(path, { defer: true }));
        }
    };

    if (settings.koa.normalize.enabled) {
        server.use(normalizePath({ defer: settings.koa.normalize.defer }));
    }

    if (settings.koa.lowercase.enabled) {
        server.use(lowercasePath({ defer: settings.koa.lowercase.defer }));
    }

    if (settings.koa.trailingSlashes.enabled === true) {
        server.use(addTrailingSlash({ defer: settings.koa.trailingSlashes.defer }));
    } else if (settings.koa.trailingSlashes.enabled === false) {
        server.use(removeTrailingSlash());
    }

    // Serve folders
    makeServe(settings.serve);

    function start(port, httpsPort) {
        port = port || process.env.PORT || settings.port;
        httpsPort = httpsPort || process.env.HTTPS_PORT || settings.https.port;

        const app = koa();
        app.use(mount(ROC_PATH, server));

        // Start the server on HTTP
        http.createServer(app.callback()).listen(port);
        logger(`Server started on port ${port} (HTTP) and served from ${ROC_PATH}`);

        // If a HTTPS port is defined we will try to start the application with SSL/TLS
        if (httpsPort) {
            let key = getAbsolutePath(settings.https.key);
            let cert = getAbsolutePath(settings.https.cert);

            // Add a self-signed certificate for development purposes if non is provided.
            if (__DEV__ && !key && !cert) {
                key = require('roc-web/certificate').getKey();
                cert = require('roc-web/certificate').getCert();
            }

            if (key && cert) {
                const httpsOptions = {
                    key: readFileSync(key),
                    cert: readFileSync(cert)
                };

                https.createServer(httpsOptions, app.callback()).listen(httpsPort);
                logger(`Server started on port ${httpsPort} (HTTPS) and served from ${ROC_PATH}`);
            } else {
                logger('You have defined a HTTPS port but not given any certificate files to use…');
            }
        }

        if (process.send) {
            process.send('online');
        }
    }

    return {
        server,
        start
    };
}