UNPKG

cloudcms-server

Version:
664 lines (554 loc) 26.7 kB
var async = require("async"); var cluster = require("cluster"); var logFactory = require("../util/logger"); var logEnabled = true; var logger = this.logger = logFactory("notifications", { wid: true }); if (typeof(process.env.CLOUDCMS_NOTIFICATIONS_LOGGER_LEVEL) !== "undefined") { logger.setLevel(("" + process.env.CLOUDCMS_NOTIFICATIONS_LOGGER_LEVEL).toLowerCase(), true); } else { logger.setLevel("info"); } var logInfo = function(text) { if (logEnabled) { logger.info(text); } }; var determineHost = function(item, obj) { var host = null; // if virtual hosts aren't enabled, then host is the standalone host ("local") if (!process.configuration.virtualHost || !process.configuration.virtualHost.enabled) { host = process.env.CLOUDCMS_STANDALONE_HOST; } else { // assume host if specified on the item host = item.host; // if not specified on the item, assume value imputed based on virtual host being active if (process.configuration.virtualHost && process.configuration.virtualHost.enabled) { if (!host && item.tenantDnsSlug) { host = item.tenantDnsSlug + ".cloudcms.net"; } } // if we have an object, maybe we can use thaT? if (!host && obj) { host = obj.host; if (process.configuration.virtualHost && process.configuration.virtualHost.enabled) { if (!host && obj.tenantDnsSlug) { host = obj.tenantDnsSlug + ".cloudcms.net"; } } } } return host; }; var handleNotificationMessages = function(items, callback) { if (!items) { return callback(); } // wrap the processing of each item into a series function var fns = []; for (var i = 0; i < items.length; i++) { var fn = function(item, i) { return function(done) { //logFn("WORKING ON ITEM: " + i + ", item: " + JSON.stringify(item, null, " ")); var operation = item.operation; /** * @deprecated support for "invalidate_object" * This is left in to support legacy installations of the API that are still doing things object-by-object. * Newer API versions do bulk operations and use the "invalidate_objects" notification event. * * This will be removed at some point in the future. Please upgrade to the latest Cloud CMS API. */ if (operation === "invalidate_object") { var type = item.type; if (type === "node") { var ref = item.ref; var nodeId = item.id; var branchId = item.branchId; var repositoryId = item.repositoryId; // TEMP: some legacy support to aid in transition if (!repositoryId || !branchId || !nodeId) { var identifier = ref.substring(ref.indexOf("://") + 3); var parts = identifier.split("/").reverse(); nodeId = parts[0]; branchId = parts[1]; repositoryId = parts[2]; } var host = determineHost(item); var paths = item.paths || {}; // broadcast invalidation process.broadcast.publish("node_invalidation", { "ref": ref, "nodeId": nodeId, "branchId": branchId, "repositoryId": repositoryId, "isMasterBranch": item.isMasterBranch, "host": host, "paths": paths }, function(err) { return done(err); }); } else { return done(); } } else if (operation === "invalidate_objects") { var invalidations = item.invalidations; if (invalidations && invalidations.length > 0) { var z_fns = []; for (var z = 0; z < invalidations.length; z++) { var z_fn = function(item, obj, z) { return function(z_done) { var host = determineHost(item, obj); var type = obj.type; if (type === "node") { var ref = obj.ref; var nodeId = obj.id; var branchId = obj.branchId; var repositoryId = obj.repositoryId; var paths = obj.paths || {}; // TEMP: some legacy support to aid in transition if (!repositoryId || !branchId || !nodeId) { var identifier = ref.substring(ref.indexOf("://") + 3); var parts = identifier.split("/").reverse(); nodeId = parts[0]; branchId = parts[1]; repositoryId = parts[2]; } logInfo("Sending node invalidation for host: " + host); // broadcast invalidation process.broadcast.publish("node_invalidation", { "ref": ref, "nodeId": nodeId, "branchId": branchId, "repositoryId": repositoryId, "isMasterBranch": obj.isMasterBranch, "host": host, "paths": paths }, z_done); } else if (type === "tenant") { //var ref = obj.ref; // broadcast the cleanup_app event for this host process.broadcast.publish("cleanup_app", { "host": host }, z_done); } else if (type === "settings") { var ref = obj.ref; var settingsKey = obj.settingsKey; var settingsScope = obj.settingsScope; // broadcast invalidation process.broadcast.publish("settings_invalidation", { "ref": ref, "settingsKey": settingsKey, "settingsScope": settingsScope, "host": host }, z_done); } else if (type === "application") { var ref = obj.ref; var applicationId = obj.applicationId; var deploymentKey = obj.deploymentKey; var stackId = obj.stackId; var stackMembers = obj.stackMembers; process.broadcast.publish("application_invalidation", { "ref": ref, "applicationId": applicationId, "deploymentKey": deploymentKey, "host": host, "stackId": stackId, "stackMembers": stackMembers }); z_done(); } else if (type === "uiconfig") { var ref = obj.ref; var id = obj.id; // broadcast invalidation process.broadcast.publish("uiconfig_invalidation", { "ref": ref, "id": id, "host": host }, z_done); } else if (type === "deploy_app") { var body = obj.body; process.broadcast.publish("deploy_app", { "body": body }); z_done(); } else if (type === "undeploy_app") { var body = obj.body; process.broadcast.publish("undeploy_app", { "body": body }); z_done(); } else if (type === "redeploy_app") { var body = obj.body; process.broadcast.publish("redeploy_app", { "body": body }); z_done(); } else if (type === "start_app") { var body = obj.body; process.broadcast.publish("start_app", { "body": body }); z_done(); } else if (type === "stop_app") { var body = obj.body; process.broadcast.publish("stop_app", { "body": body }); z_done(); } else if (type === "restart_app") { var body = obj.body; process.broadcast.publish("restart_app", { "body": body }); z_done(); } else if (type === "cleanup_app") { var host = obj.host; process.broadcast.publish("cleanup_app", { "host": host }); z_done(); } else if (type === "module_deploy") { process.broadcast.publish("module_deploy", { "host": obj.host, "moduleId": obj.moduleId, "moduleConfig": obj.moduleConfig }); z_done(); } else if (type === "module_undeploy") { process.broadcast.publish("module_undeploy", { "host": obj.host, "moduleId": obj.moduleId, "moduleConfig": obj.moduleConfig }); z_done(); } else if (type === "module_refresh") { process.broadcast.publish("module_refresh", { "host": obj.host, "moduleId": obj.moduleId, "moduleConfig": obj.moduleConfig }); z_done(); } else { z_done(); } } }(item, invalidations[z], z); z_fns.push(z_fn); } async.series(z_fns, function(err) { return done(err); }); } else { return done(); } } else if (operation === "invalidate_application") { // TODO: invalidate any cache dependent on application return done(); } else if (operation === "invalidate_application_page_rendition") { logInfo("invalidate_application_page_rendition event\n" + JSON.stringify(item,null,2)); var deploymentKey = item.deploymentKey; var applicationId = item.applicationId; var repositoryId = item.repositoryId; var branchId = item.branchId; var isMasterBranch = item.isMasterBranch; var host = determineHost(item); // SAFETY CHECK: if no repository and/or branch, just bail if (!repositoryId || !branchId) { logInfo("Missing repositoryId or branchId, skipping WCM page invalidation (1)"); return done(); } var scope = item.scope; var key = item.key; var pageCacheKey = item.pageCacheKey; var message = { "key": key, "scope": scope, "pageCacheKey": pageCacheKey, "applicationId": applicationId, "deploymentKey": deploymentKey, "host": host, "repositoryId": repositoryId, "branchId": branchId, "isMasterBranch": isMasterBranch }; var fragmentCacheKey = item.fragmentCacheKey; if (fragmentCacheKey) { message.fragmentCacheKey = fragmentCacheKey; } // broadcast invalidation process.broadcast.publish("invalidate_page_rendition", message, function(err) { if (err) { logInfo("published invalidate_page_rendition message. err:" + err + "\nmessage: " + JSON.stringify(item,null,2)); } return done(err); }); } else if (operation === "invalidate_application_page_renditions") { logInfo("invalidate_application_page_renditions event"); var invalidations = item.invalidations; if (invalidations && invalidations.length > 0) { var z_fns = []; for (var z = 0; z < invalidations.length; z++) { var z_fn = function(item, obj) { return function(z_done) { var deploymentKey = obj.deploymentKey; var applicationId = obj.applicationId; var repositoryId = obj.repositoryId; var branchId = obj.branchId; var isMasterBranch = obj.isMasterBranch; var host = determineHost(item, obj); // SAFETY CHECK: if no repository and/or branch, just bail if (!repositoryId || !branchId) { logInfo("Missing repositoryId or branchId, skipping WCM page invalidation (2)"); return z_done(); } var scope = obj.scope; var key = obj.key; var pageCacheKey = obj.pageCacheKey; var message = { "key": key, "scope": scope, "pageCacheKey": pageCacheKey, "applicationId": applicationId, "deploymentKey": deploymentKey, "host": host, "repositoryId": repositoryId, "branchId": branchId, "isMasterBranch": isMasterBranch }; var fragmentCacheKey = obj.fragmentCacheKey; if (fragmentCacheKey) { message.fragmentCacheKey = fragmentCacheKey; } // broadcast invalidation process.broadcast.publish("invalidate_page_rendition", message, function(err) { if (err) { logInfo("published invalidate_page_rendition message. err:" + err + "\nmessage: " + JSON.stringify(message,null,2)); } z_done(err); }); } }(item, invalidations[z]); z_fns.push(z_fn); } async.series(z_fns, function(err) { return done(err); }); } } else if (operation === "invalidate_application_all_page_renditions") { var deploymentKey = item.deploymentKey; var applicationId = item.applicationId; var scope = item.scope; var host = determineHost(item); var message = { "applicationId": applicationId, "deploymentKey": deploymentKey, "scope": scope, "host": host }; // broadcast invalidation process.broadcast.publish("invalidate_all_page_renditions", message, function(err) { if (err) { logInfo("published invalidate_all_page_renditions message. err:" + err + "\nmessage: " + JSON.stringify(message,null,2)); } return done(err); }); } else { logInfo("Unknown notification item: " + JSON.stringify(item)); // just assume it's something we can't deal with return done({ "message": "Unknown notification item: " + item.rawMessage }); } } }(items[i], i); fns.push(fn); } // run all of the functions in series async.series(fns, function(err) { callback(err); }); }; var completeRunnerFn = function(provider, printStartMessage) { return runnerFn(provider, printStartMessage); }; var runnerCount = 0; var runnerFn = function(provider, printStartMessage) { var wid = "main"; if (cluster && cluster.worker) { wid = cluster.worker.id; } var runner = function(provider, runnerCount, wid, printStartMessage) { return function() { if (printStartMessage) { logInfo("[" + runnerCount + "] Starting notifications loop"); } provider.process(function(err, items, postHandleCallback) { if (err) { logInfo("[" + runnerCount + "] Notification Provider error: " + err, err.stack); // start it up again return completeRunnerFn(provider); } if (!items) { items = []; } if (items.length === 0) { // start it up again return completeRunnerFn(provider, false); } logInfo("[" + runnerCount + "] Notification Provider found: " + items.length + " notification items"); handleNotificationMessages(items, function (err) { logInfo("[" + runnerCount + "] Notification Provider handled: " + items.length + " items"); postHandleCallback(err, items, function (err, items, deletedItems) { if (err) { logInfo("[" + runnerCount + "] Notification Provider completed - handled: " + items.length + ", deleted: " + deletedItems.length); } // start it up again return completeRunnerFn(provider, true); }); }); }); } ; }(provider, runnerCount++, wid, printStartMessage); setTimeout(runner, 500); }; module.exports = function() { var r = {}; r.start = function(callback) { var config = process.configuration; if (!config["notifications"]) { config["notifications"] = { "enabled": false, "log": true, "type": "", "configuration": {} }; } var notifications = config["notifications"]; if ("log" in notifications) { logEnabled = notifications.log; } if (typeof(process.env.CLOUDCMS_NOTIFICATIONS_ENABLED) !== "undefined") { if (!process.env.CLOUDCMS_NOTIFICATIONS_ENABLED || process.env.CLOUDCMS_NOTIFICATIONS_ENABLED === "false") { notifications.enabled = false; } else if (process.env.CLOUDCMS_NOTIFICATIONS_ENABLED || process.env.CLOUDCMS_NOTIFICATIONS_ENABLED === "true") { notifications.enabled = true; } } if (notifications.enabled) { if (process.env.CLOUDCMS_NOTIFICATIONS_TYPE) { notifications.type = process.env.CLOUDCMS_NOTIFICATIONS_TYPE; } if (!notifications.type) { console.error("Notification.type is not configured"); return callback(); } var type = notifications.type; var configuration = notifications.configuration; var provider = require("./providers/" + type); provider.start(configuration, function (err) { if (err) { return callback(err); } // this starts the "thread" for the provider listener runnerFn(provider, true); callback(); }); } else { callback(); } }; r.notify = function(items, callback) { if (!callback) { callback = function() { }; } handleNotificationMessages(items, callback); }; return r; }();