Source: server.js

'use strict';

const hapi = require('@hapi/hapi');
const inert = require('inert');
const vision = require('vision');
const bunyan = require('bunyan');
const path = require('path');
const _ = require('lodash');

const HapiAuthCookie = require('@hapi/cookie');
const ModuleManager = require('./moduleManager');

/**
 * 
 *
 * @class ScreaminServer
 */
class ScreaminServer {
    constructor(config) {
        this._config = _.defaultsDeep(config, {
            options: {
                port: 3000,
                host: 'localhost'
            },
            skipUiBuild: false,
            wwwDir: "public"
        });
        this._server = hapi.Server(this._config.options);
        this._logger = bunyan.createLogger({ name: this._config.name, serializers:bunyan.stdSerializers });
        this._moduleManager = new ModuleManager(this._logger, this._server, this._config);
    }

    _registerAuthentication(){
        if(this._config.auth){
            const cache = this._server.cache({ segment: 'sessions', expiresIn: this._config.auth.sessionDurationInMillis || 24 * 60 * 60 * 1000 });
            this._server.app.cache = cache;

            return this._server.register(HapiAuthCookie)
                .then(()=>{
                    let options = {
                        cookie: {
                            ttl: this._config.auth.cookieDurationInMillis || 24 * 60 * 60 * 1000,
                            name: this._config.auth.cookieName || "screaminCookie",
                            password: this._config.auth.secret,
                            isSecure: this._config.auth.isSecure || false
                        },
                        redirectTo: this._config.auth.redirectTo || false,
                        validateFunc: async (request, session) => {
                            
                            const cached = await cache.get(session.sid);
                            const out = {
                                valid: !!cached
                            };
                
                            if (out.valid) {
                                out.credentials = cached.account;
                                out.credentials.scope = cached.account.authorizedApps;
                            }
                
                            return out;
                        }
                    }
                    this._server.auth.strategy('session', 'cookie', options);
                    this._server.auth.default('session');
                })
        }
    }

    _registerStaticRoutes(){
        return Promise.all([
            this._server.register(inert), 
            this._server.register(vision)
        ])
            .then(()=>{
                this._server.route({
                    method: 'GET',
                    path: '/' + this._config.wwwDir + '/{param*}',
                    handler: {
                        directory: {
                            path: path.resolve(this._config.wwwDir),
                            listing: true
                        }
                    },
                    config: {
                        auth: false
                    }
                });
                this._server.route({
                    method: 'GET',
                    path: '/api/reserved/appInfo',
                    handler: ()=>{
                        return this._moduleManager.getGuiAppInfo();
                    },
                    config: {
                        auth: false
                    }
                });
                
                if (this._config.defaultGuiRoute){
                    this._server.route({
                        method: 'GET',
                        path: '/',
                        handler: (response, h)=>{
                            return h.redirect(this._config.defaultGuiRoute)
                        },
                        config: {
                            auth: false
                        }
                    });
                }
            })
    }

    _registerModules(){
        if (_.has(this._config, "modules" ) && this._config.modules.length > 0){
            let promArr = [];
            this._config.modules.forEach((mod)=>{
                promArr.push(this._moduleManager.registerModule(mod));
            })
            return Promise.all(promArr);
        } 
    }

    startup(){
        return this._registerAuthentication()
            .then(()=>{
                return this._registerModules();
            })
            .then(()=>{
                if (this._moduleManager.hasGuiModules()){
                    return this._registerStaticRoutes();
                }
            })
            .then(()=>{
                return this._server.start();
            })
            .then(()=>{
                this._logger.info(`Server started: http://${this._config.options.host}:${this._config.options.port}`);
            })
            
    }

    shutdown() {
        return this._server.stop()
            .then(() => {
                this._logger.info("Server stopped");
            })
            .catch((err)=>{
                this._logger.error({error: err}, "Error stopping server");
            })
    }
}

module.exports = ScreaminServer;