The Express app server, with built-in sockets, firewall and New Relic integration.
class App
express = require "express"
fs = require "fs"
http = require "http"
https = require "https"
lodash = require "lodash"
net = require "net"
path = require "path"Current node environment and HTTP server handler are set on init.
nodeEnv = nullInternal modules will be set on init.
firewall = null
logger = null
settings = null
sockets = null
utils = null@property [Object] Exposes the Express HTTP or HTTPS server object.
server: null@property [Array
extraMiddlewares: []Init the Express server. If New Relic settings are set it will automatically
require and use the newrelic module. Firewall and Sockets modules will be
used only if enabled on the settings.
@param [Object] options App init options. If passed as an array, assume it’s the array with extra middlewares.
@option options [Array] extraMiddlewares Array with extra middlewares to be loaded.
init: (options) =>
if lodash.isArray options
options = {extraMiddlewares: options}
else if not options?
options = {}Load settings and utils.
settings = require "./settings.coffee"
utils = require "./utils.coffee"
nodeEnv = process.env.NODE_ENVInit New Relic, if enabled, and set default error handler.
@initNewRelic()
@setErrorHandler()Require logger.
logger = require "./logger.coffee"
logger.debug "App", "init", options.extraMiddlewaresConfigure Express server and start server.
@configureServer options
@startServer()Init new Relic, depending on its settings (enabled, appName and LicenseKey).
initNewRelic: =>
enabled = settings.newRelic.enabled
appName = process.env.NEW_RELIC_APP_NAME or settings.newRelic.appName
licKey = process.env.NEW_RELIC_LICENSE_KEY or settings.newRelic.licenseKeyCheck if New Relic settings are available, and if so, start the New Relic agent.
if enabled and appName? and appName isnt "" and licKey? and licKey isnt ""
targetFile = path.resolve path.dirname(require.main.filename), "newrelic.js"Make sure the newrelic.js file exists on the app root, and create one if it doesn’t.
if not fs.existsSync targetFile
if process.versions.node.indexOf(".10.") > 0
enc = {encoding: settings.general.encoding}
else
enc = settings.general.encodingSet values of newrelic.js file and write it to the app root.
newRelicJson = "exports.config = {app_name: ['#{appName}'], license_key: '#{licKey}', logging: {level: 'trace'}};"
fs.writeFileSync targetFile, newRelicJson, enc
console.log "App", "Original newrelic.js file was copied to the app root, app_name and license_key were set."
require "newrelic"
console.log "App", "Started New Relic agent for #{appName}."Log proccess termination to the console. This will force flush any buffered logs to disk. Do not log the exit if running under test environment.
setErrorHandler: =>
process.on "exit", (sig) ->
if nodeEnv? and nodeEnv.indexOf("test") < 0
console.warn "App", "Terminating Expresser app...", Date(Date.now()), sig
try
logger.flushLocal()
catch err
console.warn "App", "Could not flush buffered logs to disk."Configure the server. Set views, options, use Express modules, etc.
configureServer: (options) =>
midBodyParser = require "body-parser"
midCookieParser = require "cookie-parser"
midSession = require "express-session"
midCompression = require "compression"
midErrorHandler = require "errorhandler"Create express v4 app.
@server = express()
settings.updateFromPaaS() if settings.app.paasSet view options, use Jade for HTML templates.
@server.set "views", settings.path.viewsDir
@server.set "view engine", settings.app.viewEngine
@server.set "view options", { layout: false }Enable firewall?
if settings.firewall.enabled
firewall = require "./firewall.coffee"
firewall.init @serverUse Express basic handlers.
@server.use midBodyParser()
@server.use midCookieParser settings.app.cookieSecret if settings.app.cookieEnabled
@server.use midSession {secret: settings.app.sessionSecret} if settings.app.sessionEnabledUse HTTP compression only if enabled on settings.
@server.use midCompression if settings.app.compressionEnabledFix connect assets helper context.
connectAssetsOptions = settings.app.connectAssets
connectAssetsOptions.helperContext = @server.localsConnect assets and dynamic compiling.
ConnectAssets = (require "connect-assets") connectAssetsOptions
@server.use ConnectAssetsCheck for extra middlewares to be added.
if options.extraMiddlewares?
if lodash.isArray options.extraMiddlewares
@extraMiddlewares.push mw for mw in options.extraMiddlewares
else
@extraMiddlewares.push options.extraMiddlewaresAdd more middlewares, if any (for example passport for authentication).
if @extraMiddlewares.length > 0
@server.use mw for mw in @extraMiddlewaresConfigure development environment to dump exceptions and show stack.
if nodeEnv is "development"
@server.use midErrorHandler {dumpExceptions: true, showStack: true}Configure production environment.
if nodeEnv is "production"
@server.use midErrorHandler()Use Express static routing.
@server.use express.static settings.path.publicDirIf debug is on, log requests to the console.
if settings.general.debug
@server.use (req, res, next) =>
ip = utils.getClientIP req
method = req.method
url = req.urlCheck if request flash is present before logging.
if req.flash? and lodash.isFunction req.flash
console.log "Request from #{ip}", method, url, req.flash()
else
console.log "Request from #{ip}", method, url
next() if next?Start the server using HTTP or HTTPS, depending on the settings.
startServer: =>
if settings.app.ssl.enabled and settings.path.sslKeyFile? and settings.path.sslCertFile?
sslKeyFile = utils.getFilePath settings.path.sslKeyFile
sslCertFile = utils.getFilePath settings.path.sslCertFileCertificate files were found? Proceed, otherwise alert the user and throw an error.
if sslKeyFile? and sslCertFile?
sslKey = fs.readFileSync sslKeyFile, {encoding: settings.general.encoding}
sslCert = fs.readFileSync sslCertFile, {encoding: settings.general.encoding}
sslOptions = {key: sslKey, cert: sslCert}
server = https.createServer sslOptions, @server
else
logger.error "App", "init", "Cannot find certificate files.", settings.path.sslKeyFile, settings.path.sslCertFile
throw new Error "The certificate files could not be found. Please check the 'Path.sslKeyFile' and 'Path.sslCertFile' settings."
else
server = http.createServer @serverEnable sockets?
if settings.sockets.enabled
sockets = require "./sockets.coffee"
sockets.init serverStart the server and log output.
try
if settings.app.ip? and settings.app.ip isnt ""
server.listen settings.app.port, settings.app.ip
logger.info "App", settings.general.appTitle, "Listening on #{settings.app.ip} - #{settings.app.port}"
else
server.listen settings.app.port
logger.info "App", settings.general.appTitle, "Listening on #{settings.app.port}"Using SSL and redirector port is set? Then create the http server.
if settings.app.ssl.enabled and settings.app.ssl.redirectorPort > 0
logger.info "App", "#{settings.general.appTitle} will redirect HTTP #{settings.app.ssl.redirectorPort} to HTTPS on #{settings.app.port}."
redirServer = express()
redirServer.get "*", (req, res) -> res.redirect "https://#{req.host}:#{settings.app.port}#{req.url}"
@redirectorServer = http.createServer redirServer
@redirectorServer.listen settings.app.ssl.redirectorPort
catch ex
logger.error "App", "Could not start the server!", exHelper to render pages. The request, response and view are mandatory, and the options argument is optional. @param [Object] req The request object. @param [Object] res The response object. @param [String] view The view name. @param [Object] options Options passed to the view, optional.
renderView: (req, res, view, options) =>
options = {} if not options?
options.device = utils.getClientDevice req
options.title = settings.general.appTitle if not options.title?
res.render view, options
logger.debug "App", "Render", view, optionsHelper to send error responses. When the server can’t return a valid result, send an error response with the specified status code. @param [Object] req The response object. @param [String] message The message to be sent to the client. @param [Integer] statusCode The response status code, optional, default is 500.
renderError: (res, message, statusCode) =>
message = JSON.stringify message
statusCode = 500 if not statusCode?
res.statusCode = 500
res.send "Server error: #{message}"
logger.error "App", "HTTP Error", statusCode, message, resApp.getInstance = ->
@instance = new App() if not @instance?
return @instance
module.exports = exports = App.getInstance()