codecombat/server_setup.coffee

198 lines
8 KiB
CoffeeScript
Raw Normal View History

2014-01-27 18:36:35 -05:00
express = require 'express'
path = require 'path'
2014-02-04 17:08:20 -05:00
authentication = require 'passport'
2014-01-27 18:36:35 -05:00
useragent = require 'express-useragent'
fs = require 'graceful-fs'
log = require 'winston'
compressible = require 'compressible'
geoip = require 'geoip-lite'
2014-01-27 18:36:35 -05:00
database = require './server/commons/database'
baseRoute = require './server/routes/base'
2014-01-27 18:36:35 -05:00
user = require './server/users/user_handler'
logging = require './server/commons/logging'
config = require './server_config'
auth = require './server/routes/auth'
UserHandler = require './server/users/user_handler'
2014-11-24 20:07:29 -05:00
hipchat = require './server/hipchat'
global.tv4 = require 'tv4' # required for TreemaUtils to work
global.jsondiffpatch = require 'jsondiffpatch'
global.stripe = require('stripe')(config.stripe.secretKey)
2014-01-27 18:36:35 -05:00
2014-03-15 10:08:22 -04:00
productionLogging = (tokens, req, res) ->
2014-03-01 15:18:21 -05:00
status = res.statusCode
color = 32
if status >= 500 then color = 31
else if status >= 400 then color = 33
else if status >= 300 then color = 36
elapsed = (new Date()) - req._startTime
elapsedColor = if elapsed < 500 then 90 else 31
if (status isnt 200 and status isnt 201 and status isnt 204 and status isnt 304 and status isnt 302) or elapsed > 500
return "\x1b[90m#{req.method} #{req.originalUrl} \x1b[#{color}m#{res.statusCode} \x1b[#{elapsedColor}m#{elapsed}ms\x1b[0m"
null
2014-03-01 15:18:21 -05:00
developmentLogging = (tokens, req, res) ->
status = res.statusCode
color = 32
if status >= 500 then color = 31
else if status >= 400 then color = 33
else if status >= 300 then color = 36
elapsed = (new Date()) - req._startTime
elapsedColor = if elapsed < 500 then 90 else 31
"\x1b[90m#{req.method} #{req.originalUrl} \x1b[#{color}m#{res.statusCode} \x1b[#{elapsedColor}m#{elapsed}ms\x1b[0m"
2014-11-24 20:07:29 -05:00
setupErrorMiddleware = (app) ->
app.use (err, req, res, next) ->
if err
if err.status and 400 <= err.status < 500
res.status(err.status).send("Error #{err.status}")
return
res.status(err.status ? 500).send(error: "Something went wrong!")
message = "Express error: #{req.method} #{req.path}: #{err.message}"
log.error "#{message}, stack: #{err.stack}"
hipchat.sendHipChatMessage(message, ['tower'], {papertrail: true})
2014-11-24 20:07:29 -05:00
else
next(err)
2014-01-27 18:36:35 -05:00
setupExpressMiddleware = (app) ->
if config.isProduction
express.logger.format('prod', productionLogging)
app.use(express.logger('prod'))
app.use express.compress filter: (req, res) ->
return false if req.headers.host is 'codecombat.com' # CloudFlare will gzip it for us on codecombat.com
compressible res.getHeader('Content-Type')
else
express.logger.format('dev', developmentLogging)
app.use(express.logger('dev'))
app.use(express.static(path.join(__dirname, 'public'), maxAge: 0)) # CloudFlare overrides maxAge, and we don't want local development caching.
2014-01-27 18:36:35 -05:00
app.use(useragent.express())
app.use(express.favicon())
app.use(express.cookieParser(config.cookie_secret))
app.use(express.bodyParser())
app.use(express.methodOverride())
app.use(express.cookieSession({secret:'defenestrate'}))
setupPassportMiddleware = (app) ->
2014-02-04 17:08:20 -05:00
app.use(authentication.initialize())
app.use(authentication.session())
2014-01-27 18:36:35 -05:00
setupCountryRedirectMiddleware = (app, country="china", countryCode="CN", languageCode="zh", serverID="tokyo") ->
shouldRedirectToCountryServer = (req) ->
firstLanguage = req.acceptedLanguages[0]
speaksLanguage = firstLanguage and firstLanguage.indexOf(languageCode) isnt -1
unless config[serverID]
ip = req.headers['x-forwarded-for'] or req.connection.remoteAddress
2015-08-08 14:28:39 -04:00
ip = ip?.split(/,? /)[0] # If there are two IP addresses, say because of CloudFlare, we just take the first.
geo = geoip.lookup(ip)
#if speaksLanguage or geo?.country is countryCode
# log.info("Should we redirect to #{serverID} server? speaksLanguage: #{speaksLanguage}, firstLanguage: #{firstLanguage}, ip: #{ip}, geo: #{geo} -- so redirecting? #{geo?.country is 'CN' and speaksLanguage}")
return geo?.country is countryCode and speaksLanguage
else
#log.info("We are on #{serverID} server. speaksLanguage: #{speaksLanguage}, acceptedLanguages: #{req.acceptedLanguages[0]}")
req.country = country if speaksLanguage
return false # If the user is already redirected, don't redirect them!
app.use (req, res, next) ->
if shouldRedirectToCountryServer req
res.writeHead 302, "Location": config[country + 'Domain'] + req.url
res.end()
else
next()
2014-06-08 12:35:59 -04:00
setupOneSecondDelayMiddleware = (app) ->
2014-01-27 18:36:35 -05:00
if(config.slow_down)
app.use((req, res, next) -> setTimeout((-> next()), 1000))
setupMiddlewareToSendOldBrowserWarningWhenPlayersViewLevelDirectly = (app) ->
isOldBrowser = (req) ->
# https://github.com/biggora/express-useragent/blob/master/lib/express-useragent.js
return false unless ua = req.useragent
return true if ua.isiPad or ua.isiPod or ua.isiPhone or ua.isOpera
2014-06-30 22:16:26 -04:00
return false unless ua and ua.Browser in ['Chrome', 'Safari', 'Firefox', 'IE'] and ua.Version
2014-01-27 18:36:35 -05:00
b = ua.Browser
v = parseInt ua.Version.split('.')[0], 10
return true if b is 'Chrome' and v < 17
return true if b is 'Safari' and v < 6
return true if b is 'Firefox' and v < 21
return true if b is 'IE' and v < 10
false
app.use '/play/', (req, res, next) ->
return next() if req.query['try-old-browser-anyway'] or not isOldBrowser req
res.sendfile(path.join(__dirname, 'public', 'index_old_browser.html'))
setupRedirectMiddleware = (app) ->
app.all '/account/profile/*', (req, res, next) ->
nameOrID = req.path.split('/')[3]
res.redirect 301, "/user/#{nameOrID}/profile"
2014-07-15 10:15:21 -04:00
setupTrailingSlashRemovingMiddleware = (app) ->
app.use (req, res, next) ->
# Remove trailing slashes except for in /file/.../ URLs, because those are treated as directory listings.
return res.redirect 301, req.url[...-1] if req.url.length > 1 and req.url.slice(-1) is '/' and not /\/file\//.test req.url
next()
2014-01-27 18:36:35 -05:00
exports.setupMiddleware = (app) ->
setupCountryRedirectMiddleware app, "china", "CN", "zh", "tokyo"
setupCountryRedirectMiddleware app, "brazil", "BR", "pt-BR", "saoPaulo"
2014-01-27 18:36:35 -05:00
setupMiddlewareToSendOldBrowserWarningWhenPlayersViewLevelDirectly app
setupExpressMiddleware app
setupPassportMiddleware app
2014-06-08 12:35:59 -04:00
setupOneSecondDelayMiddleware app
setupTrailingSlashRemovingMiddleware app
setupRedirectMiddleware app
2014-11-24 20:07:29 -05:00
setupErrorMiddleware app
setupJavascript404s app
2014-01-27 18:36:35 -05:00
###Routing function implementations###
setupJavascript404s = (app) ->
app.get '/javascripts/*', (req, res) ->
res.status(404).send('Not found')
2014-01-27 18:36:35 -05:00
setupFallbackRouteToIndex = (app) ->
app.all '*', (req, res) ->
fs.readFile path.join(__dirname, 'public', 'main.html'), 'utf8', (err, data) ->
log.error "Error modifying main.html: #{err}" if err
# insert the user object directly into the html so the application can have it immediately. Sanitize </script>
user = if req.user then JSON.stringify(UserHandler.formatEntity(req, req.user)).replace(/\//g, '\\/') else '{}'
data = data.replace('"userObjectTag"', user)
res.header 'Cache-Control', 'no-cache, no-store, must-revalidate'
res.header 'Pragma', 'no-cache'
res.header 'Expires', 0
res.send 200, data
2014-01-27 18:36:35 -05:00
setupFacebookCrossDomainCommunicationRoute = (app) ->
app.get '/channel.html', (req, res) ->
res.sendfile path.join(__dirname, 'public', 'channel.html')
exports.setupRoutes = (app) ->
app.use app.router
baseRoute.setup app
2014-01-27 18:36:35 -05:00
setupFacebookCrossDomainCommunicationRoute app
setupFallbackRouteToIndex app
###Miscellaneous configuration functions###
exports.setupLogging = ->
logging.setup()
exports.connectToDatabase = ->
database.connect()
2014-01-27 18:36:35 -05:00
exports.setupMailchimp = ->
mcapi = require 'mailchimp-api'
mc = new mcapi.Mailchimp(config.mail.mailchimpAPIKey)
GLOBAL.mc = mc
exports.setExpressConfigurationOptions = (app) ->
app.set('port', config.port)
app.set('views', __dirname + '/app/views')
app.set('view engine', 'jade')
app.set('view options', { layout: false })
app.set('env', if config.isProduction then 'production' else 'development')
app.set('json spaces', 0) if config.isProduction