var path = require ( 'path' ) ,
fs = require ( 'fs' ) ,
nconf = require ( 'nconf' ) ,
express = require ( 'express' ) ,
express _namespace = require ( 'express-namespace' ) ,
WebServer = express ( ) ,
server ,
winston = require ( 'winston' ) ,
validator = require ( 'validator' ) ,
async = require ( 'async' ) ,
S = require ( 'string' ) ,
qs = require ( 'querystring' ) ,
pkg = require ( '../package.json' ) ,
utils = require ( '../public/src/utils' ) ,
db = require ( './database' ) ,
user = require ( './user' ) ,
categories = require ( './categories' ) ,
CategoryTools = require ( './categoryTools' ) ,
posts = require ( './posts' ) ,
topics = require ( './topics' ) ,
ThreadTools = require ( './threadTools' ) ,
notifications = require ( './notifications' ) ,
admin = require ( './routes/admin' ) ,
userRoute = require ( './routes/user' ) ,
apiRoute = require ( './routes/api' ) ,
feedsRoute = require ( './routes/feeds' ) ,
auth = require ( './routes/authentication' ) ,
meta = require ( './meta' ) ,
plugins = require ( './plugins' ) ,
logger = require ( './logger' ) ,
templates = require ( './../public/src/templates' ) ,
translator = require ( './../public/src/translator' ) ;
if ( nconf . get ( 'ssl' ) ) {
server = require ( 'https' ) . createServer ( {
key : fs . readFileSync ( nconf . get ( 'ssl' ) . key ) ,
cert : fs . readFileSync ( nconf . get ( 'ssl' ) . cert )
} , WebServer ) ;
} else {
server = require ( 'http' ) . createServer ( WebServer ) ;
}
module . exports . server = server ;
( function ( app ) {
"use strict" ;
var clientScripts ;
plugins . ready ( function ( ) {
// Minify client-side libraries
meta . js . get ( function ( err , scripts ) {
clientScripts = scripts . map ( function ( script ) {
script = {
script : script
} ;
return script ;
} ) ;
} ) ;
} ) ;
/ * *
* ` options ` object requires : req , res
* accepts : metaTags , linkTags
* /
app . build _header = function ( options , callback ) {
var custom _header = {
'navigation' : [ ]
} ;
plugins . fireHook ( 'filter:header.build' , custom _header , function ( err , custom _header ) {
var defaultMetaTags = [ {
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' ,
content : meta . config . keywords || ''
} ] ,
defaultLinkTags = [ / * {
rel : 'apple-touch-icon' ,
href : meta . config [ 'brand:logo' ] || nconf . get ( 'relative_path' ) + '/logo.png'
} * / ] ,
templateValues = {
bootswatchCSS : meta . config [ 'theme:src' ] ,
pluginCSS : plugins . cssFiles . map ( function ( file ) { return { path : nconf . get ( 'relative_path' ) + file . replace ( /\\/g , '/' ) } ; } ) ,
title : meta . config . title || '' ,
description : meta . config . description || '' ,
'brand:logo' : meta . config [ 'brand:logo' ] || '' ,
'brand:logo:display' : meta . config [ 'brand:logo' ] ? '' : 'hide' ,
csrf : options . res . locals . csrf _token ,
relative _path : nconf . get ( 'relative_path' ) ,
clientScripts : clientScripts ,
navigation : custom _header . navigation ,
'cache-buster' : meta . config [ 'cache-buster' ] ? 'v=' + meta . config [ 'cache-buster' ] : '' ,
allowRegistration : meta . config . allowRegistration === undefined || parseInt ( meta . config . allowRegistration , 10 ) === 1
} ,
escapeList = {
'&' : '&' ,
'<' : '<' ,
'>' : '>' ,
"'" : ''' ,
'"' : '"'
} ;
var uid = '0' ;
// Meta Tags
templateValues . metaTags = defaultMetaTags . concat ( options . metaTags || [ ] ) . map ( function ( tag ) {
tag . content = tag . content . replace ( /[&<>'"]/g , function ( tag ) {
return escapeList [ tag ] || tag ;
} ) ;
return tag ;
} ) ;
// Link Tags
templateValues . linkTags = defaultLinkTags . concat ( options . linkTags || [ ] ) ;
templateValues . linkTags . push ( {
rel : "icon" ,
type : "image/x-icon" ,
href : meta . config [ 'brand:favicon' ] || nconf . get ( 'relative_path' ) + '/favicon.ico'
} ) ;
// Browser Title
var metaTitle = templateValues . metaTags . filter ( function ( tag ) {
return tag . property === 'og:title' ;
} ) ;
if ( metaTitle . length > 0 && metaTitle [ 0 ] . content ) {
templateValues . browserTitle = metaTitle [ 0 ] . content ;
} else {
templateValues . browserTitle = meta . config . browserTitle || 'NodeBB' ;
}
if ( options . req . user && options . req . user . uid ) {
uid = options . req . user . uid ;
}
// Custom CSS
templateValues . useCustomCSS = false ;
if ( meta . config . useCustomCSS === '1' ) {
templateValues . useCustomCSS = true ;
templateValues . customCSS = meta . config . customCSS ;
}
user . isAdministrator ( uid , function ( err , isAdmin ) {
templateValues . isAdmin = isAdmin ;
translator . translate ( templates . header . parse ( templateValues ) , function ( template ) {
callback ( null , template ) ;
} ) ;
} ) ;
} ) ;
} ;
// Cache static files on production
if ( global . env !== 'development' ) {
app . enable ( 'cache' ) ;
app . enable ( 'minification' ) ;
// 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 ( ) ;
// winston.info('[init] Cache buster value set to: ' + stdOut);
} else {
winston . warn ( '[init] Cache buster not set' ) ;
}
} ) ;
}
// Middlewares
app . configure ( function ( ) {
async . series ( [
function ( next ) {
// Pre-router middlewares
app . use ( express . compress ( ) ) ;
logger . init ( app ) ;
app . use ( express . favicon ( path . join ( _ _dirname , '../' , 'public' , 'favicon.ico' ) ) ) ;
app . use ( require ( 'less-middleware' ) ( {
src : path . join ( _ _dirname , '../' , 'public' ) ,
prefix : nconf . get ( 'relative_path' ) ,
yuicompress : app . enabled ( 'minification' ) ? true : false
} ) ) ;
app . use ( express . bodyParser ( ) ) ; // Puts POST vars in request.body
app . use ( express . cookieParser ( ) ) ; // If you want to parse cookies (res.cookies)
app . use ( express . session ( {
store : db . sessionStore ,
secret : nconf . get ( 'secret' ) ,
key : 'express.sid' ,
cookie : {
maxAge : 60 * 60 * 24 * 30 * 1000 // 30 days
}
} ) ) ;
app . use ( express . csrf ( ) ) ;
if ( nconf . get ( 'port' ) != 80 && nconf . get ( 'port' ) != 443 && nconf . get ( 'use_port' ) === true ) {
winston . info ( 'Enabling \'trust proxy\'' ) ;
app . enable ( 'trust proxy' ) ;
}
if ( ( nconf . get ( 'port' ) == 80 || nconf . get ( 'port' ) == 443 ) && process . env . NODE _ENV !== 'development' ) {
winston . info ( 'Using ports 80 and 443 is not recommend; use a proxy instead. See README.md' ) ;
}
// Local vars, other assorted setup
app . use ( function ( req , res , next ) {
res . locals . csrf _token = req . session . _csrf ;
// Disable framing
res . setHeader ( 'X-Frame-Options' , 'SAMEORIGIN' ) ;
next ( ) ;
} ) ;
// Authentication Routes
auth . initialize ( app ) ;
app . use ( function ( req , res , next ) {
if ( req . user ) {
user . setUserField ( req . user . uid , 'lastonline' , Date . now ( ) ) ;
}
next ( ) ;
} ) ;
next ( ) ;
} ,
function ( next ) {
async . parallel ( [
function ( next ) {
db . getObjectFields ( 'config' , [ 'theme:type' , 'theme:id' , 'theme:staticDir' , 'theme:templates' ] , function ( err , themeData ) {
var themeId = ( themeData [ 'theme:id' ] || 'nodebb-theme-vanilla' ) ;
// Detect if a theme has been selected, and handle appropriately
if ( ! themeData [ 'theme:type' ] || themeData [ 'theme:type' ] === 'local' ) {
// Local theme
if ( process . env . NODE _ENV === 'development' ) {
winston . info ( '[themes] Using theme ' + themeId ) ;
}
// Theme's static directory
if ( themeData [ 'theme:staticDir' ] ) {
app . use ( '/css/assets' , express . static ( path . join ( _ _dirname , '../node_modules' , themeData [ 'theme:id' ] , themeData [ 'theme:staticDir' ] ) , {
maxAge : app . enabled ( 'cache' ) ? 5184000000 : 0
} ) ) ;
if ( process . env . NODE _ENV === 'development' ) {
winston . info ( 'Static directory routed for theme: ' + themeData [ 'theme:id' ] ) ;
}
}
if ( themeData [ 'theme:templates' ] ) {
app . use ( '/templates' , express . static ( path . join ( _ _dirname , '../node_modules' , themeData [ 'theme:id' ] , themeData [ 'theme:templates' ] ) , {
maxAge : app . enabled ( 'cache' ) ? 5184000000 : 0
} ) ) ;
if ( process . env . NODE _ENV === 'development' ) {
winston . info ( 'Custom templates directory routed for theme: ' + themeData [ 'theme:id' ] ) ;
}
}
app . use ( require ( 'less-middleware' ) ( {
src : path . join ( _ _dirname , '../node_modules/' + themeId ) ,
dest : path . join ( _ _dirname , '../public/css' ) ,
prefix : nconf . get ( 'relative_path' ) + '/css' ,
yuicompress : app . enabled ( 'minification' ) ? true : false
} ) ) ;
next ( ) ;
} else {
// If not using a local theme (bootswatch, etc), drop back to vanilla
if ( process . env . NODE _ENV === 'development' ) {
winston . info ( '[themes] Using theme ' + themeId ) ;
}
app . use ( require ( 'less-middleware' ) ( {
src : path . join ( _ _dirname , '../node_modules/nodebb-theme-vanilla' ) ,
dest : path . join ( _ _dirname , '../public/css' ) ,
prefix : nconf . get ( 'relative_path' ) + '/css' ,
yuicompress : app . enabled ( 'minification' ) ? true : false
} ) ) ;
next ( ) ;
}
} ) ;
// Route paths to screenshots for installed themes
meta . themes . get ( function ( err , themes ) {
var screenshotPath ;
async . each ( themes , function ( themeObj , next ) {
if ( themeObj . screenshot ) {
screenshotPath = path . join ( _ _dirname , '../node_modules' , themeObj . id , themeObj . screenshot ) ;
( function ( id , path ) {
fs . exists ( path , function ( exists ) {
if ( exists ) {
app . get ( '/css/previews/' + id , function ( req , res ) {
res . sendfile ( path ) ;
} ) ;
}
} ) ;
} ) ( themeObj . id , screenshotPath ) ;
} else {
next ( false ) ;
}
} ) ;
} ) ;
}
] , next ) ;
} ,
function ( next ) {
// Router & post-router middlewares
app . use ( app . router ) ;
// Static directory /public
app . use ( nconf . get ( 'relative_path' ) , express . static ( path . join ( _ _dirname , '../' , 'public' ) , {
maxAge : app . enabled ( 'cache' ) ? 5184000000 : 0
} ) ) ;
// 404 catch-all
app . use ( function ( req , res , next ) {
var isLanguage = new RegExp ( '^' + nconf . get ( 'relative_path' ) + '/language/[\\w]{2,}/.*.json' ) ,
isClientScript = new RegExp ( '^' + nconf . get ( 'relative_path' ) + '\\/src\\/forum(\\/admin)?\\/[\\w]+\\.js' ) ;
res . status ( 404 ) ;
if ( isClientScript . test ( req . url ) ) {
// Handle missing client-side scripts
res . type ( 'text/javascript' ) . send ( 200 , '' ) ;
} else if ( isLanguage . test ( req . url ) ) {
// Handle languages by sending an empty object
res . json ( 200 , { } ) ;
} else if ( req . accepts ( 'html' ) ) {
// respond with html page
if ( process . env . NODE _ENV === 'development' ) {
winston . warn ( 'Route requested but not found: ' + req . url ) ;
}
res . redirect ( nconf . get ( 'relative_path' ) + '/404' ) ;
} else if ( req . accepts ( 'json' ) ) {
// respond with json
if ( process . env . NODE _ENV === 'development' ) {
winston . warn ( 'Route requested but not found: ' + req . url ) ;
}
res . json ( {
error : 'Not found'
} ) ;
} else {
// default to plain-text. send()
res . type ( 'txt' ) . send ( 'Not found' ) ;
}
} ) ;
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 ) ;
var status = err . status || 500 ;
res . status ( status ) ;
res . json ( status , {
error : err . message
} ) ;
} ) ;
next ( ) ;
}
] , function ( err ) {
if ( err ) {
winston . error ( 'Errors were encountered while attempting to initialise NodeBB.' ) ;
process . exit ( ) ;
} else {
if ( process . env . NODE _ENV === 'development' ) {
winston . info ( 'Middlewares loaded.' ) ;
}
}
} ) ;
} ) ;
module . exports . init = function ( ) {
// translate all static templates served by webserver here. ex. footer, logout
plugins . fireHook ( 'filter:footer.build' , '' , function ( err , appendHTML ) {
var footer = templates . footer . parse ( {
footerHTML : appendHTML
} ) ;
translator . translate ( footer , function ( parsedTemplate ) {
templates . footer = parsedTemplate ;
} ) ;
} ) ;
plugins . fireHook ( 'action:app.load' , app ) ;
translator . translate ( templates . logout . toString ( ) , function ( parsedTemplate ) {
templates . logout = parsedTemplate ;
} ) ;
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' ) ;
} ) ;
} ;
app . create _route = function ( url , tpl ) { // to remove
var routerScript = ' < script > \
ajaxify . initialLoad = true ; \
templates . ready ( function ( ) { ajaxify . go ( "' + url + '" , null , true ) ; } ) ; \
< / s c r i p t > ' ;
return routerScript ;
} ;
app . namespace ( nconf . get ( 'relative_path' ) , function ( ) {
auth . registerApp ( app ) ;
admin . createRoutes ( app ) ;
userRoute . createRoutes ( app ) ;
apiRoute . createRoutes ( app ) ;
feedsRoute . createRoutes ( app ) ;
// Basic Routes (entirely client-side parsed, goal is to move the rest of the crap in this file into this one section)
( function ( ) {
var routes = [ 'login' , 'register' , 'account' , 'recent' , 'popular' , '403' , '404' , '500' ] ,
loginRequired = [ 'unread' , 'notifications' ] ;
async . each ( routes . concat ( loginRequired ) , function ( route , next ) {
app . get ( '/' + route , function ( req , res ) {
if ( ( route === 'register' || route === 'login' ) && ( req . user && req . user . uid > 0 ) ) {
user . getUserField ( req . user . uid , 'userslug' , function ( err , userslug ) {
res . redirect ( '/user/' + userslug ) ;
} ) ;
return ;
} else if ( route === 'register' && meta . config . allowRegistration !== undefined && parseInt ( meta . config . allowRegistration , 10 ) === 0 ) {
return res . redirect ( '/403' ) ;
} else if ( loginRequired . indexOf ( route ) !== - 1 && ! req . user ) {
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 ) ;
} ) ;
} ) ;
} ) ;
} ( ) ) ;
app . get ( '/' , function ( req , res ) {
async . parallel ( {
"header" : function ( next ) {
app . build _header ( {
req : req ,
res : res ,
metaTags : [ {
name : "title" ,
content : meta . config . title || 'NodeBB'
} , {
name : "description" ,
content : meta . config . description || ''
} , {
property : 'og:title' ,
content : 'Index | ' + ( meta . config . title || 'NodeBB' )
} , {
property : "og:type" ,
content : 'website'
} ]
} , next ) ;
} ,
"categories" : function ( next ) {
function canSee ( category , next ) {
CategoryTools . privileges ( category . cid , ( ( req . user ) ? req . user . uid || 0 : 0 ) , function ( err , privileges ) {
next ( ! err && privileges . read ) ;
} ) ;
}
categories . getAllCategories ( 0 , function ( err , returnData ) {
returnData . categories = returnData . categories . filter ( function ( category ) {
return parseInt ( category . disabled , 10 ) !== 1 ;
} ) ;
async . filter ( returnData . categories , canSee , function ( visibleCategories ) {
returnData . categories = visibleCategories ;
next ( null , returnData ) ;
} ) ;
} ) ;
}
} , function ( err , data ) {
res . send (
data . header +
'\n\t<noscript>\n' + templates [ 'noscript/header' ] + templates [ 'noscript/home' ] . parse ( data . categories ) + '\n\t</noscript>' +
app . create _route ( '' ) +
templates . footer
) ;
} ) ;
} ) ;
app . get ( '/topic/:topic_id/:slug?' , function ( req , res , next ) {
var tid = req . params . topic _id ;
async . waterfall ( [
function ( next ) {
ThreadTools . privileges ( tid , ( ( req . user ) ? req . user . uid || 0 : 0 ) , function ( err , privileges ) {
if ( ! err ) {
if ( ! privileges . read ) {
next ( new Error ( 'not-enough-privileges' ) ) ;
} else {
next ( ) ;
}
} else {
next ( err ) ;
}
} ) ;
} ,
function ( next ) {
topics . getTopicWithPosts ( tid , ( ( req . user ) ? req . user . uid : 0 ) , 0 , - 1 , true , function ( err , topicData ) {
if ( topicData ) {
if ( parseInt ( topicData . deleted , 10 ) === 1 && parseInt ( topicData . expose _tools , 10 ) === 0 ) {
return next ( new Error ( 'Topic deleted' ) , null ) ;
}
}
next ( err , topicData ) ;
} ) ;
} ,
function ( topicData , next ) {
var lastMod = topicData . timestamp ,
sanitize = validator . sanitize ,
description = ( function ( ) {
var content = '' ;
if ( topicData . posts . length ) {
content = S ( topicData . posts [ 0 ] . content ) . stripTags ( ) . s ;
}
if ( content . length > 255 ) {
content = content . substr ( 0 , 255 ) + '...' ;
}
return sanitize ( content ) . escape ( ) ;
} ) ( ) ,
timestamp ;
for ( var x = 0 , numPosts = topicData . posts . length ; x < numPosts ; x ++ ) {
timestamp = parseInt ( topicData . posts [ x ] . timestamp , 10 ) ;
if ( timestamp > lastMod ) {
lastMod = timestamp ;
}
}
app . build _header ( {
req : req ,
res : res ,
metaTags : [
{
name : "title" ,
content : topicData . topic _name
} ,
{
name : "description" ,
content : description
} ,
{
property : 'og:title' ,
content : topicData . topic _name
} ,
{
property : 'og:description' ,
content : description
} ,
{
property : "og:type" ,
content : 'article'
} ,
{
property : "og:url" ,
content : nconf . get ( 'url' ) + '/topic/' + topicData . slug
} ,
{
property : "og:image:url" ,
content : nconf . get ( 'url' ) + ( meta . config [ 'brand:logo' ] ? meta . config [ 'brand:logo' ] : '' )
} ,
{
property : 'og:image' ,
content : topicData . posts . length ? topicData . posts [ 0 ] . picture : ''
} ,
{
property : "article:published_time" ,
content : utils . toISOString ( topicData . timestamp )
} ,
{
property : 'article:modified_time' ,
content : utils . toISOString ( lastMod )
} ,
{
property : 'article:section' ,
content : topicData . category _name
}
] ,
linkTags : [
{
rel : 'alternate' ,
type : 'application/rss+xml' ,
href : nconf . get ( 'url' ) + '/topic/' + tid + '.rss'
} ,
{
rel : 'up' ,
href : nconf . get ( 'url' ) + '/category/' + topicData . category _slug
}
]
} , function ( err , header ) {
next ( err , {
header : header ,
topics : topicData
} ) ;
} ) ;
} ,
] , function ( err , data ) {
if ( err ) {
if ( err . message === 'not-enough-privileges' ) {
return res . redirect ( '403' ) ;
} else {
return res . redirect ( '404' ) ;
}
}
var topic _url = tid + ( req . params . slug ? '/' + req . params . slug : '' ) ;
var queryString = qs . stringify ( req . query ) ;
if ( queryString . length ) {
topic _url += '?' + queryString ;
}
res . send (
data . header +
'\n\t<noscript>\n' + templates [ 'noscript/header' ] + templates [ 'noscript/topic' ] . parse ( data . topics ) + '\n\t</noscript>' +
'\n\t' + app . create _route ( 'topic/' + topic _url ) + templates . footer
) ;
} ) ;
} ) ;
app . get ( '/category/:category_id/:slug?' , function ( req , res , next ) {
var cid = req . params . category _id ;
async . waterfall ( [
function ( next ) {
CategoryTools . privileges ( cid , ( ( req . user ) ? req . user . uid || 0 : 0 ) , function ( err , privileges ) {
if ( ! err ) {
if ( ! privileges . read ) {
next ( new Error ( 'not-enough-privileges' ) ) ;
} else {
next ( ) ;
}
} else {
next ( err ) ;
}
} ) ;
} ,
function ( next ) {
categories . getCategoryById ( cid , 0 , - 1 , 0 , function ( err , categoryData ) {
if ( categoryData ) {
if ( parseInt ( categoryData . disabled , 10 ) === 1 ) {
return next ( new Error ( 'Category disabled' ) , null ) ;
}
}
next ( err , categoryData ) ;
} ) ;
} ,
function ( categoryData , next ) {
app . build _header ( {
req : req ,
res : res ,
metaTags : [
{
name : 'title' ,
content : categoryData . category _name
} ,
{
property : 'og:title' ,
content : categoryData . category _name
} ,
{
name : 'description' ,
content : categoryData . category _description
} ,
{
property : "og:type" ,
content : 'website'
}
] ,
linkTags : [
{
rel : 'alternate' ,
type : 'application/rss+xml' ,
href : nconf . get ( 'url' ) + '/category/' + cid + '.rss'
} ,
{
rel : 'up' ,
href : nconf . get ( 'url' )
}
]
} , function ( err , header ) {
next ( err , {
header : header ,
categories : categoryData
} ) ;
} ) ;
}
] , function ( err , data ) {
if ( err ) {
if ( err . message === 'not-enough-privileges' ) {
return res . redirect ( '403' ) ;
} else {
return res . redirect ( '404' ) ;
}
}
if ( data . categories . link ) {
return res . redirect ( data . categories . link ) ;
}
var category _url = cid + ( req . params . slug ? '/' + req . params . slug : '' ) ;
var queryString = qs . stringify ( req . query ) ;
if ( queryString . length ) {
category _url += '?' + queryString ;
}
res . send (
data . header +
'\n\t<noscript>\n' + templates [ 'noscript/header' ] + templates [ 'noscript/category' ] . parse ( data . categories ) + '\n\t</noscript>' +
'\n\t' + app . create _route ( 'category/' + category _url ) + templates . footer
) ;
} ) ;
} ) ;
app . get ( '/confirm/:code' , function ( req , res ) {
app . build _header ( {
req : req ,
res : res
} , function ( err , header ) {
res . send ( header + app . create _route ( 'confirm/' + req . params . code ) + templates . footer ) ;
} ) ;
} ) ;
app . get ( '/sitemap.xml' , function ( req , res ) {
var sitemap = require ( './sitemap.js' ) ;
sitemap . render ( function ( xml ) {
res . header ( 'Content-Type' , 'application/xml' ) ;
res . send ( xml ) ;
} ) ;
} ) ;
app . get ( '/robots.txt' , function ( req , res ) {
res . set ( 'Content-Type' , 'text/plain' ) ;
if ( meta . config [ "robots.txt" ] ) {
res . send ( meta . config [ "robots.txt" ] )
} else {
res . send ( "User-agent: *\n" +
"Disallow: /admin/\n" +
"Sitemap: " + nconf . get ( 'url' ) + "/sitemap.xml" ) ;
}
} ) ;
app . get ( '/recent/:term?' , function ( req , res ) {
// TODO consolidate with /recent route as well -> that can be combined into this area. See "Basic Routes" near top.
app . build _header ( {
req : req ,
res : res
} , function ( err , header ) {
res . send ( header + app . create _route ( 'recent/' + req . params . term , null , 'recent' ) + templates . footer ) ;
} ) ;
} ) ;
app . get ( '/popular/:term?' , function ( req , res ) {
app . build _header ( {
req : req ,
res : res
} , function ( err , header ) {
res . send ( header + app . create _route ( 'popular/' + req . params . term , null , 'popular' ) + templates . footer ) ;
} ) ;
} ) ;
app . get ( '/outgoing' , function ( req , res ) {
if ( ! req . query . url ) {
return res . redirect ( '/404' ) ;
}
app . build _header ( {
req : req ,
res : res
} , function ( err , header ) {
res . send ( header + app . create _route ( 'outgoing?url=' + encodeURIComponent ( req . query . url ) ) + templates . footer ) ;
} ) ;
} ) ;
app . get ( '/search/:term?' , function ( req , res ) {
if ( ! req . user && meta . config . allowGuestSearching !== '1' ) {
return res . redirect ( '/403' ) ;
}
if ( ! req . params . term ) {
req . params . term = '' ;
}
app . build _header ( {
req : req ,
res : res
} , function ( err , header ) {
res . send ( header + app . create _route ( 'search/' + req . params . term , null , 'search' ) + templates . footer ) ;
} ) ;
} ) ;
// Other routes
require ( './routes/plugins' ) ( app ) ;
// Debug routes
if ( process . env . NODE _ENV === 'development' ) {
require ( './routes/debug' ) ( app ) ;
}
var custom _routes = {
'routes' : [ ] ,
'api' : [ ] ,
'templates' : [ ]
} ;
app . get _custom _templates = function ( ) {
return custom _routes . templates . map ( function ( tpl ) {
return tpl . template . split ( '.tpl' ) [ 0 ] ;
} ) ;
}
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 ) ) {
( function ( route ) {
app [ routes [ route ] . method || 'get' ] ( routes [ route ] . route , function ( req , res ) {
routes [ route ] . options ( req , res , function ( options ) {
app . build _header ( {
req : options . req || req ,
res : options . res || res
} , function ( err , header ) {
res . send ( header + options . content + templates . footer ) ;
} ) ;
} ) ;
} ) ;
} ( route ) ) ;
}
}
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 ) ) ;
}
}
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 ) ) ;
}
}
} ) ;
} ) ;
} ) ;
} ( WebServer ) ) ;