UNPKG

expresser

Version:

A ready-to-use platform for Node.js web apps, built on top of Express.

551 lines (295 loc) 26.7 kB
<!DOCTYPE html> <html> <head> <title>app.coffee</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <link rel="stylesheet" media="all" href="public/stylesheets/normalize.css" /> <link rel="stylesheet" media="all" href="docco.css" /> </head> <body> <div class="container"> <div class="page"> <div class="header"> <h1>app.coffee</h1> <div class="toc"> <h3>Table of Contents</h3> <ol> <li> <a class="source" href="index.html"> index.coffee </a> </li> <li> <a class="source" href="app.html"> app.coffee </a> </li> <li> <a class="source" href="cron.html"> cron.coffee </a> </li> <li> <a class="source" href="database.html"> database.coffee </a> </li> <li> <a class="source" href="downloader.html"> downloader.coffee </a> </li> <li> <a class="source" href="events.html"> events.coffee </a> </li> <li> <a class="source" href="firewall.html"> firewall.coffee </a> </li> <li> <a class="source" href="imaging.html"> imaging.coffee </a> </li> <li> <a class="source" href="logger.html"> logger.coffee </a> </li> <li> <a class="source" href="mailer.html"> mailer.coffee </a> </li> <li> <a class="source" href="settings.html"> settings.coffee </a> </li> <li> <a class="source" href="sockets.html"> sockets.coffee </a> </li> <li> <a class="source" href="utils.html"> utils.coffee </a> </li> </ol> </div> </div> <h2 id="expresser-app">EXPRESSER APP</h2> <p>The Express app server, with built-in sockets, firewall and New Relic integration. <!-- @see Settings.app @see Firewall @see Sockets --></p> <div class='highlight'><pre><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">App</span></span> express = <span class="hljs-built_in">require</span> <span class="hljs-string">"express"</span> fs = <span class="hljs-built_in">require</span> <span class="hljs-string">"fs"</span> http = <span class="hljs-built_in">require</span> <span class="hljs-string">"http"</span> https = <span class="hljs-built_in">require</span> <span class="hljs-string">"https"</span> lodash = <span class="hljs-built_in">require</span> <span class="hljs-string">"lodash"</span> net = <span class="hljs-built_in">require</span> <span class="hljs-string">"net"</span> path = <span class="hljs-built_in">require</span> <span class="hljs-string">"path"</span></pre></div> <p>Current node environment and HTTP server handler are set on init.</p> <div class='highlight'><pre> nodeEnv = <span class="hljs-literal">null</span></pre></div> <p>Internal modules will be set on <code>init</code>.</p> <div class='highlight'><pre> firewall = <span class="hljs-literal">null</span> logger = <span class="hljs-literal">null</span> settings = <span class="hljs-literal">null</span> sockets = <span class="hljs-literal">null</span> utils = <span class="hljs-literal">null</span></pre></div> <p>@property [Object] Exposes the Express HTTP or HTTPS <code>server</code> object.</p> <div class='highlight'><pre> <span class="hljs-attribute">server</span>: <span class="hljs-literal">null</span></pre></div> <p>@property [Array<Object>] Array of additional middlewares to be use by the Express server. Please note that if you’re adding middlewares manually you must do it BEFORE calling <code>init</code>.</p> <div class='highlight'><pre> <span class="hljs-attribute">extraMiddlewares</span>: []</pre></div> <h2 id="init">INIT</h2> <p>Init the Express server. If New Relic settings are set it will automatically require and use the <code>newrelic</code> 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.</p> <div class='highlight'><pre> <span class="hljs-attribute">init</span>: <span class="hljs-function"><span class="hljs-params">(options)</span> =&gt;</span> <span class="hljs-keyword">if</span> lodash.isArray options options = {<span class="hljs-attribute">extraMiddlewares</span>: options} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> options? options = {}</pre></div> <p>Load settings and utils.</p> <div class='highlight'><pre> settings = <span class="hljs-built_in">require</span> <span class="hljs-string">"./settings.coffee"</span> utils = <span class="hljs-built_in">require</span> <span class="hljs-string">"./utils.coffee"</span> nodeEnv = process.env.NODE_ENV</pre></div> <p>Init New Relic, if enabled, and set default error handler.</p> <div class='highlight'><pre> <span class="hljs-property">@initNewRelic</span>() <span class="hljs-property">@setErrorHandler</span>()</pre></div> <p>Require logger.</p> <div class='highlight'><pre> logger = <span class="hljs-built_in">require</span> <span class="hljs-string">"./logger.coffee"</span> logger.debug <span class="hljs-string">"App"</span>, <span class="hljs-string">"init"</span>, options.extraMiddlewares</pre></div> <p>Configure Express server and start server.</p> <div class='highlight'><pre> <span class="hljs-property">@configureServer</span> options <span class="hljs-property">@startServer</span>()</pre></div> <p>Init new Relic, depending on its settings (enabled, appName and LicenseKey).</p> <div class='highlight'><pre> <span class="hljs-attribute">initNewRelic</span>:<span class="hljs-function"> =&gt;</span> enabled = settings.newRelic.enabled appName = process.env.NEW_RELIC_APP_NAME <span class="hljs-keyword">or</span> settings.newRelic.appName licKey = process.env.NEW_RELIC_LICENSE_KEY <span class="hljs-keyword">or</span> settings.newRelic.licenseKey</pre></div> <p>Check if New Relic settings are available, and if so, start the New Relic agent.</p> <div class='highlight'><pre> <span class="hljs-keyword">if</span> enabled <span class="hljs-keyword">and</span> appName? <span class="hljs-keyword">and</span> appName <span class="hljs-keyword">isnt</span> <span class="hljs-string">""</span> <span class="hljs-keyword">and</span> licKey? <span class="hljs-keyword">and</span> licKey <span class="hljs-keyword">isnt</span> <span class="hljs-string">""</span> targetFile = path.resolve path.dirname(<span class="hljs-built_in">require</span>.main.filename), <span class="hljs-string">"newrelic.js"</span></pre></div> <p>Make sure the newrelic.js file exists on the app root, and create one if it doesn’t.</p> <div class='highlight'><pre> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> fs.existsSync targetFile <span class="hljs-keyword">if</span> process.versions.node.indexOf(<span class="hljs-string">".10."</span>) &gt; <span class="hljs-number">0</span> enc = {<span class="hljs-attribute">encoding</span>: settings.general.encoding} <span class="hljs-keyword">else</span> enc = settings.general.encoding</pre></div> <p>Set values of newrelic.js file and write it to the app root.</p> <div class='highlight'><pre> newRelicJson = <span class="hljs-string">"exports.config = {app_name: ['<span class="hljs-subst">#{appName}</span>'], license_key: '<span class="hljs-subst">#{licKey}</span>', logging: {level: 'trace'}};"</span> fs.writeFileSync targetFile, newRelicJson, enc <span class="hljs-built_in">console</span>.log <span class="hljs-string">"App"</span>, <span class="hljs-string">"Original newrelic.js file was copied to the app root, app_name and license_key were set."</span> <span class="hljs-built_in">require</span> <span class="hljs-string">"newrelic"</span> <span class="hljs-built_in">console</span>.log <span class="hljs-string">"App"</span>, <span class="hljs-string">"Started New Relic agent for <span class="hljs-subst">#{appName}</span>."</span></pre></div> <p>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.</p> <div class='highlight'><pre> <span class="hljs-attribute">setErrorHandler</span>:<span class="hljs-function"> =&gt;</span> process.<span class="hljs-literal">on</span> <span class="hljs-string">"exit"</span>, <span class="hljs-function"><span class="hljs-params">(sig)</span> -&gt;</span> <span class="hljs-keyword">if</span> nodeEnv? <span class="hljs-keyword">and</span> nodeEnv.indexOf(<span class="hljs-string">"test"</span>) &lt; <span class="hljs-number">0</span> <span class="hljs-built_in">console</span>.warn <span class="hljs-string">"App"</span>, <span class="hljs-string">"Terminating Expresser app..."</span>, Date(Date.now()), sig <span class="hljs-keyword">try</span> logger.flushLocal() <span class="hljs-keyword">catch</span> err <span class="hljs-built_in">console</span>.warn <span class="hljs-string">"App"</span>, <span class="hljs-string">"Could not flush buffered logs to disk."</span></pre></div> <p>Configure the server. Set views, options, use Express modules, etc.</p> <div class='highlight'><pre> <span class="hljs-attribute">configureServer</span>: <span class="hljs-function"><span class="hljs-params">(options)</span> =&gt;</span> midBodyParser = <span class="hljs-built_in">require</span> <span class="hljs-string">"body-parser"</span> midCookieParser = <span class="hljs-built_in">require</span> <span class="hljs-string">"cookie-parser"</span> midSession = <span class="hljs-built_in">require</span> <span class="hljs-string">"express-session"</span> midCompression = <span class="hljs-built_in">require</span> <span class="hljs-string">"compression"</span> midErrorHandler = <span class="hljs-built_in">require</span> <span class="hljs-string">"errorhandler"</span></pre></div> <p>Create express v4 app.</p> <div class='highlight'><pre> <span class="hljs-property">@server</span> = express() settings.updateFromPaaS() <span class="hljs-keyword">if</span> settings.app.paas</pre></div> <p>Set view options, use Jade for HTML templates.</p> <div class='highlight'><pre> <span class="hljs-property">@server</span>.set <span class="hljs-string">"views"</span>, settings.path.viewsDir <span class="hljs-property">@server</span>.set <span class="hljs-string">"view engine"</span>, settings.app.viewEngine <span class="hljs-property">@server</span>.set <span class="hljs-string">"view options"</span>, { <span class="hljs-attribute">layout</span>: <span class="hljs-literal">false</span> }</pre></div> <p>Enable firewall?</p> <div class='highlight'><pre> <span class="hljs-keyword">if</span> settings.firewall.enabled firewall = <span class="hljs-built_in">require</span> <span class="hljs-string">"./firewall.coffee"</span> firewall.init <span class="hljs-property">@server</span></pre></div> <p>Use Express basic handlers.</p> <div class='highlight'><pre> <span class="hljs-property">@server</span>.use midBodyParser() <span class="hljs-property">@server</span>.use midCookieParser settings.app.cookieSecret <span class="hljs-keyword">if</span> settings.app.cookieEnabled <span class="hljs-property">@server</span>.use midSession {<span class="hljs-attribute">secret</span>: settings.app.sessionSecret} <span class="hljs-keyword">if</span> settings.app.sessionEnabled</pre></div> <p>Use HTTP compression only if enabled on settings.</p> <div class='highlight'><pre> <span class="hljs-property">@server</span>.use midCompression <span class="hljs-keyword">if</span> settings.app.compressionEnabled</pre></div> <p>Fix connect assets helper context.</p> <div class='highlight'><pre> connectAssetsOptions = settings.app.connectAssets connectAssetsOptions.helperContext = <span class="hljs-property">@server</span>.locals</pre></div> <p>Connect assets and dynamic compiling.</p> <div class='highlight'><pre> ConnectAssets = (<span class="hljs-built_in">require</span> <span class="hljs-string">"connect-assets"</span>) connectAssetsOptions <span class="hljs-property">@server</span>.use ConnectAssets</pre></div> <p>Check for extra middlewares to be added.</p> <div class='highlight'><pre> <span class="hljs-keyword">if</span> options.extraMiddlewares? <span class="hljs-keyword">if</span> lodash.isArray options.extraMiddlewares <span class="hljs-property">@extraMiddlewares</span>.push mw <span class="hljs-keyword">for</span> mw <span class="hljs-keyword">in</span> options.extraMiddlewares <span class="hljs-keyword">else</span> <span class="hljs-property">@extraMiddlewares</span>.push options.extraMiddlewares</pre></div> <p>Add more middlewares, if any (for example passport for authentication).</p> <div class='highlight'><pre> <span class="hljs-keyword">if</span> <span class="hljs-property">@extraMiddlewares</span>.length &gt; <span class="hljs-number">0</span> <span class="hljs-property">@server</span>.use mw <span class="hljs-keyword">for</span> mw <span class="hljs-keyword">in</span> <span class="hljs-property">@extraMiddlewares</span></pre></div> <p>Configure development environment to dump exceptions and show stack.</p> <div class='highlight'><pre> <span class="hljs-keyword">if</span> nodeEnv <span class="hljs-keyword">is</span> <span class="hljs-string">"development"</span> <span class="hljs-property">@server</span>.use midErrorHandler {<span class="hljs-attribute">dumpExceptions</span>: <span class="hljs-literal">true</span>, <span class="hljs-attribute">showStack</span>: <span class="hljs-literal">true</span>}</pre></div> <p>Configure production environment.</p> <div class='highlight'><pre> <span class="hljs-keyword">if</span> nodeEnv <span class="hljs-keyword">is</span> <span class="hljs-string">"production"</span> <span class="hljs-property">@server</span>.use midErrorHandler()</pre></div> <p>Use Express static routing.</p> <div class='highlight'><pre> <span class="hljs-property">@server</span>.use express.static settings.path.publicDir</pre></div> <p>If debug is on, log requests to the console.</p> <div class='highlight'><pre> <span class="hljs-keyword">if</span> settings.general.debug <span class="hljs-property">@server</span>.use <span class="hljs-function"><span class="hljs-params">(req, res, next)</span> =&gt;</span> ip = utils.getClientIP req method = req.method url = req.url</pre></div> <p>Check if request flash is present before logging.</p> <div class='highlight'><pre> <span class="hljs-keyword">if</span> req.flash? <span class="hljs-keyword">and</span> lodash.isFunction req.flash <span class="hljs-built_in">console</span>.log <span class="hljs-string">"Request from <span class="hljs-subst">#{ip}</span>"</span>, method, url, req.flash() <span class="hljs-keyword">else</span> <span class="hljs-built_in">console</span>.log <span class="hljs-string">"Request from <span class="hljs-subst">#{ip}</span>"</span>, method, url next() <span class="hljs-keyword">if</span> next?</pre></div> <p>Start the server using HTTP or HTTPS, depending on the settings.</p> <div class='highlight'><pre> <span class="hljs-attribute">startServer</span>:<span class="hljs-function"> =&gt;</span> <span class="hljs-keyword">if</span> settings.app.ssl.enabled <span class="hljs-keyword">and</span> settings.path.sslKeyFile? <span class="hljs-keyword">and</span> settings.path.sslCertFile? sslKeyFile = utils.getFilePath settings.path.sslKeyFile sslCertFile = utils.getFilePath settings.path.sslCertFile</pre></div> <p>Certificate files were found? Proceed, otherwise alert the user and throw an error.</p> <div class='highlight'><pre> <span class="hljs-keyword">if</span> sslKeyFile? <span class="hljs-keyword">and</span> sslCertFile? sslKey = fs.readFileSync sslKeyFile, {<span class="hljs-attribute">encoding</span>: settings.general.encoding} sslCert = fs.readFileSync sslCertFile, {<span class="hljs-attribute">encoding</span>: settings.general.encoding} sslOptions = {<span class="hljs-attribute">key</span>: sslKey, <span class="hljs-attribute">cert</span>: sslCert} server = https.createServer sslOptions, <span class="hljs-property">@server</span> <span class="hljs-keyword">else</span> logger.error <span class="hljs-string">"App"</span>, <span class="hljs-string">"init"</span>, <span class="hljs-string">"Cannot find certificate files."</span>, settings.path.sslKeyFile, settings.path.sslCertFile <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Error <span class="hljs-string">"The certificate files could not be found. Please check the 'Path.sslKeyFile' and 'Path.sslCertFile' settings."</span> <span class="hljs-keyword">else</span> server = http.createServer <span class="hljs-property">@server</span></pre></div> <p>Enable sockets?</p> <div class='highlight'><pre> <span class="hljs-keyword">if</span> settings.sockets.enabled sockets = <span class="hljs-built_in">require</span> <span class="hljs-string">"./sockets.coffee"</span> sockets.init server</pre></div> <p>Start the server and log output.</p> <div class='highlight'><pre> <span class="hljs-keyword">try</span> <span class="hljs-keyword">if</span> settings.app.ip? <span class="hljs-keyword">and</span> settings.app.ip <span class="hljs-keyword">isnt</span> <span class="hljs-string">""</span> server.listen settings.app.port, settings.app.ip logger.info <span class="hljs-string">"App"</span>, settings.general.appTitle, <span class="hljs-string">"Listening on <span class="hljs-subst">#{settings.app.ip}</span> - <span class="hljs-subst">#{settings.app.port}</span>"</span> <span class="hljs-keyword">else</span> server.listen settings.app.port logger.info <span class="hljs-string">"App"</span>, settings.general.appTitle, <span class="hljs-string">"Listening on <span class="hljs-subst">#{settings.app.port}</span>"</span></pre></div> <p>Using SSL and redirector port is set? Then create the http server.</p> <div class='highlight'><pre> <span class="hljs-keyword">if</span> settings.app.ssl.enabled <span class="hljs-keyword">and</span> settings.app.ssl.redirectorPort &gt; <span class="hljs-number">0</span> logger.info <span class="hljs-string">"App"</span>, <span class="hljs-string">"<span class="hljs-subst">#{settings.general.appTitle}</span> will redirect HTTP <span class="hljs-subst">#{settings.app.ssl.redirectorPort}</span> to HTTPS on <span class="hljs-subst">#{settings.app.port}</span>."</span> redirServer = express() redirServer.get <span class="hljs-string">"*"</span>, <span class="hljs-function"><span class="hljs-params">(req, res)</span> -&gt;</span> res.redirect <span class="hljs-string">"https://<span class="hljs-subst">#{req.host}</span>:<span class="hljs-subst">#{settings.app.port}</span><span class="hljs-subst">#{req.url}</span>"</span> <span class="hljs-property">@redirectorServer</span> = http.createServer redirServer <span class="hljs-property">@redirectorServer</span>.listen settings.app.ssl.redirectorPort <span class="hljs-keyword">catch</span> ex logger.error <span class="hljs-string">"App"</span>, <span class="hljs-string">"Could not start the server!"</span>, ex</pre></div> <h2 id="helper-and-utils">HELPER AND UTILS</h2> <p>Helper 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.</p> <div class='highlight'><pre> <span class="hljs-attribute">renderView</span>: <span class="hljs-function"><span class="hljs-params">(req, res, view, options)</span> =&gt;</span> options = {} <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> options? options.device = utils.getClientDevice req options.title = settings.general.appTitle <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> options.title? res.render view, options logger.debug <span class="hljs-string">"App"</span>, <span class="hljs-string">"Render"</span>, view, options</pre></div> <p>Helper 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.</p> <div class='highlight'><pre> <span class="hljs-attribute">renderError</span>: <span class="hljs-function"><span class="hljs-params">(res, message, statusCode)</span> =&gt;</span> message = JSON.stringify message statusCode = <span class="hljs-number">500</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> statusCode? res.statusCode = <span class="hljs-number">500</span> res.send <span class="hljs-string">"Server error: <span class="hljs-subst">#{message}</span>"</span> logger.error <span class="hljs-string">"App"</span>, <span class="hljs-string">"HTTP Error"</span>, statusCode, message, res</pre></div> <h2 id="singleton-implementation">Singleton implementation</h2> <div class='highlight'><pre>App.<span class="hljs-function"><span class="hljs-title">getInstance</span> = -&gt;</span> <span class="hljs-property">@instance</span> = <span class="hljs-keyword">new</span> App() <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> <span class="hljs-property">@instance</span>? <span class="hljs-keyword">return</span> <span class="hljs-property">@instance</span> <span class="hljs-built_in">module</span>.<span class="hljs-built_in">exports</span> = <span class="hljs-built_in">exports</span> = App.getInstance()</pre></div> <div class="fleur">h</div> </div> </div> </body> </html>