logger = require 'logger-sharelatex' fs = require 'fs' crypto = require 'crypto' Settings = require('settings-sharelatex') SubscriptionFormatters = require('../Features/Subscription/SubscriptionFormatters') querystring = require('querystring') SystemMessageManager = require("../Features/SystemMessages/SystemMessageManager") AuthenticationController = require("../Features/Authentication/AuthenticationController") _ = require("underscore") async = require("async") Modules = require "./Modules" Url = require "url" PackageVersions = require "./PackageVersions" htmlEncoder = new require("node-html-encoder").Encoder("numerical") hashedFiles = {} Path = require 'path' Features = require "./Features" Modules = require "./Modules" jsPath = if Settings.useMinifiedJs "/minjs/" else "/js/" ace = PackageVersions.lib('ace') pdfjs = PackageVersions.lib('pdfjs') fineuploader = PackageVersions.lib('fineuploader') getFileContent = (filePath)-> filePath = Path.join __dirname, "../../../", "public#{filePath}" exists = fs.existsSync filePath if exists content = fs.readFileSync filePath, "UTF-8" return content else logger.log filePath:filePath, "file does not exist for hashing" return "" pathList = [ "#{jsPath}libs/require.js" "#{jsPath}ide.js" "#{jsPath}main.js" "#{jsPath}libraries.js" "/stylesheets/style.css" "/stylesheets/ol-style.css" ].concat(Modules.moduleAssetFiles(jsPath)) if !Settings.useMinifiedJs logger.log "not using minified JS, not hashing static files" else logger.log "Generating file hashes..." for path in pathList content = getFileContent(path) hash = crypto.createHash("md5").update(content).digest("hex") splitPath = path.split("/") filenameSplit = splitPath.pop().split(".") filenameSplit.splice(filenameSplit.length-1, 0, hash) splitPath.push(filenameSplit.join(".")) hashPath = splitPath.join("/") hashedFiles[path] = hashPath fsHashPath = Path.join __dirname, "../../../", "public#{hashPath}" fs.writeFileSync(fsHashPath, content) logger.log "Finished hashing static content" cdnAvailable = Settings.cdn?.web?.host? darkCdnAvailable = Settings.cdn?.web?.darkHost? module.exports = (app, webRouter, privateApiRouter, publicApiRouter)-> webRouter.use (req, res, next)-> res.locals.session = req.session next() addSetContentDisposition = (req, res, next) -> res.setContentDisposition = (type, opts) -> directives = for k, v of opts "#{k}=\"#{encodeURIComponent(v)}\"" contentDispositionValue = "#{type}; #{directives.join('; ')}" res.setHeader( 'Content-Disposition', contentDispositionValue ) next() webRouter.use addSetContentDisposition privateApiRouter.use addSetContentDisposition publicApiRouter.use addSetContentDisposition webRouter.use (req, res, next)-> req.externalAuthenticationSystemUsed = Features.externalAuthenticationSystemUsed res.locals.externalAuthenticationSystemUsed = Features.externalAuthenticationSystemUsed req.hasFeature = res.locals.hasFeature = Features.hasFeature res.locals.userIsFromOLv1 = (user) -> user.overleaf?.id? res.locals.userIsFromSL = (user) -> !user.overleaf?.id? next() webRouter.use (req, res, next)-> cdnBlocked = req.query.nocdn == 'true' or req.session.cdnBlocked user_id = AuthenticationController.getLoggedInUserId(req) if cdnBlocked and !req.session.cdnBlocked? logger.log user_id:user_id, ip:req?.ip, "cdnBlocked for user, not using it and turning it off for future requets" req.session.cdnBlocked = true isDark = req.headers?.host?.slice(0,7)?.toLowerCase().indexOf("dark") != -1 isSmoke = req.headers?.host?.slice(0,5)?.toLowerCase() == "smoke" isLive = !isDark and !isSmoke if cdnAvailable and isLive and !cdnBlocked staticFilesBase = Settings.cdn?.web?.host else if darkCdnAvailable and isDark staticFilesBase = Settings.cdn?.web?.darkHost else staticFilesBase = "" res.locals.jsPath = jsPath res.locals.fullJsPath = Url.resolve(staticFilesBase, jsPath) res.locals.lib = PackageVersions.lib res.locals.buildJsPath = (jsFile, opts = {})-> path = Path.join(jsPath, jsFile) if opts.hashedPath && hashedFiles[path]? path = hashedFiles[path] if !opts.qs? opts.qs = {} if opts.cdn != false path = Url.resolve(staticFilesBase, path) qs = querystring.stringify(opts.qs) if opts.removeExtension == true path = path.slice(0,-3) if qs? and qs.length > 0 path = path + "?" + qs return path res.locals.buildWebpackPath = (jsFile, opts = {}) -> if Settings.webpack? and !Settings.useMinifiedJs path = Path.join(jsPath, jsFile) if opts.removeExtension == true path = path.slice(0,-3) return "#{Settings.webpack.url}/public#{path}" else return res.locals.buildJsPath(jsFile, opts) res.locals.buildCssPath = (cssFile, opts)-> path = Path.join("/stylesheets/", cssFile) if opts?.hashedPath && hashedFiles[path]? hashedPath = hashedFiles[path] return Url.resolve(staticFilesBase, hashedPath) return Url.resolve(staticFilesBase, path) res.locals.buildImgPath = (imgFile)-> path = Path.join("/img/", imgFile) return Url.resolve(staticFilesBase, path) res.locals.mathJaxPath = res.locals.buildJsPath( 'libs/mathjax/MathJax.js', {cdn:false, qs:{config:'TeX-AMS_HTML'}} ) next() webRouter.use (req, res, next)-> res.locals.settings = Settings next() webRouter.use (req, res, next)-> res.locals.translate = (key, vars = {}, htmlEncode = false) -> vars.appName = Settings.appName str = req.i18n.translate(key, vars) if htmlEncode then htmlEncoder.htmlEncode(str) else str # Don't include the query string parameters, otherwise Google # treats ?nocdn=true as the canonical version res.locals.currentUrl = Url.parse(req.originalUrl).pathname res.locals.capitalize = (string) -> return "" if string.length == 0 return string.charAt(0).toUpperCase() + string.slice(1) next() webRouter.use (req, res, next)-> res.locals.getSiteHost = -> Settings.siteUrl.substring(Settings.siteUrl.indexOf("//")+2) next() webRouter.use (req, res, next) -> res.locals.getUserEmail = -> user = AuthenticationController.getSessionUser(req) email = user?.email or "" return email next() webRouter.use (req, res, next)-> res.locals.formatProjectPublicAccessLevel = (privilegeLevel)-> formatedPrivileges = private:"Private", readOnly:"Public: Read Only", readAndWrite:"Public: Read and Write" return formatedPrivileges[privilegeLevel] || "Private" next() webRouter.use (req, res, next)-> res.locals.buildReferalUrl = (referal_medium) -> url = Settings.siteUrl currentUser = AuthenticationController.getSessionUser(req) if currentUser? and currentUser?.referal_id? url+="?r=#{currentUser.referal_id}&rm=#{referal_medium}&rs=b" # Referal source = bonus return url res.locals.getReferalId = -> currentUser = AuthenticationController.getSessionUser(req) if currentUser? and currentUser?.referal_id? return currentUser.referal_id res.locals.getReferalTagLine = -> tagLines = [ "Roar!" "Shout about us!" "Please recommend us" "Tell the world!" "Thanks for using ShareLaTeX" ] return tagLines[Math.floor(Math.random()*tagLines.length)] res.locals.getRedirAsQueryString = -> if req.query.redir? return "?#{querystring.stringify({redir:req.query.redir})}" return "" res.locals.getLoggedInUserId = -> return AuthenticationController.getLoggedInUserId(req) res.locals.isUserLoggedIn = -> return AuthenticationController.isUserLoggedIn(req) res.locals.getSessionUser = -> return AuthenticationController.getSessionUser(req) next() webRouter.use (req, res, next) -> res.locals.csrfToken = req?.csrfToken() next() webRouter.use (req, res, next) -> res.locals.getReqQueryParam = (field)-> return req.query?[field] next() webRouter.use (req, res, next)-> res.locals.formatPrice = SubscriptionFormatters.formatPrice next() webRouter.use (req, res, next)-> currentUser = AuthenticationController.getSessionUser(req) if currentUser? res.locals.user = email: currentUser.email first_name: currentUser.first_name last_name: currentUser.last_name if req.session.justRegistered res.locals.justRegistered = true delete req.session.justRegistered if req.session.justLoggedIn res.locals.justLoggedIn = true delete req.session.justLoggedIn res.locals.gaToken = Settings.analytics?.ga?.token res.locals.tenderUrl = Settings.tenderUrl res.locals.sentrySrc = Settings.sentry?.src res.locals.sentryPublicDSN = Settings.sentry?.publicDSN next() webRouter.use (req, res, next) -> if req.query? and req.query.scribtex_path? res.locals.lookingForScribtex = true res.locals.scribtexPath = req.query.scribtex_path next() webRouter.use (req, res, next) -> # Clone the nav settings so they can be modified for each request res.locals.nav = {} for key, value of Settings.nav res.locals.nav[key] = _.clone(Settings.nav[key]) res.locals.templates = Settings.templateLinks if res.locals.nav.header console.error {}, "The `nav.header` setting is no longer supported, use `nav.header_extras` instead" next() webRouter.use (req, res, next) -> SystemMessageManager.getMessages (error, messages = []) -> res.locals.systemMessages = messages next() webRouter.use (req, res, next)-> res.locals.query = req.query next() webRouter.use (req, res, next)-> subdomain = _.find Settings.i18n.subdomainLang, (subdomain)-> subdomain.lngCode == req.showUserOtherLng and !subdomain.hide res.locals.recomendSubdomain = subdomain res.locals.currentLngCode = req.lng next() webRouter.use (req, res, next) -> if Settings.reloadModuleViewsOnEachRequest Modules.loadViewIncludes() res.locals.moduleIncludes = Modules.moduleIncludes res.locals.moduleIncludesAvailable = Modules.moduleIncludesAvailable next() webRouter.use (req, res, next) -> isOl = (Settings.brandPrefix == 'ol-') res.locals.uiConfig = defaultResizerSizeOpen : if isOl then 7 else 24 defaultResizerSizeClosed : if isOl then 7 else 24 eastResizerCursor : if isOl then "ew-resize" else null westResizerCursor : if isOl then "ew-resize" else null chatResizerSizeOpen : if isOl then 7 else 12 chatResizerSizeClosed : 0 chatMessageBorderSaturation: if isOl then "85%" else "70%" chatMessageBorderLightness : if isOl then "40%" else "70%" chatMessageBgSaturation : if isOl then "85%" else "60%" chatMessageBgLightness : if isOl then "40%" else "97%" defaultFontFamily : if isOl then 'lucida' else 'monaco' defaultLineHeight : if isOl then 'normal' else 'compact' renderAnnouncements : !isOl next()