UNPKG

cloudcms-server

Version:
1,395 lines (1,191 loc) 78 kB
var path = require('path'); var http = require('http'); var util = require("../../util/util"); var async = require("async"); var Gitana = require("gitana"); var passport = require('passport'); var LocalStrategy = require('passport-local').Strategy; var mime = require("mime"); var cloudcmsUtil = require("../../util/cloudcms"); var SENTINEL_NOT_FOUND_VALUE = "null"; var logEnabled = true; /** * Cloud CMS middleware. * * @type {*} */ /** * Looks up the user by username or email. * * @param req * @param username * @param callback */ var findUser = function(req, username, callback) { var query = { "$or": [{ "name": username }, { "email": username }] }; var domain = req.gitana.datastore("principals"); Chain(domain).queryPrincipals(query).then(function() { if (this.size() === 0) { // If user not found in principals domain, try the primary domain req.gitana.getPlatform().readPrimaryDomain().then(function() { this.queryPrincipals(query).then(function() { if (this.size() === 0) { return callback({ "message": "Unable to find user for username or email: " + username }); } else { this.keepOne().then(function() { callback(null, this); }); } }); }); } else { this.keepOne().then(function() { callback(null, this); }); } }); }; passport.use(new LocalStrategy({ passReqToCallback: true }, function(req, username, password, done) { var clientKey = req.gitanaConfig.clientKey; var clientSecret = req.gitanaConfig.clientSecret; var applicationId = req.gitanaConfig.application; var baseURL = req.gitanaConfig.baseURL; // pick the domain that we'll authenticate against var domainId = req.gitana.datastore("principals").getId(); findUser(req, username, function(err, user) { if (err) { return done(null, false, { "message": err.message }); } if (!user) { return done(null, false, { "message": "Unable to find user: " + username }); } // update username to the username of the actual user username = user.name; // update domain id in case the user is not in the principals domain if (user.domainId) { domainId = user.domainId; } // authenticate to cloud cms // automatically caches based on ticket Gitana.connect({ "clientKey": clientKey, "clientSecret": clientSecret, "application": applicationId, "username": domainId + "/" + username, "password": password, "baseURL": baseURL, "invalidatePlatformCache": true }, function(err) { if (err) { return done(null, false, err); } // authentication was successful! // auth info var authInfo = this.platform().getDriver().getAuthInfo(); // ticket var ticket = authInfo.getTicket(); // user object var user = { "id": authInfo.getPrincipalId(), "domainId": authInfo.getPrincipalDomainId(), "name": authInfo.getPrincipalName(), "firstName": authInfo["user"]["firstName"], "middleName": authInfo["user"]["middleName"], "lastName": authInfo["user"]["lastName"] }; // construct full name var fullName = null; if (user.firstName) { fullName = user.firstName; if (user.lastName) { fullName += " " + user.lastName; } } if (!fullName) { fullName = user.name; } user.fullName = fullName; done(null, user, { "ticket": ticket, "user": user, "test": 1 }); }); }); } )); //////////////////////////////////////////////////////////////////////////// // // INTERFACE METHODS // //////////////////////////////////////////////////////////////////////////// exports = module.exports = function() { var handleErrorMessage = function(req, res, errorMessage) { if (req.flash) { // stores error message into "flash" session memory req.flash("info", errorMessage); } var failureUrl = req.query["failureUrl"]; if (failureUrl) { // redirect //res.redirect(failureUrl); req.session.save(function(){ res.redirect(failureUrl); }); } else { // otherwise, send JSON response util.status(res, 503); var body = { "ok": false }; if (errorMessage) { body.message = errorMessage; } res.send(body); } }; var handleLogin = function(req, res, next) { var successUrl = req.query["successUrl"]; var failureUrl = req.query["failureUrl"]; var options = { //session: false }; passport.authenticate("local", options, function(err, user, info) { var errorMessage = null; if (err) { process.log("ERROR DURING AUTHENTICATION: " + err + ", text: " + JSON.stringify(err)); errorMessage = "There was a problem logging in, please contact your system administrator"; } if (!user) { if (info && info.message) { errorMessage = info.message; } else { errorMessage = "There was a problem logging in, please contact your system administrator. User not found."; } } // if there was an error, respond thusly if (errorMessage) { return handleErrorMessage(req, res, errorMessage); } // otherwise, we're ok // info contains the "GITANA_COOKIE" that we handle back as a SSO token // it should be sent over in the GITANA_COOKIE or a "GITANA_TICKET" header on every follow-on request var ticket = info.ticket; var user = info.user; // convert to a regular old JS object to be compatible with session serialization user = util.clone(user, true); // try to log in to cloud cms with this user req.logIn(user, function(err) { if (err) { // failed to log in... process.log("ERROR DURING LOGIN: " + err + ", text: " + JSON.stringify(err)); errorMessage = "There was a problem logging in, please contact your system administrator"; return handleErrorMessage(req, res, errorMessage); } var config = process.configuration || {}; config.auth = config.auth || {}; if (successUrl) { if(config.auth.passTicket) { res.redirect(successUrl + "?ticket=" + ticket); } else { res.reoirect(successUrl); } } else { // otherwise, send JSON response util.status(res, 200); var result = { "ok": true, "user": user }; if (config.auth.passTicket) { result.ticket = ticket; } res.send(result); } }); })(req, res, next); }; var handleLogout = function(req, res, next) { var logoutStrategiesFn = function(req, res, finished) { // if we have authentication strategies configured, walk each one // if a strategy has an authenticator, then invoke it's logout function var authenticationService = require("../authentication/authentication"); authenticationService.buildStrategies(req, function (err, strategyResults) { if (err || !strategyResults) { return finished(); } var fns = []; for (var strategyId in strategyResults) { var authenticator = strategyResults[strategyId].authenticator; if (authenticator) { var fn = function(req, res, strategyId, authenticator) { return function(done) { process.log("Logging out for strategy: " + strategyId); authenticator.logout(req, res, function () { done(); }); } }(req, res, strategyId, authenticator); fns.push(fn); } } async.series(fns, function() { finished(); }); }); }; logoutStrategiesFn(req, res, function(err) { var redirectUri = req.query["redirectUri"]; if (!redirectUri) { redirectUri = req.query["redirectURI"]; } if (!redirectUri) { redirectUri = req.query["redirect"]; } // throw this in here for safe measure if (req.logout) { req.logout(); } var ticket = req.query["ticket"]; if (ticket) { Gitana.disconnect(ticket); } // redirect? if (redirectUri) { return res.redirect(redirectUri); } util.status(res, 200); res.send({ "ok": true }); }); }; ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // RESULTING OBJECT // ////////////////////////////////////////////////////////////////////////////////////////////////////////////// var r = {}; /** * Determines which gitana repository to use in future operations. * * @return {Function} */ r.repositoryInterceptor = function() { return util.createInterceptor("repository", function(req, res, next, stores, cache, configuration) { if (req.gitana && req.gitana.datastore) { var repository = req.gitana.datastore("content"); if (repository) { req.repositoryId = repository.getId(); // helper function req.repository = function(repository) { return function(callback) { callback(null, repository); }; }(repository); } } if (!req.repository) { req.repository = function(callback){ callback(); }; } next(); }); }; // store a cache of branches since we don't want to reload these every time var CACHED_BRANCHES = {}; var _branch_key = function(repository, branchId) { return repository._doc + "_" + branchId; }; var _load_branch = function(repository, branchId, callback) { var cacheKey = _branch_key(repository, branchId); var branch = CACHED_BRANCHES[cacheKey]; if (branch) { return callback(null, Chain(branch)); } var loadFn = function(finished) { Chain(repository).trap(function(e) { // unable to load branch! process.log("Unable to load branch: " + repository._doc + "/" + branchId + ", err: " + e); process.log(e); finished({ "message": "Unable to load branch: " + repository._doc + "/" + branchId }); return false; }).readBranch(branchId).then(function() { finished(null, this); }); }; loadFn(function(err, branch) { if (err || !branch) { // try again... return loadFn(function(err, branch) { if (err) { return callback(err); } // success! // store in cache CACHED_BRANCHES[cacheKey] = branch; return callback(null, branch); }); } // store in cache CACHED_BRANCHES[cacheKey] = branch; // do the callback return callback(null, branch); }); }; var _invalidate_branch = function(repository, branchId) { var cacheKey = _branch_key(repository, branchId); delete CACHED_BRANCHES[cacheKey]; }; /** * Allows for branch switching via request parameter. * * @return {Function} */ r.branchInterceptor = function() { return util.createInterceptor("branch", function(req, res, next, stores, cache, configuration) { if (req.gitana) { req.application(function(err, application) { var branchCookieName = null; var branchCookieId = null; if (!err && application) { // if cookies are parsed ahead of interceptor, check them... if (req.cookies) { branchCookieName = "cloudcms-server-application-" + application.getId() + "-branch-id"; branchCookieId = req.cookies[branchCookieName]; } } // pick which branch var branchId = req.query["branch"]; if (!branchId) { branchId = req.query["branchId"]; } if (!branchId) { branchId = req.header("CLOUDCMS_BRANCH"); } if (!branchId) { branchId = req.header("BRANCH"); } if (!branchId) { branchId = req.header("branchId"); } if (!branchId) { // does the runtime tell us which branch to use? if (req.runtime && req.runtime.branchId) { branchId = req.runtime.branchId; } } if (!branchId) { // allow for the branch to specified via an environment parameter if (process.env.CLOUDCMS_BRANCH_ID) { branchId = process.env.CLOUDCMS_BRANCH_ID; } } if (!branchId) { // allow for the branch to specified via an environment parameter if (process.env.CLOUDCMS_RUNTIME_BRANCH_ID) { branchId = process.env.CLOUDCMS_RUNTIME_BRANCH_ID; } } if (!branchId) { branchId = branchCookieId; } // fallback to master if no other choice if (!branchId) { branchId = "master"; } // allow value to be forced? if (process.env.CLOUDCMS_RUNTIME_BRANCH_ID) { branchId = process.env.CLOUDCMS_RUNTIME_BRANCH_ID; } req.branchId = branchId; // write a cookie down to store branch ID if it changed if (branchId !== branchCookieId) { if (branchCookieName) { util.setCookie(req, res, branchCookieName, req.branchId); // legacy cleanup util.clearCookie(res, "cloudcms-server-branch-id"); } } // declare the helper function req.branch = function() { var _branch = null; return function(callback) { // if we already have a cached branch on this request, just hand that back if (_branch) { return callback(null, Chain(_branch)); } // load the branch, first get the repository req.repository(function(err, repository) { if (err) { process.log("Attempting to load branch, could not load repository, err: " + err); process.log(err); return callback({ "message": "Attempting to load branch, failed to load repository for branch: " + req.branchId }); } // now load the branch with a sync lock _load_branch(repository, req.branchId, function(err, branch) { if (err) { // make sure to remove cookie var cookieName = "cloudcms-server-application-" + req.applicationId + "-branch-id"; util.clearCookie(res, cookieName); return callback(err); } _branch = branch; callback(null, _branch); }); }); } }(); }); } if (!req.branch) { req.branch = function(callback){ callback(); }; } next(); }); }; /** * Determines which gitana domain to use in future operations. * * @return {Function} */ r.domainInterceptor = function() { return util.createInterceptor("domain", function(req, res, next, stores, cache, configuration) { if (req.gitana && req.gitana.datastore) { var domain = req.gitana.datastore("principals"); if (domain) { req.domainId = domain.getId(); } // helper function req.principalsDomain = function(callback) { callback(null, Chain(domain)); }; } if (!req.principalsDomain) { req.principalsDomain = function(callback){ callback(); }; } next(); }); }; /** * Determines which gitana domain to use in future operations. * * @return {Function} */ r.applicationInterceptor = function() { return util.createInterceptor("application", function(req, res, next, stores, cache, configuration) { if (req.gitana && req.gitana.datastore) { var application = req.gitana.application(); if (application) { req.applicationId = application.getId(); } // helper function req.application = function(application) { return function (callback) { callback(null, application); }; }(application); } if (!req.application) { req.application = function(callback){ callback(); }; } next(); }); }; var CACHED_APP_SETTINGS_TTL = 5 * 60 * 1000; // five minutes var CACHED_APP_SETTINGS = {}; var CACHED_APP_SETTINGS_TIMESTAMPS = {}; r.applicationSettingsInterceptor = function() { return util.createInterceptor("applicationSettings", function(req, res, next, stores, cache, configuration) { if (req.gitana && req.gitana.application) { var application = req.gitana.application(); if (application) { // helper function req.applicationSettings = function (req, application) { return function (callback) { var cacheKey = cacheSettingsKey(application.ref(), "application", "application"); var nowMs = Date.now(); var timestamp = CACHED_APP_SETTINGS_TIMESTAMPS[cacheKey]; if (!timestamp || (nowMs - timestamp > CACHED_APP_SETTINGS_TTL)) { // expire or didn't exist delete CACHED_APP_SETTINGS[cacheKey]; delete CACHED_APP_SETTINGS_TIMESTAMPS[cacheKey]; } var x = CACHED_APP_SETTINGS[cacheKey]; if (x) { if (x === SENTINEL_NOT_FOUND_VALUE) { return callback({ "message": "Failed to find application settings" }); } return callback(null, Chain(x)); } Chain(application).trap(function(e){ // store null sentinel CACHED_APP_SETTINGS[cacheKey] = SENTINEL_NOT_FOUND_VALUE; CACHED_APP_SETTINGS_TIMESTAMPS[cacheKey] = nowMs; callback({ "message": "Failed to find application settings" }); return false; }).querySettings({ "scope": "application", "key": "application" }).keepOne().then(function() { // store onto cache CACHED_APP_SETTINGS[cacheKey] = this; CACHED_APP_SETTINGS_TIMESTAMPS[cacheKey] = nowMs; // respond callback(null, this); }); }; }(req, application); } } // default if (!req.applicationSettings) { req.applicationSettings = function(callback) { callback(); }; } next(); }); }; /** * Allows for an in-context menu when connected to Cloud CMS for editing content. * * @return {Function} */ r.iceInterceptor = function() { return function(req, res, next) { if (req.gitana) { req.ice = true; } next(); } }; /** * Binds the req.cmsLog(message, level, data) method for use in logging to Cloud CMS. * * @return {Function} */ r.cmsLogInterceptor = function() { return util.createInterceptor("cmslog", function(req, res, next, stores, cache, configuration) { // define function req.cmsLog = function (message, level, data, callback) { if (!this.gitana) { process.log("Cannot find req.gitana instance, skipping logging"); return; } if (typeof(data) === "function") { callback = data; data = {}; } if (!data) { data = {}; } var obj = { "data": data }; this.gitana.platform().createLogEntry(message, level, obj).then(function () { if (callback) { callback(); } }); }; next(); }); }; /** * Provides virtualized content retrieval from Cloud CMS. * * This handler checks to see if the requested resource is already cached to disk. If not, it makes an attempt * to retrieve the content from Cloud CMS (and cache to disk). * * If nothing found, this handler passes through, allowing other handlers downstream to serve back the content. * * URIs may include the following structures: * * (preferred) * * /static/{filename}?repository={repositoryId}&branch={branchId}&node={nodeId} * /static/{filename}?repository={repositoryId}&branch={branchId}&path={path} * /static/{filename}?ref={ref} * * /preview/{filename}?repository={repositoryId}&branch={branchId}&node={nodeId} * /preview/{filename}?repository={repositoryId}&branch={branchId}&path={path} * /preview/{filename}?ref={ref} * * (legacy) * * /static/path/{path...} * /static/node/{nodeId} * /static/node/{nodeId}/{attachmentId} * /static/node/{nodeId}/{attachmentId}/{filename} * /static/repository/{repositoryId}/branch/{branchId}/node/{nodeId}/{attachmentId} * /static/repository/{repositoryId}/branch/{branchId}/node/{nodeId}/{attachmentId}/{filename} * /static/repository/{repositoryId}/branch/{branchId}/path/A/B/C/D... * /static/repository/{repositoryId}/branch/{branchId}?path=/A/B/C/D * * /preview/path/{path...} * /preview/node/{nodeId} * /preview/node/{nodeId}/{previewId} * /preview/repository/{repositoryId}/branch/{branchId}/node/{nodeId}/{previewId} * /preview/repository/{repositoryId}/branch/{branchId}/node/{nodeId}?name={previewId} * /preview/repository/{repositoryId}/branch/{branchId}/path/A/B/C/D/{previewId} * /preview/repository/{repositoryId}/branch/{branchId}/{previewId}?path={path} * /s/{applicationsPath} * * And the following flags are supported: * * metadata - set to true to retrieve JSON metadata for object * full - set to true to retrieve JSON recordset data * attachment - the ID of the attachment ("default") * force - whether to overwrite saved state * a - set to true to set Content Disposition response header * * For preview, the following are also supported: * * name - sets the name of the preview attachment id to be written / cached * mimetype - sets the desired mimetype of response * size - for images, sets the width in px of response image * * @param directory * @return {Function} */ r.virtualNodeHandler = function() { // bind listeners for broadcast events bindSubscriptions.call(this); return util.createHandler("virtualNode", null, function(req, res, next, stores, cache, configuration) { var contentStore = stores.content; var repositoryId = req.repositoryId; var branchId = req.branchId; var locale = req.locale; var previewId = null; var gitana = req.gitana; if (gitana) { var offsetPath = req.path; var virtualizedPath = null; var virtualizedNode = null; var virtualizedNodeExtra = null; var virtualizedUriExtra = null; var previewPath = null; var previewNode = null; var previewUriExtra = null; if (offsetPath.indexOf("/static/path/") === 0) { virtualizedPath = offsetPath.substring(13); } else if (offsetPath.indexOf("/static/node/") === 0) { virtualizedNode = offsetPath.substring(13); // trim off anything extra... var x = virtualizedNode.indexOf("/"); if (x > 0) { virtualizedNodeExtra = virtualizedNode.substring(x+1); virtualizedNode = virtualizedNode.substring(0,x); } } else if (offsetPath.indexOf("/static/repository/") === 0) { // examples // /static/repository/ABC/branch/DEF/node/XYZ // /static/repository/ABC/branch/DEF/node/XYZ/filename.ext // /static/repository/ABC/branch/DEF/path/A/B/C/D/E.jpg var z = offsetPath.substring(19); // ABC/branch/DEF/node/XYZ // pluck off the repository id var x1 = z.indexOf("/"); repositoryId = z.substring(0, x1); // advance to branch x1 = z.indexOf("/", x1+1); z = z.substring(x1+1); // DEF/node/XYZ // pluck off the branch id x1 = z.indexOf("/"); if (x1 > -1) { branchId = z.substring(0, x1); // advance to "thing" (either node or path) z = z.substring(x1+1); // node/XYZ or path/1/2/3/4 } else { branchId = z; z = ""; } // pluck off the thing // "node" or "path" or "{filename} x1 = z.indexOf("/"); var thing = null; if (x1 > -1) { thing = z.substring(0, x1); } else { thing = z; } if (thing === "node") { virtualizedNode = z.substring(x1+1); // trim off anything extra... var x = virtualizedNode.indexOf("/"); if (x > 0) { virtualizedNodeExtra = virtualizedNode.substring(x+1); virtualizedNode = virtualizedNode.substring(0,x); } } else if (thing === "path") { virtualizedPath = z.substring(x1+1); } else { virtualizedPath = req.query["path"]; } } else if (offsetPath.indexOf("/preview/path/") === 0) { previewPath = offsetPath.substring(14); } else if (offsetPath.indexOf("/preview/node/") === 0) { previewNode = offsetPath.substring(14); // trim off anything extra... var x = previewNode.indexOf("/"); if (x > 0) { previewNode = previewNode.substring(0,x); // if preview node has "/" in it, then it is "<nodeId>/<filename>" x1 = previewNode.indexOf("/"); if (x1 > -1) { previewId = previewNode.substring(x1 + 1); previewNode = previewNode.substring(0, x1); } } } else if (offsetPath.indexOf("/preview/repository/") === 0) { // examples // /preview/repository/ABC/branch/DEF/node/XYZ // /preview/repository/ABC/branch/DEF/path/1/2/3/4 // /preview/repository/ABC/branch/DEF/{previewId}?path={path} var z = offsetPath.substring(20); // ABC/branch/DEF/node/XYZ // pluck off the repository id var x1 = z.indexOf("/"); repositoryId = z.substring(0, x1); // advance to branch x1 = z.indexOf("/", x1+1); z = z.substring(x1+1); // DEF/node/XYZ // pluck off the branch id x1 = z.indexOf("/"); branchId = z.substring(0, x1); // advance to "thing" (either node or path or preview ID) z = z.substring(x1+1); // node/XYZ or path/1/2/3/4 or {previewId} // pluck off the thing // "node" or "path" or "{previewId} x1 = z.indexOf("/"); var thing = null; if (x1 > -1) { thing = z.substring(0, x1); } else { thing = z; } if (thing === "node") { previewNode = z.substring(x1+1); // if preview node has "/" in it, then it is "<nodeId>/<filename>" x1 = previewNode.indexOf("/"); if (x1 > -1) { previewId = previewNode.substring(x1 + 1); previewNode = previewNode.substring(0, x1); } } else if (thing == "path") { previewPath = z.substring(x1+1); } else { previewId = thing; previewPath = req.query["path"]; } } else if ((offsetPath.indexOf("/static") === 0) || (offsetPath.indexOf("/preview") === 0)) { var isStatic = (offsetPath.indexOf("/static") === 0); if (isStatic) { virtualizedUriExtra = offsetPath.substring(7); if (virtualizedUriExtra.indexOf("/") === 0) { virtualizedUriExtra = virtualizedUriExtra.substring(1); } } var isPreview = (offsetPath.indexOf("/preview") === 0); if (isPreview) { previewUriExtra = offsetPath.substring(8); if (previewUriExtra.indexOf("/") === 0) { previewUriExtra = previewUriExtra.substring(1); } } var _repositoryId = req.query["repository"]; if (_repositoryId) { repositoryId = _repositoryId; } var _branchId = req.query["branch"]; if (_branchId) { branchId = _branchId; } var _node = req.query["node"]; if (_node) { if (isStatic) { virtualizedNode = _node; } else if (isPreview) { previewNode = _node; } } var _path = req.query["path"]; if (_path) { if (isStatic) { virtualizedPath = _path; } else if (isPreview) { previewPath = _path; } } } // TODO: handle certain mimetypes // TODO: images, css, html, js? // virtualized content retrieval // these urls can have request parameters // // "metadata" // "full" // "attachment" // "force" // "a" (to force content disposition header) // // Virtual Path is: // /static/path/{...path}?options... // // Virtual Node is: // /static/node/{nodeId}?options... // /static/node/GUID/tommy.jpg?options... // if (virtualizedPath || virtualizedNode) { // node and path to offset against var nodePath = null; var nodeId = null; if (virtualizedNode) { nodeId = virtualizedNode; nodePath = null; } else if (virtualizedPath) { nodeId = "root"; nodePath = virtualizedPath; } var requestedFilename = null; var attachmentId = "default"; if (virtualizedNode && virtualizedNodeExtra) { attachmentId = virtualizedNodeExtra; if (attachmentId) { // if the attachment id is "a/b" or something with a slash in it // we keep everything ahead of the slash var p = attachmentId.indexOf("/"); if (p > -1) { requestedFilename = attachmentId.substring(p+1); attachmentId = attachmentId.substring(0, p); } else { requestedFilename = attachmentId; } } if (attachmentId) { var a = attachmentId.indexOf("."); if (a > -1) { attachmentId = attachmentId.substring(0, a); } } } // pass in the ?metadata=true parameter to get back the JSON for any Gitana object // otherwise, the "default" attachment is gotten if (req.query["metadata"]) { attachmentId = null; } // or override the attachmentId if (req.query["attachment"]) { attachmentId = req.query["attachment"]; } // check whether there is a file matching this uri if (nodePath && "/" === nodePath) { nodePath = "index.html"; } // the cache can be invalidated with either the "force" or "invalidate" request parameters var forceCommand = req.query["force"] ? req.query["force"] : false; var invalidateCommand = req.query["invalidate"] ? req.query["invalidate"] : false; var forceReload = forceCommand || invalidateCommand; // whether to set content disposition on response var useContentDispositionResponse = false; var a = req.query["a"]; if (a === "true") { useContentDispositionResponse = true; } var filename = req.query["filename"]; if (filename) { useContentDispositionResponse = true; } cloudcmsUtil.download(contentStore, gitana, repositoryId, branchId, nodeId, attachmentId, nodePath, locale, forceReload, function(err, filePath, cacheInfo, releaseLock) { // if the file was found on disk or was downloaded, then stream it back if (!err && filePath && cacheInfo) { if (useContentDispositionResponse) { var filename = resolveFilename(req, filePath, cacheInfo, requestedFilename); contentStore.downloadFile(res, filePath, filename, function(err) { // something went wrong while streaming the content back... if (err) { util.status(res, 503); res.send(err); res.end(); } releaseLock(); }); } else { util.applyDefaultContentTypeCaching(res, cacheInfo); //util.applyResponseContentType(res, cacheInfo, filePath); contentStore.sendFile(res, filePath, cacheInfo, function(err) { if (err) { util.handleSendFileError(req, res, filePath, cacheInfo, req.log, err); } releaseLock(); }); } } else { if (err && err.invalidateGitanaDriver) { process.log("Found err.invalidateGitanaDriver true"); if (req.gitanaConfig) { // at this point, our gitana driver's auth token was pronounced dead and we need to invalidate // to get a new one, so blow things away here // in terms of the current request, it is allowed to do the fallback // however the next request will go to the gitana.json and attempt to login // if that fails and virtual driver mode, then a new gitana.json will be pulled down if (req.gitanaConfig.key) { process.log("Disconnecting driver: " + req.gitanaConfig.key); try { Gitana.disconnect(req.gitanaConfig.key); } catch (e) { } } // remove from cache if (req.virtualHost) { process.log("Remove driver cache for virtual host: " + req.virtualHost); try { process.driverConfigCache.invalidate(req.virtualHost, function () { // all done }); } catch (e) { } } } } if (req.query["fallback"]) { // redirect to the fallback res.redirect(req.query["fallback"]); } else { // otherwise, allow other handlers to process this request next(); } releaseLock(); } }); } else if (previewPath || previewNode) { /* Params are: "name" "mimetype" "size" "force" Preview path is: /preview/path/{...path}?name={name}...rest of options Preview node is: /preview/node/{nodeId}?name={name}... rest of options /preview/node/GUID/tommy.jpg?name={name}... rest of options */ // node and path to offset against var nodePath = null; var nodeId = null; if (previewNode) { nodeId = previewNode; nodePath = null; } else if (previewPath) { nodeId = "root"; nodePath = previewPath; } // mimetype (allow null or undefined) var mimetype = req.query["mimetype"]; // determine attachment id var attachmentId = "default"; if (req.query["attachment"]) { attachmentId = req.query["attachment"]; } var requestedFilename = null; if (previewId) { requestedFilename = previewId; var p = previewId.indexOf("."); if (p > -1) { var extension = previewId.substring(p + 1); if (extension) { // see if we can determine the requested mimetype from the file extension of the previewId mimetype = util.lookupMimeType(extension); //mimetype = mime.lookup(extension); } previewId = previewId.substring(0, p); } } if (req.query["name"]) { previewId = req.query["name"]; } // note: mimetype can be null or undefined at this