spserver/lib/spserver.mjs

156 lines
4.5 KiB
JavaScript
Raw Permalink Normal View History

import fs from 'fs'
import http from 'http'
import https from 'https'
import path from 'path'
import url from 'url'
import _ from 'lodash'
import nStatic from 'node-static'
import config from './config.mjs'
import logger from './logger.mjs'
// The different config sources sometimes manipulate different setting names.
// E.g. command line flags maniuplate root settings, but config files can
// manipulate settings at the prod/debug level. Resolve all of these into a
// final object of settings.
function _resolveFinalSettings(settings) {
var finalSettings = {};
var env = config.get('NODE_ENV');
if (!settings) {
settings = config.get();
}
if (!settings[env]) {
settings[env] = {};
}
// For 'name', 'file', 'serve', 'ip', and 'port', default to the global setting rather than an
// individual environment's setting, because it might have been set via command-line flags
_(['name', 'file', 'serve', 'ip', 'port']).forEach(function (field) {
finalSettings[field] = settings[field] || settings[env][field];
});
finalSettings.rooturlpath = config.get('rooturlpath') || config.get('ROOT_URL_PATH') || '/';
// For 'staticOptions', there are no command-line flags, so individual configuration options
// override global defaults where set
finalSettings.staticOptions = _.defaultsDeep(settings.staticOptions, settings[env].staticOptions);
// Make a template function so we can just pass that in downstream
finalSettings.template = (settings.template || settings[env].template) ?
function (contents) {
// Note: template is run with _original_, non-resolved settings
return _.template(contents)(settings);
} : null;
return finalSettings;
}
function _rerouteRootUrl(reqUrl, rootUrl) {
var parsedUrl = url.parse(reqUrl);
parsedUrl.pathname = path.normalize(
parsedUrl.pathname.replace(rootUrl, '/') || '/'
);
return url.format(parsedUrl);
}
class SPServer {
constructor(settings, opts = {}) {
Object.assign(this, {
fs: opts.fs || fs,
})
var finalSettings = _resolveFinalSettings(settings);
var fileServer = new nStatic.Server(
path.resolve(finalSettings.serve),
finalSettings.staticOptions
);
var base = SPServer.generateBase(finalSettings.file ? path.resolve(finalSettings.file) : null, finalSettings);
let requestHandler = function (req, res) {
var startTime = new Date().getTime();
var isFinished = false
var done = function () {
if (isFinished) return
isFinished = true
var requestTime = new Date().getTime() - startTime;
logger.debug('[RES]', req.method + ':', '(' + res.statusCode + ')', req.url,
'took', requestTime, 'ms');
};
req.url = _rerouteRootUrl(req.url, finalSettings.rooturlpath);
res.addListener('finish', done);
res.addListener('close', done);
req.addListener('end', function () {
fileServer.serve(req, res, function (err) {
if (err) {
if (err.status === 404 && base) {
return base(req, res);
} else if (err.status === 404) {
res.writeHead(err.status, err.headers);
res.end(err.message);
} else {
logger.debug('[REQ]', req.method + ':', req.url);
logger.error(err);
res.writeHead(err.status, err.headers);
res.end(err.message);
}
}
});
}).resume();
}
var server
if (config.get('ssl')) {
server = https.createServer({
key: fs.readFileSync(config.get('sslkey')),
cert: fs.readFileSync(config.get('sslcert'))
}, requestHandler)
} else {
server = http.createServer(requestHandler)
}
server.listen(finalSettings.port, finalSettings.ip);
logger.info('Started single-page server: ' + finalSettings.name
+ ', base file: ' + (finalSettings.file || '<none>')
+ ', static folder: ' + finalSettings.serve
+ ', port: ' + finalSettings.port
+ (config.get('ssl') ? ' [SSL]' : '')
);
this.server = server
}
static generateBase(file, finalSettings, useFs = fs) {
if (!file) {
return null;
}
if (_.endsWith(file, 'js')) {
throw new Error('javascript file has been deprecated')
}
var contents = useFs.readFileSync(file, { encoding: 'utf-8' });
if (finalSettings.template) {
contents = finalSettings.template(contents)
}
return function(req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.end(contents);
};
}
};
export default SPServer;