2013-11-18 02:39:08 -06:00
var path = require ( 'path' ) ,
fs = require ( 'fs' ) ,
2014-01-04 18:05:15 -05:00
nconf = require ( 'nconf' ) ,
2013-11-18 02:39:08 -06:00
express = require ( 'express' ) ,
2013-07-10 16:22:03 -04:00
express _namespace = require ( 'express-namespace' ) ,
2013-04-22 15:17:41 -04:00
WebServer = express ( ) ,
2014-01-04 18:05:15 -05:00
server ,
2013-09-30 16:28:22 -04:00
winston = require ( 'winston' ) ,
2013-10-04 01:46:50 -04:00
validator = require ( 'validator' ) ,
2013-10-14 16:41:34 -04:00
async = require ( 'async' ) ,
2013-11-18 02:39:08 -06:00
utils = require ( '../public/src/utils' ) ,
2013-12-02 17:10:26 -05:00
db = require ( './database' ) ,
2013-11-18 02:39:08 -06:00
user = require ( './user' ) ,
notifications = require ( './notifications' ) ,
auth = require ( './routes/authentication' ) ,
meta = require ( './meta' ) ,
plugins = require ( './plugins' ) ,
2014-01-25 16:39:27 -05:00
logger = require ( './logger' ) ,
templates = require ( './../public/src/templates' ) ,
2014-02-25 14:13:09 -05:00
translator = require ( './../public/src/translator' ) ,
2014-02-27 14:55:41 -05:00
controllers = require ( './controllers' ) ,
2014-02-25 14:13:09 -05:00
admin = require ( './routes/admin' ) ,
apiRoute = require ( './routes/api' ) ,
feedsRoute = require ( './routes/feeds' ) ,
2014-02-25 16:50:58 -05:00
metaRoute = require ( './routes/meta' ) ;
2013-05-02 15:57:43 -04:00
2014-01-04 18:05:15 -05:00
if ( nconf . get ( 'ssl' ) ) {
server = require ( 'https' ) . createServer ( {
key : fs . readFileSync ( nconf . get ( 'ssl' ) . key ) ,
2014-01-04 18:09:43 -05:00
cert : fs . readFileSync ( nconf . get ( 'ssl' ) . cert )
2014-01-04 18:05:15 -05:00
} , WebServer ) ;
} else {
server = require ( 'http' ) . createServer ( WebServer ) ;
}
2014-01-25 16:39:27 -05:00
module . exports . server = server ;
2014-01-04 18:05:15 -05:00
2014-02-21 23:52:23 -05:00
// Signals
2014-02-22 02:27:14 -05:00
var shutdown = function ( code ) {
winston . info ( '[app] Shutdown (SIGTERM/SIGINT) Initialised.' ) ;
db . close ( ) ;
winston . info ( '[app] Database connection closed.' ) ;
2014-02-21 23:52:23 -05:00
2014-02-27 10:06:31 -05:00
winston . info ( '[app] Shutdown complete.' ) ;
2014-02-22 02:27:14 -05:00
process . exit ( ) ;
} ,
restart = function ( ) {
if ( process . send ) {
winston . info ( '[app] Restarting...' ) ;
process . send ( 'nodebb:restart' ) ;
} else {
winston . error ( '[app] Could not restart server. Shutting down.' ) ;
shutdown ( ) ;
}
} ;
process . on ( 'SIGTERM' , shutdown ) ;
process . on ( 'SIGINT' , shutdown ) ;
process . on ( 'SIGHUP' , restart ) ;
process . on ( 'uncaughtException' , function ( err ) {
winston . error ( '[app] Encountered Uncaught Exception: ' + err . message ) ;
console . log ( err . stack ) ;
restart ( ) ;
2014-02-21 23:52:23 -05:00
} ) ;
2013-09-23 12:50:27 -04:00
( function ( app ) {
2013-11-11 13:25:54 -05:00
"use strict" ;
2014-01-25 16:39:27 -05:00
var clientScripts ;
2013-11-25 16:28:07 -05:00
2013-11-25 12:35:54 -05:00
plugins . ready ( function ( ) {
// Minify client-side libraries
meta . js . get ( function ( err , scripts ) {
clientScripts = scripts . map ( function ( script ) {
script = {
script : script
} ;
return script ;
} ) ;
2013-09-24 12:33:51 -04:00
} ) ;
} ) ;
2013-08-23 13:14:36 -04:00
2014-02-27 14:55:41 -05:00
app . prepareAPI = function ( req , res , next ) {
res . locals . isAPI = true ;
next ( ) ;
} ;
2014-02-27 16:34:58 -05:00
app . authenticate = function ( req , res , next ) {
if ( ! req . user ) {
if ( res . locals . isAPI ) {
return res . json ( 403 , 'not-allowed' ) ;
} else {
return res . redirect ( '403' ) ;
}
} else {
next ( ) ;
}
} ;
2014-02-28 14:04:21 -05:00
app . checkGlobalPrivacySettings = function ( req , res , next ) {
2014-02-28 14:34:35 -05:00
var callerUID = req . user ? parseInt ( req . user . uid , 10 ) : 0 ;
if ( ! callerUID && ! ! parseInt ( meta . config . privateUserInfo , 10 ) ) {
2014-02-28 14:04:21 -05:00
if ( res . locals . isAPI ) {
return res . json ( 403 , 'not-allowed' ) ;
} else {
return res . redirect ( '403' ) ;
}
}
next ( ) ;
} ;
2014-02-28 14:34:35 -05:00
app . checkAccountPermissions = function ( req , res , next ) {
2014-02-28 15:58:29 -05:00
var callerUID = req . user ? parseInt ( req . user . uid , 10 ) : 0 ;
// this function requires userslug to be passed in. todo: /user/uploadpicture should pass in userslug I think
2014-02-28 14:34:35 -05:00
user . getUidByUserslug ( req . params . userslug , function ( err , uid ) {
if ( err ) {
return next ( err ) ;
}
// not sure if this check really should belong here. also make sure we're not doing this check again in the actual method
if ( ! uid ) {
if ( res . locals . isAPI ) {
return res . json ( 404 ) ;
} else {
return res . redirect ( '404' ) ;
}
}
if ( parseInt ( uid , 10 ) === callerUID ) {
return next ( ) ;
}
user . isAdministrator ( callerUID , function ( err , isAdmin ) {
if ( err ) {
return next ( err ) ;
}
if ( isAdmin ) {
next ( ) ;
}
if ( res . locals . isAPI ) {
return res . json ( 403 , 'not-allowed' ) ;
} else {
return res . redirect ( '403' ) ;
}
} ) ;
} ) ;
} ;
2014-02-27 14:55:41 -05:00
app . buildHeader = function ( req , res , next ) {
async . parallel ( [
function ( next ) {
// temp, don't forget to set metaTags and linkTags to res.locals.header
app . build _header ( {
req : req ,
res : res
} , function ( err , template ) {
res . locals . header = template ;
next ( err ) ;
} ) ;
} ,
function ( next ) {
2014-02-28 15:15:00 -05:00
// this is slower than the original implementation because the rendered template is not cached
// but I didn't bother to fix this because we will deprecate [filter:footer.build] in favour of the widgets system by 0.4x
plugins . fireHook ( 'filter:footer.build' , '' , function ( err , appendHTML ) {
app . render ( 'footer' , { footerHTML : appendHTML } , function ( err , template ) {
translator . translate ( template , function ( parsedTemplate ) {
res . locals . footer = template ;
next ( err ) ;
} ) ;
} ) ;
2014-02-27 14:55:41 -05:00
} ) ;
}
] , function ( err ) {
next ( ) ;
} ) ;
} ;
2013-07-24 18:54:14 -04:00
/ * *
* ` options ` object requires : req , res
2013-12-31 20:28:31 -05:00
* accepts : metaTags , linkTags
2013-07-24 18:54:14 -04:00
* /
2013-09-23 12:50:27 -04:00
app . build _header = function ( options , callback ) {
2013-10-12 13:05:47 -04:00
var custom _header = {
'navigation' : [ ]
} ;
2013-07-24 15:19:26 -04:00
2013-10-12 13:05:47 -04:00
plugins . fireHook ( 'filter:header.build' , custom _header , function ( err , custom _header ) {
var defaultMetaTags = [ {
2013-10-22 12:23:07 -04:00
name : 'viewport' ,
content : 'width=device-width, initial-scale=1.0, user-scalable=no'
} , {
name : 'content-type' ,
content : 'text/html; charset=UTF-8'
} , {
name : 'apple-mobile-web-app-capable' ,
content : 'yes'
} , {
property : 'og:site_name' ,
content : meta . config . title || 'NodeBB'
} , {
property : 'keywords' ,
2013-11-11 13:25:54 -05:00
content : meta . config . keywords || ''
2013-10-22 12:23:07 -04:00
} ] ,
2014-02-19 21:47:26 -05:00
defaultLinkTags = [ {
2013-11-26 11:34:38 -05:00
rel : 'apple-touch-icon' ,
2014-02-19 21:47:26 -05:00
href : '/apple-touch-icon'
} ] ,
2013-10-12 13:05:47 -04:00
templateValues = {
2014-01-18 16:13:01 -05:00
bootswatchCSS : meta . config [ 'theme:src' ] ,
2014-01-30 09:20:43 -05:00
pluginCSS : plugins . cssFiles . map ( function ( file ) { return { path : nconf . get ( 'relative_path' ) + file . replace ( /\\/g , '/' ) } ; } ) ,
2013-10-22 12:39:14 -04:00
title : meta . config . title || '' ,
2013-10-29 17:27:23 -04:00
description : meta . config . description || '' ,
2013-10-22 12:39:14 -04:00
'brand:logo' : meta . config [ 'brand:logo' ] || '' ,
'brand:logo:display' : meta . config [ 'brand:logo' ] ? '' : 'hide' ,
2013-10-12 13:05:47 -04:00
csrf : options . res . locals . csrf _token ,
relative _path : nconf . get ( 'relative_path' ) ,
clientScripts : clientScripts ,
2013-12-09 14:50:40 -05:00
navigation : custom _header . navigation ,
2013-12-26 15:58:46 -05:00
'cache-buster' : meta . config [ 'cache-buster' ] ? 'v=' + meta . config [ 'cache-buster' ] : '' ,
2014-02-20 21:21:41 -05:00
allowRegistration : meta . config . allowRegistration === undefined || parseInt ( meta . config . allowRegistration , 10 ) === 1 ,
searchEnabled : plugins . hasListeners ( 'filter:search.query' ) ? true : false
2013-12-31 20:53:24 -05:00
} ,
escapeList = {
'&' : '&' ,
'<' : '<' ,
'>' : '>' ,
"'" : ''' ,
'"' : '"'
2013-10-12 13:05:47 -04:00
} ;
2013-10-28 15:49:12 -04:00
var uid = '0' ;
2013-12-31 20:28:31 -05:00
// Meta Tags
2014-02-27 14:55:41 -05:00
/ * t e m p l a t e V a l u e s . m e t a T a g s = d e f a u l t M e t a T a g s . c o n c a t ( o p t i o n s . m e t a T a g s | | [ ] ) . m a p ( f u n c t i o n ( t a g ) {
2014-02-27 01:51:33 -05:00
if ( ! tag || typeof tag . content !== 'string' ) {
winston . warn ( 'Invalid meta tag. ' , tag ) ;
2014-02-27 01:43:24 -05:00
return tag ;
}
2013-12-31 20:53:24 -05:00
tag . content = tag . content . replace ( /[&<>'"]/g , function ( tag ) {
return escapeList [ tag ] || tag ;
} ) ;
2013-12-31 20:28:31 -05:00
return tag ;
2014-02-27 14:55:41 -05:00
} ) ; * /
2014-01-30 09:20:43 -05:00
// Link Tags
2014-02-27 14:55:41 -05:00
/ * t e m p l a t e V a l u e s . l i n k T a g s = d e f a u l t L i n k T a g s . c o n c a t ( o p t i o n s . l i n k T a g s | | [ ] ) ;
2014-01-30 09:20:43 -05:00
templateValues . linkTags . push ( {
rel : "icon" ,
type : "image/x-icon" ,
2014-02-10 22:24:36 -05:00
href : nconf . get ( 'relative_path' ) + '/favicon.ico'
2014-02-27 14:55:41 -05:00
} ) ; * /
2013-12-31 20:28:31 -05:00
2013-11-11 13:25:54 -05:00
if ( options . req . user && options . req . user . uid ) {
2013-10-28 15:49:12 -04:00
uid = options . req . user . uid ;
2013-11-11 13:25:54 -05:00
}
2013-10-28 15:49:12 -04:00
2014-01-31 13:17:28 -05:00
// Custom CSS
templateValues . useCustomCSS = false ;
if ( meta . config . useCustomCSS === '1' ) {
templateValues . useCustomCSS = true ;
templateValues . customCSS = meta . config . customCSS ;
}
2014-02-19 14:33:34 -05:00
async . parallel ( [
function ( next ) {
translator . get ( 'pages:' + path . basename ( options . req . url ) , function ( translated ) {
2014-02-27 14:55:41 -05:00
/ * v a r m e t a T i t l e = t e m p l a t e V a l u e s . m e t a T a g s . f i l t e r ( f u n c t i o n ( t a g ) {
2014-02-19 14:33:34 -05:00
return tag . name === 'title' ;
} ) ;
if ( translated ) {
templateValues . browserTitle = translated ;
} else if ( metaTitle . length > 0 && metaTitle [ 0 ] . content ) {
templateValues . browserTitle = metaTitle [ 0 ] . content ;
} else {
templateValues . browserTitle = meta . config . browserTitle || 'NodeBB' ;
2014-02-27 14:55:41 -05:00
} * /
2014-02-19 14:33:34 -05:00
next ( ) ;
} ) ;
} ,
function ( next ) {
user . isAdministrator ( uid , function ( err , isAdmin ) {
templateValues . isAdmin = isAdmin || false ;
next ( ) ;
} ) ;
}
] , function ( ) {
2014-02-27 14:55:41 -05:00
/ * t r a n s l a t o r . t r a n s l a t e ( t e m p l a t e s . h e a d e r . p a r s e ( t e m p l a t e V a l u e s ) , f u n c t i o n ( t e m p l a t e ) {
2013-10-28 15:49:12 -04:00
callback ( null , template ) ;
2014-02-27 14:55:41 -05:00
} ) ; * /
app . render ( 'header' , templateValues , function ( err , template ) {
callback ( null , template )
2013-10-28 15:49:12 -04:00
} ) ;
2013-11-11 13:25:54 -05:00
} ) ;
2013-09-24 14:14:26 -04:00
} ) ;
2013-06-20 16:04:58 -04:00
} ;
2013-04-22 16:51:32 +00:00
2013-11-28 17:37:17 -05:00
// Cache static files on production
if ( global . env !== 'development' ) {
app . enable ( 'cache' ) ;
2013-12-04 20:37:13 -05:00
app . enable ( 'minification' ) ;
2013-12-09 14:50:40 -05:00
2013-12-10 22:20:11 -05:00
// Configure cache-buster timestamp
require ( 'child_process' ) . exec ( 'git describe --tags' , {
cwd : path . join ( _ _dirname , '../' )
} , function ( err , stdOut ) {
if ( ! err ) {
meta . config [ 'cache-buster' ] = stdOut . trim ( ) ;
2013-12-26 15:58:46 -05:00
// winston.info('[init] Cache buster value set to: ' + stdOut);
2013-12-10 22:20:11 -05:00
} else {
2014-02-13 12:26:43 -05:00
fs . stat ( path . join ( _ _dirname , '../package.json' ) , function ( err , stats ) {
meta . config [ 'cache-buster' ] = new Date ( stats . mtime ) . getTime ( ) ;
} ) ;
2013-12-09 14:50:40 -05:00
}
2013-12-10 22:20:11 -05:00
} ) ;
}
2013-12-09 14:50:40 -05:00
2013-04-24 16:42:12 -04:00
// Middlewares
2013-09-29 20:27:52 -04:00
app . configure ( function ( ) {
2014-02-26 17:00:03 -05:00
app . engine ( 'tpl' , templates . _ _express ) ;
app . set ( 'view engine' , 'tpl' ) ;
app . set ( 'views' , path . join ( _ _dirname , '../public/templates' ) ) ;
2013-09-29 20:27:52 -04:00
async . series ( [
function ( next ) {
// Pre-router middlewares
app . use ( express . compress ( ) ) ;
2013-10-02 00:25:46 -04:00
2013-10-04 01:46:50 -04:00
logger . init ( app ) ;
2013-10-02 00:25:46 -04:00
2014-02-19 21:47:26 -05:00
// favicon & apple-touch-icon middleware
2014-02-10 22:24:36 -05:00
app . use ( express . favicon ( path . join ( _ _dirname , '../' , 'public' , meta . config [ 'brand:favicon' ] ? meta . config [ 'brand:favicon' ] : 'favicon.ico' ) ) ) ;
2014-02-19 21:47:26 -05:00
app . use ( '/apple-touch-icon' , function ( req , res ) {
2014-02-20 15:13:15 -05:00
if ( meta . config [ 'brand:logo' ] && validator . isURL ( meta . config [ 'brand:logo' ] ) ) {
return res . redirect ( meta . config [ 'brand:logo' ] ) ;
} else {
return res . sendfile ( path . join ( _ _dirname , '../public' , meta . config [ 'brand:logo' ] || nconf . get ( 'relative_path' ) + '/logo.png' ) , {
maxAge : app . enabled ( 'cache' ) ? 5184000000 : 0
} ) ;
}
2014-02-19 21:47:26 -05:00
} ) ;
2014-02-10 22:24:36 -05:00
2013-09-29 20:27:52 -04:00
app . use ( require ( 'less-middleware' ) ( {
src : path . join ( _ _dirname , '../' , 'public' ) ,
prefix : nconf . get ( 'relative_path' ) ,
2013-12-04 20:37:13 -05:00
yuicompress : app . enabled ( 'minification' ) ? true : false
2013-09-29 20:27:52 -04:00
} ) ) ;
app . use ( express . bodyParser ( ) ) ; // Puts POST vars in request.body
app . use ( express . cookieParser ( ) ) ; // If you want to parse cookies (res.cookies)
2013-12-02 17:10:26 -05:00
2013-12-02 22:33:55 -05:00
app . use ( express . session ( {
store : db . sessionStore ,
2013-12-02 17:10:26 -05:00
secret : nconf . get ( 'secret' ) ,
key : 'express.sid' ,
cookie : {
2014-02-13 21:06:41 -05:00
maxAge : 1000 * 60 * 60 * 24 * parseInt ( meta . configs . loginDays || 14 , 10 )
2013-12-02 17:10:26 -05:00
}
2013-09-29 20:27:52 -04:00
} ) ) ;
2013-12-02 17:10:26 -05:00
2013-09-29 20:27:52 -04:00
app . use ( express . csrf ( ) ) ;
2014-02-18 10:34:56 -05:00
if ( nconf . get ( 'port' ) != 80 && nconf . get ( 'port' ) != 443 && nconf . get ( 'use_port' ) === false ) {
2014-01-23 20:35:53 -05:00
winston . info ( 'Enabling \'trust proxy\'' ) ;
2014-01-20 13:23:40 -05:00
app . enable ( 'trust proxy' ) ;
2014-01-29 12:42:33 -05:00
}
if ( ( nconf . get ( 'port' ) == 80 || nconf . get ( 'port' ) == 443 ) && process . env . NODE _ENV !== 'development' ) {
2014-01-23 20:35:53 -05:00
winston . info ( 'Using ports 80 and 443 is not recommend; use a proxy instead. See README.md' ) ;
2014-01-20 13:23:40 -05:00
}
2013-09-29 20:27:52 -04:00
// Local vars, other assorted setup
app . use ( function ( req , res , next ) {
res . locals . csrf _token = req . session . _csrf ;
2013-11-01 11:27:02 -04:00
2013-10-11 11:08:52 +02:00
// Disable framing
2013-11-29 11:04:46 +01:00
res . setHeader ( 'X-Frame-Options' , 'SAMEORIGIN' ) ;
2013-11-01 11:27:02 -04:00
2013-09-29 20:27:52 -04:00
next ( ) ;
} ) ;
2014-02-27 14:55:41 -05:00
app . use ( function ( req , res , next ) {
// res.render post-processing middleware, modified from here: https://gist.github.com/mrlannigan/5051687
var render = res . render ;
res . render = function ( template , options , fn ) {
var self = this ,
options = options || { } ,
req = this . req ,
app = req . app ,
defaultFn = function ( err , str ) {
if ( err ) {
return req . next ( err ) ;
}
self . send ( str ) ;
} ;
if ( 'function' == typeof options ) {
fn = options , options = { } ;
}
if ( 'function' != typeof fn ) {
fn = defaultFn ;
}
render . call ( self , template , options , function ( err , str ) {
if ( res . locals . header ) {
str = res . locals . header + str ;
}
if ( res . locals . footer ) {
str = str + res . locals . footer ;
}
if ( str ) {
translator . translate ( str , function ( translated ) {
fn ( err , translated ) ;
} ) ;
} else {
fn ( err , str ) ;
}
} ) ;
} ;
next ( ) ;
} ) ;
2013-09-29 20:27:52 -04:00
// Authentication Routes
auth . initialize ( app ) ;
next ( ) ;
} ,
function ( next ) {
2013-10-19 16:24:33 -04:00
async . parallel ( [
function ( next ) {
2013-12-02 17:10:26 -05:00
db . getObjectFields ( 'config' , [ 'theme:type' , 'theme:id' , 'theme:staticDir' , 'theme:templates' ] , function ( err , themeData ) {
var themeId = ( themeData [ 'theme:id' ] || 'nodebb-theme-vanilla' ) ;
2013-10-19 17:41:26 -04:00
2013-10-21 12:07:37 -04:00
// Detect if a theme has been selected, and handle appropriately
2013-12-02 17:10:26 -05:00
if ( ! themeData [ 'theme:type' ] || themeData [ 'theme:type' ] === 'local' ) {
2013-10-21 12:07:37 -04:00
// Local theme
2013-10-21 11:09:46 -04:00
if ( process . env . NODE _ENV === 'development' ) {
winston . info ( '[themes] Using theme ' + themeId ) ;
}
2013-10-19 16:24:33 -04:00
2013-10-21 12:07:37 -04:00
// Theme's static directory
2013-12-02 17:10:26 -05:00
if ( themeData [ 'theme:staticDir' ] ) {
2014-02-14 17:11:25 +00:00
app . use ( '/css/assets' , express . static ( path . join ( nconf . get ( 'themes_path' ) , themeData [ 'theme:id' ] , themeData [ 'theme:staticDir' ] ) , {
2013-11-28 17:37:17 -05:00
maxAge : app . enabled ( 'cache' ) ? 5184000000 : 0
2013-11-26 11:17:34 -05:00
} ) ) ;
2013-10-21 12:07:37 -04:00
if ( process . env . NODE _ENV === 'development' ) {
2013-12-02 17:10:26 -05:00
winston . info ( 'Static directory routed for theme: ' + themeData [ 'theme:id' ] ) ;
2013-10-21 12:07:37 -04:00
}
}
2013-12-02 17:10:26 -05:00
if ( themeData [ 'theme:templates' ] ) {
2014-02-14 17:11:25 +00:00
app . use ( '/templates' , express . static ( path . join ( nconf . get ( 'themes_path' ) , themeData [ 'theme:id' ] , themeData [ 'theme:templates' ] ) , {
2013-11-28 17:37:17 -05:00
maxAge : app . enabled ( 'cache' ) ? 5184000000 : 0
2013-11-26 11:17:34 -05:00
} ) ) ;
2013-10-29 12:34:41 -04:00
if ( process . env . NODE _ENV === 'development' ) {
2013-12-02 17:10:26 -05:00
winston . info ( 'Custom templates directory routed for theme: ' + themeData [ 'theme:id' ] ) ;
2013-10-29 12:34:41 -04:00
}
}
2013-10-19 17:41:26 -04:00
next ( ) ;
} else {
// If not using a local theme (bootswatch, etc), drop back to vanilla
2013-10-21 11:09:46 -04:00
if ( process . env . NODE _ENV === 'development' ) {
winston . info ( '[themes] Using theme ' + themeId ) ;
}
2013-10-19 17:41:26 -04:00
app . use ( require ( 'less-middleware' ) ( {
2014-02-14 17:11:25 +00:00
src : path . join ( nconf . get ( 'themes_path' ) , '/nodebb-theme-vanilla' ) ,
2013-10-19 17:41:26 -04:00
dest : path . join ( _ _dirname , '../public/css' ) ,
prefix : nconf . get ( 'relative_path' ) + '/css' ,
2013-12-04 20:37:13 -05:00
yuicompress : app . enabled ( 'minification' ) ? true : false
2013-10-19 17:41:26 -04:00
} ) ) ;
2013-10-19 16:24:33 -04:00
next ( ) ;
}
} ) ;
2013-11-01 11:27:02 -04:00
// Route paths to screenshots for installed themes
meta . themes . get ( function ( err , themes ) {
var screenshotPath ;
async . each ( themes , function ( themeObj , next ) {
if ( themeObj . screenshot ) {
2014-02-14 17:11:25 +00:00
screenshotPath = path . join ( nconf . get ( 'themes_path' ) , themeObj . id , themeObj . screenshot ) ;
2013-11-01 11:36:05 -04:00
( function ( id , path ) {
fs . exists ( path , function ( exists ) {
if ( exists ) {
app . get ( '/css/previews/' + id , function ( req , res ) {
res . sendfile ( path ) ;
} ) ;
}
} ) ;
} ) ( themeObj . id , screenshotPath ) ;
2013-11-01 11:27:02 -04:00
} else {
next ( false ) ;
}
} ) ;
} ) ;
2013-09-29 20:27:52 -04:00
}
2013-10-19 16:24:33 -04:00
] , next ) ;
2013-09-29 20:27:52 -04:00
} ,
function ( next ) {
// Router & post-router middlewares
app . use ( app . router ) ;
2013-10-19 16:24:33 -04:00
// Static directory /public
2013-11-26 11:17:34 -05:00
app . use ( nconf . get ( 'relative_path' ) , express . static ( path . join ( _ _dirname , '../' , 'public' ) , {
2013-11-28 17:37:17 -05:00
maxAge : app . enabled ( 'cache' ) ? 5184000000 : 0
2013-11-26 11:17:34 -05:00
} ) ) ;
2013-10-19 16:24:33 -04:00
2013-09-29 20:27:52 -04:00
// 404 catch-all
app . use ( function ( req , res , next ) {
2014-01-12 14:38:54 -05:00
var isLanguage = new RegExp ( '^' + nconf . get ( 'relative_path' ) + '/language/[\\w]{2,}/.*.json' ) ,
isClientScript = new RegExp ( '^' + nconf . get ( 'relative_path' ) + '\\/src\\/forum(\\/admin)?\\/[\\w]+\\.js' ) ;
2013-10-21 11:09:46 -04:00
2013-09-29 20:27:52 -04:00
res . status ( 404 ) ;
2013-10-21 11:09:46 -04:00
if ( isClientScript . test ( req . url ) ) {
2013-10-03 15:04:25 -04:00
// Handle missing client-side scripts
res . type ( 'text/javascript' ) . send ( 200 , '' ) ;
2013-10-21 11:09:46 -04:00
} else if ( isLanguage . test ( req . url ) ) {
// Handle languages by sending an empty object
res . json ( 200 , { } ) ;
2013-10-03 15:04:25 -04:00
} else if ( req . accepts ( 'html' ) ) {
// respond with html page
2013-11-11 13:25:54 -05:00
if ( process . env . NODE _ENV === 'development' ) {
winston . warn ( 'Route requested but not found: ' + req . url ) ;
}
2013-09-29 20:27:52 -04:00
res . redirect ( nconf . get ( 'relative_path' ) + '/404' ) ;
2013-10-03 15:04:25 -04:00
} else if ( req . accepts ( 'json' ) ) {
// respond with json
2013-11-11 13:25:54 -05:00
if ( process . env . NODE _ENV === 'development' ) {
winston . warn ( 'Route requested but not found: ' + req . url ) ;
}
2013-10-03 15:04:25 -04:00
res . json ( {
2013-09-29 20:27:52 -04:00
error : 'Not found'
} ) ;
2013-10-03 15:04:25 -04:00
} else {
// default to plain-text. send()
res . type ( 'txt' ) . send ( 'Not found' ) ;
2013-09-29 20:27:52 -04:00
}
} ) ;
app . use ( function ( err , req , res , next ) {
// we may use properties of the error object
// here and next(err) appropriately, or if
// we possibly recovered from the error, simply next().
console . error ( err . stack ) ;
2013-12-02 13:28:46 -05:00
var status = err . status || 500 ;
res . status ( status ) ;
2013-09-29 20:27:52 -04:00
2013-12-02 13:28:46 -05:00
res . json ( status , {
2013-09-29 20:27:52 -04:00
error : err . message
} ) ;
} ) ;
next ( ) ;
}
] , function ( err ) {
if ( err ) {
winston . error ( 'Errors were encountered while attempting to initialise NodeBB.' ) ;
2013-10-05 22:33:29 -04:00
process . exit ( ) ;
2013-09-29 20:27:52 -04:00
} else {
2013-11-11 13:25:54 -05:00
if ( process . env . NODE _ENV === 'development' ) {
winston . info ( 'Middlewares loaded.' ) ;
}
2013-09-13 11:10:17 -04:00
}
} ) ;
} ) ;
2013-09-23 12:50:27 -04:00
module . exports . init = function ( ) {
2013-09-24 14:14:26 -04:00
// translate all static templates served by webserver here. ex. footer, logout
2014-01-23 17:08:33 -05:00
plugins . fireHook ( 'action:app.load' , app ) ;
2013-11-22 11:42:42 -05:00
2014-02-27 14:55:41 -05:00
/ * t r a n s l a t o r . t r a n s l a t e ( t e m p l a t e s . l o g o u t . t o S t r i n g ( ) , f u n c t i o n ( p a r s e d T e m p l a t e ) {
2013-11-11 13:25:54 -05:00
templates . logout = parsedTemplate ;
2014-02-27 14:55:41 -05:00
} ) ; * /
2013-09-24 14:14:26 -04:00
2014-02-09 23:07:12 +00:00
server . on ( "error" , function ( e ) {
if ( e . code === 'EADDRINUSE' ) {
winston . error ( 'NodeBB address in use, exiting...' ) ;
process . exit ( 1 ) ;
} else {
throw e ;
}
} ) ;
var port = nconf . get ( 'PORT' ) || nconf . get ( 'port' ) ;
winston . info ( 'NodeBB attempting to listen on: ' + ( ( nconf . get ( 'bind_address' ) === "0.0.0.0" || ! nconf . get ( 'bind_address' ) ) ? '0.0.0.0' : nconf . get ( 'bind_address' ) ) + ':' + port ) ;
server . listen ( port , nconf . get ( 'bind_address' ) , function ( ) {
winston . info ( 'NodeBB Ready' ) ;
} ) ;
2013-11-11 13:25:54 -05:00
} ;
2013-05-17 13:36:35 -04:00
2013-09-23 12:50:27 -04:00
app . create _route = function ( url , tpl ) { // to remove
2014-01-24 10:00:57 -05:00
var routerScript = ' < script > \
2014-01-24 10:09:28 -05:00
ajaxify . initialLoad = true ; \
2014-01-24 10:00:57 -05:00
templates . ready ( function ( ) { ajaxify . go ( "' + url + '" , null , true ) ; } ) ; \
< / s c r i p t > ' ;
return routerScript ;
2013-05-09 06:26:32 +00:00
} ;
2013-08-23 13:14:36 -04:00
2013-09-23 12:50:27 -04:00
app . namespace ( nconf . get ( 'relative_path' ) , function ( ) {
2013-11-28 11:13:03 -05:00
auth . registerApp ( app ) ;
2014-02-25 16:50:58 -05:00
metaRoute . createRoutes ( app ) ;
2013-11-12 12:41:16 -05:00
admin . createRoutes ( app ) ;
apiRoute . createRoutes ( app ) ;
2014-02-09 23:20:11 +00:00
feedsRoute . createRoutes ( app ) ;
2014-02-09 21:29:21 +00:00
2013-07-10 21:31:58 -04:00
// Basic Routes (entirely client-side parsed, goal is to move the rest of the crap in this file into this one section)
2013-09-23 12:50:27 -04:00
( function ( ) {
2014-02-28 14:34:35 -05:00
var routes = [ ] ,
2014-02-27 16:52:46 -05:00
loginRequired = [ 'notifications' ] ;
2013-08-23 13:14:36 -04:00
2013-11-11 14:06:26 -05:00
async . each ( routes . concat ( loginRequired ) , function ( route , next ) {
app . get ( '/' + route , function ( req , res ) {
2014-02-28 14:34:35 -05:00
if ( loginRequired . indexOf ( route ) !== - 1 && ! req . user ) {
2013-11-11 14:06:26 -05:00
return res . redirect ( '/403' ) ;
}
app . build _header ( {
req : req ,
res : res
} , function ( err , header ) {
res . send ( ( isNaN ( parseInt ( route , 10 ) ) ? 200 : parseInt ( route , 10 ) ) , header + app . create _route ( route ) + templates . footer ) ;
2013-07-10 21:31:58 -04:00
} ) ;
2013-11-11 14:06:26 -05:00
} ) ;
} ) ;
2013-07-10 21:31:58 -04:00
} ( ) ) ;
2013-08-23 13:14:36 -04:00
2014-02-27 17:04:41 -05:00
/* Main */
2014-02-27 14:55:41 -05:00
app . get ( '/' , app . buildHeader , controllers . home ) ;
app . get ( '/api/home' , app . prepareAPI , controllers . home ) ;
2014-01-05 01:39:33 -05:00
2014-02-27 16:52:46 -05:00
app . get ( '/login' , app . buildHeader , controllers . login ) ;
app . get ( '/api/login' , app . prepareAPI , controllers . login ) ;
app . get ( '/register' , app . buildHeader , controllers . register ) ;
app . get ( '/api/register' , app . prepareAPI , controllers . register ) ;
2014-02-27 17:04:41 -05:00
app . get ( '/confirm/:code' , app . buildHeader , controllers . confirmEmail ) ;
app . get ( '/api/confirm/:code' , app . prepareAPI , controllers . confirmEmail ) ;
2014-02-27 17:16:06 -05:00
app . get ( '/sitemap.xml' , controllers . sitemap ) ;
app . get ( '/robots.txt' , controllers . robots ) ;
2014-02-28 14:04:21 -05:00
app . get ( '/outgoing' , app . buildHeader , controllers . outgoing ) ;
app . get ( '/api/outgoing' , app . prepareAPI , controllers . outgoing ) ;
2014-02-28 14:08:06 -05:00
/* Static Pages */
2014-02-28 14:04:21 -05:00
app . get ( '/404' , app . buildHeader , controllers . static [ '404' ] ) ;
app . get ( '/api/404' , app . prepareAPI , controllers . static [ '404' ] ) ;
app . get ( '/403' , app . buildHeader , controllers . static [ '403' ] ) ;
app . get ( '/api/403' , app . prepareAPI , controllers . static [ '403' ] ) ;
app . get ( '/500' , app . buildHeader , controllers . static [ '500' ] ) ;
app . get ( '/api/500' , app . prepareAPI , controllers . static [ '500' ] ) ;
2014-02-27 17:04:41 -05:00
/* Topics */
2014-02-27 14:55:41 -05:00
app . get ( '/topic/:topic_id/:slug?' , app . buildHeader , controllers . topics . get ) ;
app . get ( '/api/topic/:topic_id/:slug?' , app . prepareAPI , controllers . topics . get ) ;
2013-07-23 17:21:44 -04:00
2014-02-27 17:04:41 -05:00
/* Categories */
2014-02-27 14:55:41 -05:00
app . get ( '/popular/:set?' , app . buildHeader , controllers . categories . popular ) ;
app . get ( '/api/popular/:set?' , app . prepareAPI , controllers . categories . popular ) ;
2014-02-18 19:23:36 -05:00
2014-02-27 16:34:58 -05:00
app . get ( '/recent/:term?' , app . buildHeader , controllers . categories . recent ) ;
app . get ( '/api/recent/:term?' , app . prepareAPI , controllers . categories . recent ) ;
2014-02-27 16:39:34 -05:00
app . get ( '/unread/' , app . buildHeader , app . authenticate , controllers . categories . unread ) ;
app . get ( '/api/unread/' , app . prepareAPI , app . authenticate , controllers . categories . unread ) ;
app . get ( '/unread/total' , app . buildHeader , app . authenticate , controllers . categories . unreadTotal ) ;
app . get ( '/api/unread/total' , app . prepareAPI , app . authenticate , controllers . categories . unreadTotal ) ;
2014-02-27 16:34:58 -05:00
2014-02-27 14:55:41 -05:00
app . get ( '/category/:category_id/:slug?' , app . buildHeader , controllers . categories . get ) ;
app . get ( '/api/category/:category_id/:slug?' , app . prepareAPI , controllers . categories . get ) ;
2013-05-30 16:02:13 -04:00
2014-02-28 14:08:06 -05:00
/* Accounts */
2014-02-28 15:58:29 -05:00
app . get ( '/user/:userslug' , app . buildHeader , app . checkGlobalPrivacySettings , controllers . accounts . getAccount ) ;
app . get ( '/api/user/:userslug' , app . prepareAPI , app . checkGlobalPrivacySettings , controllers . accounts . getAccount ) ;
2014-02-28 14:04:21 -05:00
2014-02-28 15:58:29 -05:00
app . get ( '/user/:userslug/following' , app . buildHeader , app . checkGlobalPrivacySettings , controllers . accounts . getFollowing ) ;
app . get ( '/api/user/:userslug/following' , app . prepareAPI , app . checkGlobalPrivacySettings , controllers . accounts . getFollowing ) ;
2014-02-28 14:04:21 -05:00
2014-02-28 15:58:29 -05:00
app . get ( '/user/:userslug/followers' , app . buildHeader , app . checkGlobalPrivacySettings , controllers . accounts . getFollowers ) ;
app . get ( '/api/user/:userslug/followers' , app . prepareAPI , app . checkGlobalPrivacySettings , controllers . accounts . getFollowers ) ;
2014-02-28 14:04:21 -05:00
2014-02-28 15:58:29 -05:00
app . get ( '/user/:userslug/favourites' , app . buildHeader , app . checkGlobalPrivacySettings , app . checkAccountPermissions , controllers . accounts . getFavourites ) ;
app . get ( '/api/user/:userslug/favourites' , app . prepareAPI , app . checkGlobalPrivacySettings , app . checkAccountPermissions , controllers . accounts . getFavourites ) ;
2014-02-28 14:04:21 -05:00
2014-02-28 15:58:29 -05:00
app . get ( '/user/:userslug/posts' , app . buildHeader , app . checkGlobalPrivacySettings , controllers . accounts . getPosts ) ;
app . get ( '/api/user/:userslug/posts' , app . prepareAPI , app . checkGlobalPrivacySettings , controllers . accounts . getPosts ) ;
2014-02-28 14:04:21 -05:00
2014-02-28 15:58:29 -05:00
app . get ( '/user/:userslug/edit' , app . buildHeader , app . checkGlobalPrivacySettings , app . checkAccountPermissions , controllers . accounts . accountEdit ) ;
app . get ( '/api/user/:userslug/edit' , app . prepareAPI , app . checkGlobalPrivacySettings , app . checkAccountPermissions , controllers . accounts . accountEdit ) ;
2014-02-28 14:04:21 -05:00
2014-02-28 14:34:35 -05:00
// todo: admin recently gained access to this page, pls check if it actually works
2014-02-28 15:58:29 -05:00
app . get ( '/user/:userslug/settings' , app . buildHeader , app . checkGlobalPrivacySettings , app . checkAccountPermissions , controllers . accounts . accountSettings ) ;
app . get ( '/api/user/:userslug/settings' , app . prepareAPI , app . checkGlobalPrivacySettings , app . checkAccountPermissions , controllers . accounts . accountSettings ) ;
2014-02-28 14:04:21 -05:00
2014-02-28 15:33:25 -05:00
app . get ( '/api/user/uid/:uid' , app . checkGlobalPrivacySettings , controllers . accounts . getUserByUID ) ;
2014-02-28 15:26:39 -05:00
// this should have been in the API namespace
2014-02-28 15:58:29 -05:00
// also, perhaps pass in :userslug so we can use checkAccountPermissions middleware, in future will allow admins to upload a picture for a user
app . post ( '/user/uploadpicture' , app . prepareAPI , app . checkGlobalPrivacySettings , /*app.checkAccountPermissions,*/ controllers . accounts . uploadPicture ) ;
2014-02-28 15:26:39 -05:00
2014-02-28 14:19:43 -05:00
/* Users */
app . get ( '/users' , app . buildHeader , app . checkGlobalPrivacySettings , controllers . users . getOnlineUsers ) ;
app . get ( '/api/users' , app . prepareAPI , app . checkGlobalPrivacySettings , controllers . users . getOnlineUsers ) ;
// was this duped by accident or purpose?
app . get ( '/users/online' , app . buildHeader , app . checkGlobalPrivacySettings , controllers . users . getOnlineUsers ) ;
app . get ( '/api/users/online' , app . prepareAPI , app . checkGlobalPrivacySettings , controllers . users . getOnlineUsers ) ;
app . get ( '/users/sort-posts' , app . buildHeader , app . checkGlobalPrivacySettings , controllers . users . getUsersSortedByPosts ) ;
app . get ( '/api/users/sort-posts' , app . prepareAPI , app . checkGlobalPrivacySettings , controllers . users . getUsersSortedByPosts ) ;
app . get ( '/users/sort-reputation' , app . buildHeader , app . checkGlobalPrivacySettings , controllers . users . getUsersSortedByReputation ) ;
app . get ( '/api/users/sort-reputation' , app . prepareAPI , app . checkGlobalPrivacySettings , controllers . users . getUsersSortedByReputation ) ;
app . get ( '/users/latest' , app . buildHeader , app . checkGlobalPrivacySettings , controllers . users . getUsersSortedByJoinDate ) ;
app . get ( '/api/users/latest' , app . prepareAPI , app . checkGlobalPrivacySettings , controllers . users . getUsersSortedByJoinDate ) ;
app . get ( '/users/search' , app . buildHeader , app . checkGlobalPrivacySettings , controllers . users . getUsersForSearch ) ;
app . get ( '/api/users/search' , app . prepareAPI , app . checkGlobalPrivacySettings , controllers . users . getUsersForSearch ) ;
2014-02-28 14:04:21 -05:00
2014-01-30 19:52:32 -05:00
2013-08-08 15:04:22 -04:00
2013-08-03 20:54:16 -04:00
2013-12-21 00:07:00 -05:00
app . get ( '/search/:term?' , function ( req , res ) {
2013-12-12 22:51:17 -05:00
if ( ! req . user && meta . config . allowGuestSearching !== '1' ) {
2013-09-23 14:40:31 -04:00
return res . redirect ( '/403' ) ;
2013-11-11 13:25:54 -05:00
}
2013-12-21 00:07:00 -05:00
if ( ! req . params . term ) {
req . params . term = '' ;
}
2013-09-17 13:09:37 -04:00
app . build _header ( {
req : req ,
res : res
2013-09-23 12:50:27 -04:00
} , function ( err , header ) {
2013-11-18 02:39:08 -06:00
res . send ( header + app . create _route ( 'search/' + req . params . term , null , 'search' ) + templates . footer ) ;
2013-08-03 20:54:16 -04:00
} ) ;
} ) ;
2013-08-23 13:14:36 -04:00
2013-11-03 17:15:18 -05:00
// Other routes
require ( './routes/plugins' ) ( app ) ;
2013-10-25 16:01:31 -04:00
// Debug routes
if ( process . env . NODE _ENV === 'development' ) {
require ( './routes/debug' ) ( app ) ;
}
2013-10-12 17:19:13 -04:00
var custom _routes = {
'routes' : [ ] ,
2013-12-30 16:19:59 -05:00
'api' : [ ] ,
'templates' : [ ]
2013-10-12 17:19:13 -04:00
} ;
2014-01-25 23:13:51 +01:00
app . get _custom _templates = function ( ) {
return custom _routes . templates . map ( function ( tpl ) {
return tpl . template . split ( '.tpl' ) [ 0 ] ;
} ) ;
2014-02-20 02:05:49 -05:00
} ;
2014-01-25 23:13:51 +01:00
2013-10-12 17:19:13 -04:00
plugins . ready ( function ( ) {
plugins . fireHook ( 'filter:server.create_routes' , custom _routes , function ( err , custom _routes ) {
var routes = custom _routes . routes ;
for ( var route in routes ) {
if ( routes . hasOwnProperty ( route ) ) {
2013-11-04 23:59:33 -05:00
( function ( route ) {
app [ routes [ route ] . method || 'get' ] ( routes [ route ] . route , function ( req , res ) {
routes [ route ] . options ( req , res , function ( options ) {
app . build _header ( {
2014-01-04 17:00:52 -05:00
req : options . req || req ,
res : options . res || res
2013-11-04 23:59:33 -05:00
} , function ( err , header ) {
2013-11-11 13:25:54 -05:00
res . send ( header + options . content + templates . footer ) ;
2013-11-04 23:59:33 -05:00
} ) ;
2013-10-12 17:19:13 -04:00
} ) ;
} ) ;
2013-11-04 23:59:33 -05:00
} ( route ) ) ;
2013-10-12 17:19:13 -04:00
}
}
2013-12-03 13:10:46 -05:00
var apiRoutes = custom _routes . api ;
for ( var route in apiRoutes ) {
if ( apiRoutes . hasOwnProperty ( route ) ) {
( function ( route ) {
app [ apiRoutes [ route ] . method || 'get' ] ( '/api' + apiRoutes [ route ] . route , function ( req , res ) {
apiRoutes [ route ] . callback ( req , res , function ( data ) {
res . json ( data ) ;
} ) ;
} ) ;
} ( route ) ) ;
}
}
2013-12-30 16:19:59 -05:00
var templateRoutes = custom _routes . templates ;
for ( var route in templateRoutes ) {
if ( templateRoutes . hasOwnProperty ( route ) ) {
( function ( route ) {
app . get ( '/templates/' + templateRoutes [ route ] . template , function ( req , res ) {
res . send ( templateRoutes [ route ] . content ) ;
} ) ;
} ( route ) ) ;
}
}
2013-10-21 11:09:46 -04:00
} ) ;
2013-10-12 17:19:13 -04:00
} ) ;
2013-10-21 11:09:46 -04:00
2013-10-12 17:19:13 -04:00
2013-05-05 13:05:05 -04:00
} ) ;
2013-04-22 16:51:32 +00:00
} ( WebServer ) ) ;