nextorigin-express-skeleton
Version:
Express app skeleton for nextorigin
111 lines (84 loc) • 3.23 kB
text/coffeescript
http = require "http"
path = require "path"
express = require "express"
favicon = require "serve-favicon"
Flannel = require "flannel"
compression = require "compression"
bodyParser = require "body-parser"
render = require "express-rendertype"
GracefulExit = require "express-graceful-exit"
class Skeleton
logPrefix: "(Skeleton)"
port: 3000
shutdownTimeout: 2*60 + 10
constructor: (@options) ->
unless @Flannel?.winston
@Flannel = Flannel.init Console: level: "debug"
@Flannel.shirt this
@debug "initializing"
@address = @options.address
@port = process.env.PORT or @options.port or @port
@app or= express()
@server = http.createServer @app
@app.use @Flannel.morgan " info"
@loadMiddleware()
@bindRoutes()
@handleRouteErrors()
process.on "SIGTERM", @gracefulShutdown
process.on "uncaughtException", @errThenGracefulShutdown
loadMiddleware: ->
# view engine setup
@app.set "views", @options.views if @options.views
@app.set "view engine", "pug"
@app.use GracefulExit.middleware @app
@app.use compression()
@app.use express.static (@options.static.root or @options.static), (@options.static.options or {}) if @options.static
@app.use favicon @options.favicon if @options.favicon
@app.use bodyParser.json()
@app.use bodyParser.urlencoded extended: !!@options.urlencoded_extended
@app.use @options.render or render.auto "text"
redirectToHttps: (req, res, next) ->
unless proto = req.headers["x-forwarded-proto"]
forwarded = req.headers["forwarded"]
forwarded = /proto=(http[s]?)/.exec forwarded
proto = forwarded and forwarded[1]
if proto and proto isnt "https"
dest = "https://#{req.hostname}#{req.url}"
return res.redirect dest
next()
health: (req, res, next) ->
res.send "OK"
listen: (port = @port) =>
@server.on "error", @handleListeningError
@server.on "listening", @listening
@server.listen port, @address
handleListeningError: (error) =>
throw error if error.syscall isnt "listen"
bind = if typeof @port is "string" then "Pipe #{@port}" else "Port #{@port}"
switch error.code
when "EACCES"
console.error "#{bind} requires elevated privileges"
process.exit 1
when "EADDRINUSE"
console.error "#{bind} is already in use"
process.exit 1
else
throw error
listening: => @info "listening on #{@server.address().address}:#{@server.address().port}"
bindRoutes: => @debug "stub for loading routes"
handleRouteErrors: =>
@app.use render.Errors.Error404
@app.use render.FancyErrors.auto "text", null, @log if (@app.get "env") is "development"
@app.use render.Errors.auto "text", null, @log
close: (callback) =>
@server.close callback
errThenGracefulShutdown: (err) =>
@err err.stack
@gracefulShutdown()
gracefulShutdown: =>
GracefulExit.gracefulExitHandler @app, this,
log: true,
logger: (@Flannel.shirt().info.bind this),
suicideTimeout: @shutdownTimeout * 1000
delay: (timeout, fn) -> setTimeout (fn.bind this), timeout
module.exports = Skeleton