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 '
2014-04-17 13:12:23 -04:00
log = require ' winston '
compressible = require ' compressible '
2015-03-19 15:25:24 -04:00
geoip = require ' geoip-lite '
2014-01-27 18:36:35 -05:00
2014-02-04 15:30:05 -05:00
database = require ' ./server/commons/database '
2014-02-04 16:29:13 -05:00
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 '
2014-04-02 16:12:24 -04:00
auth = require ' ./server/routes/auth '
2014-04-17 13:12:23 -04:00
UserHandler = require ' ./server/users/user_handler '
2014-11-24 20:07:29 -05:00
hipchat = require ' ./server/hipchat '
2014-08-29 15:41:25 -04:00
global.tv4 = require ' tv4 ' # required for TreemaUtils to work
2014-10-27 20:11:48 -04:00
global.jsondiffpatch = require ' jsondiffpatch '
2014-12-05 19:47:44 -05:00
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
2014-03-03 11:10:36 -05:00
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
2014-11-25 12:28:42 -05:00
if ( status isnt 200 and status isnt 201 and status isnt 204 and status isnt 304 and status isnt 302 ) or elapsed > 500
2014-03-03 11:10:36 -05:00
return " \x 1b[90m #{ req . method } #{ req . originalUrl } \x 1b[ #{ color } m #{ res . statusCode } \x 1b[ #{ elapsedColor } m #{ elapsed } ms \x 1b[0m "
null
2014-03-01 15:18:21 -05:00
2014-10-26 13:52:22 -04: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
" \x 1b[90m #{ req . method } #{ req . originalUrl } \x 1b[ #{ color } m #{ res . statusCode } \x 1b[ #{ elapsedColor } m #{ elapsed } ms \x 1b[0m "
2014-11-24 20:07:29 -05:00
setupErrorMiddleware = (app) ->
app . use (err, req, res, next) ->
if err
2014-11-29 14:05:19 -05:00
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! " )
2014-11-29 11:43:40 -05:00
message = " Express error: #{ req . method } #{ req . path } : #{ err . message } "
2014-11-28 14:37:54 -05:00
log . error " #{ message } , stack: #{ err . stack } "
2015-02-11 23:24:12 -05:00
hipchat . sendHipChatMessage ( message , [ ' tower ' ] , { papertrail: true } )
2014-11-24 20:07:29 -05:00
else
next ( err )
2015-02-26 20:20:27 -05:00
2014-01-27 18:36:35 -05:00
setupExpressMiddleware = (app) ->
2014-03-03 11:10:36 -05:00
if config . isProduction
express . logger . format ( ' prod ' , productionLogging )
app . use ( express . logger ( ' prod ' ) )
2014-04-17 13:12:23 -04:00
app . use express . compress filter: (req, res) ->
2014-12-18 23:34:59 -05:00
return false if req . headers . host is ' codecombat.com ' # CloudFlare will gzip it for us on codecombat.com
2014-04-17 13:12:23 -04:00
compressible res . getHeader ( ' Content-Type ' )
2014-03-03 11:10:36 -05:00
else
2014-10-26 13:52:22 -04:00
express . logger . format ( ' dev ' , developmentLogging )
2014-03-03 11:10:36 -05:00
app . use ( express . logger ( ' dev ' ) )
2014-12-20 16:39:40 -05:00
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
2015-03-19 15:25:24 -04:00
setupChinaRedirectMiddleware = (app) ->
2015-03-23 18:26:44 -04:00
shouldRedirectToChinaVersion = (req) ->
2015-07-31 17:32:32 -04:00
firstLanguage = req . acceptedLanguages [ 0 ]
speaksChinese = firstLanguage and firstLanguage . indexOf ( ' zh ' ) isnt - 1
2015-03-19 15:25:24 -04:00
unless config . tokyo
2015-03-23 18:26:44 -04:00
ip = req . headers [ ' x-forwarded-for ' ] or req . connection . remoteAddress
2015-07-31 17:32:32 -04:00
ip = ip ? . split ( ' ' ) [ 0 ] # If there are two IP addresses, say because of CloudFlare, we just take the first.
2015-03-19 15:25:24 -04:00
geo = geoip . lookup ( ip )
2015-07-27 14:35:20 -04:00
if speaksChinese or geo ? . country is " CN "
2015-07-31 17:32:32 -04:00
log . info ( " Should we redirect to Tokyo server? speaksChinese: #{ speaksChinese } , firstLanguage: #{ firstLanguage } , ip: #{ ip } , geo: #{ geo } -- so redirecting? #{ geo ? . country is ' CN ' and speaksChinese } " )
2015-03-23 18:26:44 -04:00
return geo ? . country is " CN " and speaksChinese
2015-03-19 15:25:24 -04:00
else
2015-07-27 14:35:20 -04:00
log . info ( " We are on Tokyo server. speaksChinese: #{ speaksChinese } , acceptedLanguages: #{ req . acceptedLanguages [ 0 ] } " )
2015-03-23 20:00:11 -04:00
req.chinaVersion = true if speaksChinese
2015-03-23 18:26:44 -04:00
return false # If the user is already redirected, don't redirect them!
2015-03-19 15:25:24 -04:00
app . use (req, res, next) ->
2015-03-23 18:26:44 -04:00
if shouldRedirectToChinaVersion req
2015-03-19 15:25:24 -04:00
res . writeHead 302 , " Location " : config . chinaDomain + 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 ' ) )
2014-07-13 16:34:32 -04:00
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
2014-07-13 14:49:13 -04:00
setupTrailingSlashRemovingMiddleware = (app) ->
app . use (req, res, next) ->
2014-08-26 18:42:33 -04:00
# 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
2014-07-13 14:49:13 -04:00
next ( )
2014-07-13 16:34:32 -04:00
2014-01-27 18:36:35 -05:00
exports.setupMiddleware = (app) ->
2015-03-21 21:49:32 -04:00
setupChinaRedirectMiddleware app
2014-01-27 18:36:35 -05:00
setupMiddlewareToSendOldBrowserWarningWhenPlayersViewLevelDirectly app
setupExpressMiddleware app
setupPassportMiddleware app
2014-06-08 12:35:59 -04:00
setupOneSecondDelayMiddleware app
2014-07-13 14:49:13 -04:00
setupTrailingSlashRemovingMiddleware app
2014-07-13 16:34:32 -04:00
setupRedirectMiddleware app
2014-11-24 20:07:29 -05:00
setupErrorMiddleware app
2014-11-28 19:38:50 -05:00
setupJavascript404s app
2014-11-29 11:43:40 -05:00
2014-01-27 18:36:35 -05:00
###Routing function implementations###
2014-11-28 19:38:50 -05:00
setupJavascript404s = (app) ->
app . get ' /javascripts/* ' , (req, res) ->
res . status ( 404 ) . send ( ' Not found ' )
2014-01-27 18:36:35 -05:00
setupFallbackRouteToIndex = (app) ->
2014-03-12 13:07:18 -04:00
app . all ' * ' , (req, res) ->
2015-01-05 17:43:20 -05:00
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
2014-02-05 12:42:25 -05:00
2014-02-04 16:29:13 -05:00
baseRoute . setup app
2014-01-27 18:36:35 -05:00
setupFacebookCrossDomainCommunicationRoute app
setupFallbackRouteToIndex app
###Miscellaneous configuration functions###
exports.setupLogging = ->
logging . setup ( )
exports.connectToDatabase = ->
2014-02-04 15:30:05 -05:00
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 } )
2014-03-03 19:19:35 -05:00
app . set ( ' env ' , if config . isProduction then ' production ' else ' development ' )
2014-10-29 17:17:07 -04:00
app . set ( ' json spaces ' , 0 ) if config . isProduction