Repository: ignite Updated Branches: refs/heads/ignite-843-rc3 407895a5c -> 125f56617
http://git-wip-us.apache.org/repos/asf/ignite/blob/721a1165/modules/control-center-web/src/main/js/serve/routes/caches.js ---------------------------------------------------------------------- diff --git a/modules/control-center-web/src/main/js/serve/routes/caches.js b/modules/control-center-web/src/main/js/serve/routes/caches.js new file mode 100644 index 0000000..7d719e3 --- /dev/null +++ b/modules/control-center-web/src/main/js/serve/routes/caches.js @@ -0,0 +1,196 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Fire me up! + +module.exports = { + implements: 'caches-routes', + inject: ['require(lodash)', 'require(express)', 'mongo'] +}; + +module.exports.factory = function (_, express, mongo) { + return new Promise((resolve) => { + const router = express.Router(); + + /** + * Get spaces and caches accessed for user account. + * + * @param req Request. + * @param res Response. + */ + router.post('/list', function (req, res) { + var user_id = req.currentUserId(); + + // Get owned space and all accessed space. + mongo.Space.find({$or: [{owner: user_id}, {usedBy: {$elemMatch: {account: user_id}}}]}, function (err, spaces) { + if (mongo.processed(err, res)) { + var space_ids = spaces.map(function (value) { + return value._id; + }); + + // Get all clusters for spaces. + mongo.Cluster.find({space: {$in: space_ids}}, '_id name caches').sort('name').exec(function (err, clusters) { + if (mongo.processed(err, res)) { + // Get all domain models for spaces. + mongo.DomainModel.find({space: {$in: space_ids}}).sort('name').exec(function (err, domains) { + if (mongo.processed(err, res)) { + // Get all caches for spaces. + mongo.Cache.find({space: {$in: space_ids}}).sort('name').exec(function (err, caches) { + if (mongo.processed(err, res)) { + _.forEach(clusters, function (cluster) { + cluster.caches = _.filter(cluster.caches, function (cacheId) { + return _.find(caches, {_id: cacheId}); + }); + }); + + _.forEach(domains, function (domain) { + domain.caches = _.filter(domain.caches, function (cacheId) { + return _.find(caches, {_id: cacheId}); + }); + }); + + _.forEach(caches, function (cache) { + // Remove deleted clusters. + cache.clusters = _.filter(cache.clusters, function (clusterId) { + return _.findIndex(clusters, function (cluster) { + return cluster._id.equals(clusterId); + }) >= 0; + }); + + // Remove deleted domain models. + cache.domains = _.filter(cache.domains, function (metaId) { + return _.findIndex(domains, function (domain) { + return domain._id.equals(metaId); + }) >= 0; + }); + }); + + res.json({ + spaces: spaces, + clusters: clusters.map(function (cluster) { + return { + value: cluster._id, + label: cluster.name, + caches: cluster.caches + }; + }), + domains: domains, + caches: caches + }); + } + }); + } + }); + } + }); + } + }); + }); + + /** + * Save cache. + */ + router.post('/save', function (req, res) { + var params = req.body; + var cacheId = params._id; + var clusters = params.clusters; + var domains = params.domains; + + if (params._id) { + mongo.Cache.update({_id: cacheId}, params, {upsert: true}, function (err) { + if (mongo.processed(err, res)) + mongo.Cluster.update({_id: {$in: clusters}}, {$addToSet: {caches: cacheId}}, {multi: true}, function (err) { + if (mongo.processed(err, res)) + mongo.Cluster.update({_id: {$nin: clusters}}, {$pull: {caches: cacheId}}, {multi: true}, function (err) { + if (mongo.processed(err, res)) + mongo.DomainModel.update({_id: {$in: domains}}, {$addToSet: {caches: cacheId}}, {multi: true}, function (err) { + if (mongo.processed(err, res)) + mongo.DomainModel.update({_id: {$nin: domains}}, {$pull: {caches: cacheId}}, {multi: true}, function (err) { + if (mongo.processed(err, res)) + res.send(params._id); + }); + }); + }); + }); + }) + } + else + mongo.Cache.findOne({space: params.space, name: params.name}, function (err, cache) { + if (mongo.processed(err, res)) { + if (cache) + return res.status(500).send('Cache with name: "' + cache.name + '" already exist.'); + + (new mongo.Cache(params)).save(function (err, cache) { + if (mongo.processed(err, res)) { + cacheId = cache._id; + + mongo.Cluster.update({_id: {$in: clusters}}, {$addToSet: {caches: cacheId}}, {multi: true}, function (err) { + if (mongo.processed(err, res)) + mongo.DomainModel.update({_id: {$in: domains}}, {$addToSet: {caches: cacheId}}, {multi: true}, function (err) { + if (mongo.processed(err, res)) + res.send(cacheId); + }); + }); + } + }); + } + }); + }); + + /** + * Remove cache by ._id. + */ + router.post('/remove', function (req, res) { + mongo.Cache.remove(req.body, function (err) { + if (mongo.processed(err, res)) + res.sendStatus(200); + }) + }); + + /** + * Remove all caches. + */ + router.post('/remove/all', function (req, res) { + var user_id = req.currentUserId(); + + // Get owned space and all accessed space. + mongo.Space.find({$or: [{owner: user_id}, {usedBy: {$elemMatch: {account: user_id}}}]}, function (err, spaces) { + if (mongo.processed(err, res)) { + var space_ids = spaces.map(function (value) { + return value._id; + }); + + mongo.Cache.remove({space: {$in: space_ids}}, function (err) { + if (err) + return res.status(500).send(err.message); + + mongo.Cluster.update({space: {$in: space_ids}}, {caches: []}, {multi: true}, function (err) { + if (mongo.processed(err, res)) + mongo.DomainModel.update({space: {$in: space_ids}}, {caches: []}, {multi: true}, function (err) { + if (mongo.processed(err, res)) + res.sendStatus(200); + }); + }); + }) + } + }); + }); + + resolve(router); + }); +}; + http://git-wip-us.apache.org/repos/asf/ignite/blob/721a1165/modules/control-center-web/src/main/js/serve/routes/clusters.js ---------------------------------------------------------------------- diff --git a/modules/control-center-web/src/main/js/serve/routes/clusters.js b/modules/control-center-web/src/main/js/serve/routes/clusters.js new file mode 100644 index 0000000..b740c01 --- /dev/null +++ b/modules/control-center-web/src/main/js/serve/routes/clusters.js @@ -0,0 +1,181 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Fire me up! + +module.exports = { + implements: 'clusters-routes', + inject: ['require(lodash)', 'require(express)', 'mongo'] +}; + +module.exports.factory = function (_, express, mongo) { + return new Promise((resolve) => { + const router = express.Router(); + + /** + * Get spaces and clusters accessed for user account. + * + * @param req Request. + * @param res Response. + */ + router.post('/list', function (req, res) { + var user_id = req.currentUserId(); + + // Get owned space and all accessed space. + mongo.Space.find({$or: [{owner: user_id}, {usedBy: {$elemMatch: {account: user_id}}}]}, function (err, spaces) { + if (mongo.processed(err, res)) { + var space_ids = spaces.map(function (value) { + return value._id; + }); + + // Get all caches for spaces. + mongo.Cache.find({space: {$in: space_ids}}).sort('name').deepPopulate('domains').exec(function (err, caches) { + if (mongo.processed(err, res)) { + // Get all IGFSs for spaces. + mongo.Igfs.find({space: {$in: space_ids}}).sort('name').exec(function (err, igfss) { + if (mongo.processed(err, res)) + // Get all clusters for spaces. + mongo.Cluster.find({space: {$in: space_ids}}).sort('name').deepPopulate(mongo.ClusterDefaultPopulate).exec(function (err, clusters) { + if (mongo.processed(err, res)) { + _.forEach(caches, function (cache) { + // Remove deleted caches. + cache.clusters = _.filter(cache.clusters, function (clusterId) { + return _.find(clusters, {_id: clusterId}); + }); + }); + + _.forEach(igfss, function (igfs) { + // Remove deleted caches. + igfs.clusters = _.filter(igfs.clusters, function (clusterId) { + return _.find(clusters, {_id: clusterId}); + }); + }); + + _.forEach(clusters, function (cluster) { + // Remove deleted caches. + cluster.caches = _.filter(cluster.caches, function (cacheId) { + return _.find(caches, {_id: cacheId}); + }); + + // Remove deleted IGFS. + cluster.igfss = _.filter(cluster.igfss, function (igfsId) { + return _.findIndex(igfss, function (igfs) { + return igfs._id.equals(igfsId); + }) >= 0; + }); + }); + + res.json({ + spaces: spaces, + caches: caches, + igfss: igfss, + clusters: clusters + }); + } + }); + }); + } + }); + } + }); + }); + + /** + * Save cluster. + */ + router.post('/save', function (req, res) { + var params = req.body; + var clusterId = params._id; + var caches = params.caches; + + if (params._id) + mongo.Cluster.update({_id: params._id}, params, {upsert: true}, function (err) { + if (mongo.processed(err, res)) + mongo.Cache.update({_id: {$in: caches}}, {$addToSet: {clusters: clusterId}}, {multi: true}, function (err) { + if (mongo.processed(err, res)) { + mongo.Cache.update({_id: {$nin: caches}}, {$pull: {clusters: clusterId}}, {multi: true}, function (err) { + if (mongo.processed(err, res)) + res.send(params._id); + }); + } + }); + }); + else { + mongo.Cluster.findOne({space: params.space, name: params.name}, function (err, cluster) { + if (mongo.processed(err, res)) { + if (cluster) + return res.status(500).send('Cluster with name: "' + cluster.name + '" already exist.'); + + (new mongo.Cluster(params)).save(function (err, cluster) { + if (mongo.processed(err, res)) { + clusterId = cluster._id; + + mongo.Cache.update({_id: {$in: caches}}, {$addToSet: {clusters: clusterId}}, {multi: true}, function (err) { + if (mongo.processed(err, res)) + res.send(clusterId); + }); + } + }); + } + }); + } + }); + + /** + * Remove cluster by ._id. + */ + router.post('/remove', function (req, res) { + mongo.Cluster.remove(req.body, function (err) { + if (err) + return res.status(500).send(err.message); + + res.sendStatus(200); + }) + }); + + /** + * Remove all clusters. + */ + router.post('/remove/all', function (req, res) { + var user_id = req.currentUserId(); + + // Get owned space and all accessed space. + mongo.Space.find({$or: [{owner: user_id}, {usedBy: {$elemMatch: {account: user_id}}}]}, function (err, spaces) { + if (mongo.processed(err, res)) { + var space_ids = spaces.map(function (value) { + return value._id; + }); + + mongo.Cluster.remove({space: {$in: space_ids}}, function (err) { + if (err) + return res.status(500).send(err.message); + + mongo.Cache.update({space: {$in: space_ids}}, {clusters: []}, {multi: true}, function (err) { + if (mongo.processed(err, res)) + mongo.Igfs.update({space: {$in: space_ids}}, {clusters: []}, {multi: true}, function (err) { + if (mongo.processed(err, res)) + res.sendStatus(200); + }); + }); + }) + } + }); + }); + + resolve(router); + }); +}; http://git-wip-us.apache.org/repos/asf/ignite/blob/721a1165/modules/control-center-web/src/main/js/serve/routes/domains.js ---------------------------------------------------------------------- diff --git a/modules/control-center-web/src/main/js/serve/routes/domains.js b/modules/control-center-web/src/main/js/serve/routes/domains.js new file mode 100644 index 0000000..5e6e934 --- /dev/null +++ b/modules/control-center-web/src/main/js/serve/routes/domains.js @@ -0,0 +1,285 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Fire me up! + +module.exports = { + implements: 'domains-routes', + inject: ['require(lodash)', 'require(express)', 'require(async)', 'mongo'] +}; + +module.exports.factory = function (_, express, async, mongo) { + return new Promise((resolve) => { + const router = express.Router(); + + /** + * Get spaces and domain models accessed for user account. + * + * @param req Request. + * @param res Response. + */ + router.post('/list', function (req, res) { + var user_id = req.currentUserId(); + + // Get owned space and all accessed space. + mongo.Space.find({$or: [{owner: user_id}, {usedBy: {$elemMatch: {account: user_id}}}]}, function (err, spaces) { + if (mongo.processed(err, res)) { + var space_ids = spaces.map(function (value) { + return value._id; + }); + + // Get all clusters for spaces. + mongo.Cluster.find({space: {$in: space_ids}}, '_id name').sort('name').exec(function (err, clusters) { + if (mongo.processed(err, res)) { + // Get all caches for spaces. + mongo.Cache.find({space: {$in: space_ids}}).sort('name').exec(function (err, caches) { + if (mongo.processed(err, res)) { + // Get all domain models for spaces. + mongo.DomainModel.find({space: {$in: space_ids}}).sort('valueType').exec(function (err, domains) { + if (mongo.processed(err, res)) { + _.forEach(caches, function (cache) { + cache.domains = _.filter(cache.domains, function (metaId) { + return _.find(domains, {_id: metaId}); + }); + }); + + // Remove deleted caches. + _.forEach(domains, function (domain) { + domain.caches = _.filter(domain.caches, function (cacheId) { + return _.find(caches, {_id: cacheId}); + }); + }); + + res.json({ + spaces: spaces, + clusters: clusters.map(function (cluster) { + return {value: cluster._id, label: cluster.name}; + }), + caches: caches, + domains: domains + }); + } + }); + } + }); + } + }); + } + }); + }); + + function _saveDomainModel(domain, savedDomains, callback) { + var domainId = domain._id; + var caches = domain.caches; + + var cacheStoreChanges = domain.cacheStoreChanges; + + if (domainId) + mongo.DomainModel.update({_id: domain._id}, domain, {upsert: true}, function (err) { + if (err) + callback(err); + else + mongo.Cache.update({_id: {$in: caches}}, {$addToSet: {domains: domainId}}, {multi: true}, function (err) { + if (err) + callback(err); + else + mongo.Cache.update({_id: {$nin: caches}}, {$pull: {domains: domainId}}, {multi: true}, function (err) { + if (err) + callback(err); + else { + savedDomains.push(domain); + + _updateCacheStore(cacheStoreChanges, callback); + } + }); + }); + }); + else + mongo.DomainModel.findOne({space: domain.space, valueType: domain.valueType}, function (err, found) { + if (err) + callback(err); + else if (found) + return callback('Domain model with value type: "' + found.valueType + '" already exist.'); + + (new mongo.DomainModel(domain)).save(function (err, domain) { + if (err) + callback(err); + else { + domainId = domain._id; + + mongo.Cache.update({_id: {$in: caches}}, {$addToSet: {domains: domainId}}, {multi: true}, function (err) { + if (err) + callback(err); + else { + savedDomains.push(domain); + + _updateCacheStore(cacheStoreChanges, callback); + } + }); + } + }); + }); + } + + function _updateCacheStore(cacheStoreChanges, callback) { + if (cacheStoreChanges && cacheStoreChanges.length > 0) { + async.forEachOf(cacheStoreChanges, function (change, idx, callback) { + mongo.Cache.update({_id: {$eq: change.cacheId}}, change.change, {}, function (err) { + if (err) + callback(err); + else + callback(); + }); + }, callback); + } + else + callback(); + } + + function _save(domains, res) { + var savedDomains = []; + var generatedCaches = []; + + if (domains && domains.length > 0) + async.forEachOf(domains, function (domain, idx, callback) { + if (domain.newCache) { + mongo.Cache.findOne({space: domain.space, name: domain.newCache.name}, function (err, cache) { + if (mongo.processed(err, res)) + if (cache) { + // Cache already exists, just save domain model. + domain.caches = [cache._id]; + + _saveDomainModel(domain, savedDomains, callback); + } + else { + // If cache not found, then create it and associate with domain model. + var newCache = domain.newCache; + newCache.space = domain.space; + + (new mongo.Cache(newCache)).save(function (err, cache) { + var cacheId = cache._id; + + if (mongo.processed(err, res)) { + mongo.Cluster.update({_id: {$in: cache.clusters}}, {$addToSet: {caches: cacheId}}, {multi: true}, function (err) { + if (mongo.processed(err, res)) { + domain.caches = [cacheId]; + generatedCaches.push(cache); + + _saveDomainModel(domain, savedDomains, callback); + } + }); + } + }); + } + }); + } + else + _saveDomainModel(domain, savedDomains, callback); + }, function (err) { + if (err) + res.status(500).send(err.message); + else + res.send({savedDomains: savedDomains, generatedCaches: generatedCaches}); + }); + else + res.status(500).send('Nothing to save!'); + } + + /** + * Save domain model. + */ + router.post('/save', function (req, res) { + _save([req.body], res); + }); + + /** + * Batch save domain models. + */ + router.post('/save/batch', function (req, res) { + _save(req.body, res); + }); + + /** + * Remove domain model by ._id. + */ + router.post('/remove', function (req, res) { + mongo.DomainModel.remove(req.body, function (err) { + if (mongo.processed(err, res)) + res.sendStatus(200); + }) + }); + + /** + * Remove all domain models. + */ + router.post('/remove/all', function (req, res) { + var user_id = req.currentUserId(); + + // Get owned space and all accessed space. + mongo.Space.find({$or: [{owner: user_id}, {usedBy: {$elemMatch: {account: user_id}}}]}, function (err, spaces) { + if (mongo.processed(err, res)) { + var space_ids = spaces.map(function (value) { + return value._id; + }); + + mongo.DomainModel.remove({space: {$in: space_ids}}, function (err) { + if (err) + return res.status(500).send(err.message); + + mongo.Cache.update({space: {$in: space_ids}}, {domains: []}, {multi: true}, function (err) { + if (mongo.processed(err, res)) + res.sendStatus(200); + }); + }) + } + }); + }); + + /** + * Remove all generated demo domain models and caches. + */ + router.post('/remove/demo', function (req, res) { + var user_id = req.currentUserId(); + + // Get owned space and all accessed space. + mongo.Space.find({$or: [{owner: user_id}, {usedBy: {$elemMatch: {account: user_id}}}]}, function (err, spaces) { + if (mongo.processed(err, res)) { + var space_ids = spaces.map(function (value) { + return value._id; + }); + + // Remove all demo domain models. + mongo.DomainModel.remove({$and: [{space: {$in: space_ids}}, {demo: true}]}, function (err) { + if (err) + return res.status(500).send(err.message); + + // Remove all demo caches. + mongo.Cache.remove({$and: [{space: {$in: space_ids}}, {demo: true}]}, function (err) { + if (err) + return res.status(500).send(err.message); + + res.sendStatus(200); + }); + }); + } + }); + }); + + resolve(router); + }); +}; + http://git-wip-us.apache.org/repos/asf/ignite/blob/721a1165/modules/control-center-web/src/main/js/serve/routes/igfs.js ---------------------------------------------------------------------- diff --git a/modules/control-center-web/src/main/js/serve/routes/igfs.js b/modules/control-center-web/src/main/js/serve/routes/igfs.js new file mode 100644 index 0000000..cf312f5 --- /dev/null +++ b/modules/control-center-web/src/main/js/serve/routes/igfs.js @@ -0,0 +1,154 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Fire me up! + +module.exports = { + implements: 'igfs-routes', + inject: ['require(lodash)', 'require(express)', 'mongo'] +}; + +module.exports.factory = function (_, express, mongo) { + return new Promise((resolve) => { + const router = express.Router(); + + /** + * Get spaces and IGFSs accessed for user account. + * + * @param req Request. + * @param res Response. + */ + router.post('/list', function (req, res) { + var user_id = req.currentUserId(); + + // Get owned space and all accessed space. + mongo.Space.find({$or: [{owner: user_id}, {usedBy: {$elemMatch: {account: user_id}}}]}, function (err, spaces) { + if (mongo.processed(err, res)) { + var space_ids = spaces.map(function (value) { + return value._id; + }); + + // Get all clusters for spaces. + mongo.Cluster.find({space: {$in: space_ids}}, '_id name').sort('name').exec(function (err, clusters) { + if (mongo.processed(err, res)) { + // Get all IGFSs for spaces. + mongo.Igfs.find({space: {$in: space_ids}}).sort('name').exec(function (err, igfss) { + if (mongo.processed(err, res)) { + _.forEach(igfss, function (igfs) { + // Remove deleted clusters. + igfs.clusters = _.filter(igfs.clusters, function (clusterId) { + return _.findIndex(clusters, function (cluster) { + return cluster._id.equals(clusterId); + }) >= 0; + }); + }); + + res.json({ + spaces: spaces, + clusters: clusters.map(function (cluster) { + return {value: cluster._id, label: cluster.name}; + }), + igfss: igfss + }); + } + }); + } + }); + } + }); + }); + + /** + * Save IGFS. + */ + router.post('/save', function (req, res) { + var params = req.body; + var igfsId = params._id; + var clusters = params.clusters; + + if (params._id) { + mongo.Igfs.update({_id: igfsId}, params, {upsert: true}, function (err) { + if (mongo.processed(err, res)) + mongo.Cluster.update({_id: {$in: clusters}}, {$addToSet: {igfss: igfsId}}, {multi: true}, function (err) { + if (mongo.processed(err, res)) + mongo.Cluster.update({_id: {$nin: clusters}}, {$pull: {igfss: igfsId}}, {multi: true}, function (err) { + if (mongo.processed(err, res)) + res.send(params._id); + }); + }); + }) + } + else + mongo.Igfs.findOne({space: params.space, name: params.name}, function (err, igfs) { + if (mongo.processed(err, res)) { + if (igfs) + return res.status(500).send('IGFS with name: "' + igfs.name + '" already exist.'); + + (new mongo.Igfs(params)).save(function (err, igfs) { + if (mongo.processed(err, res)) { + igfsId = igfs._id; + + mongo.Cluster.update({_id: {$in: clusters}}, {$addToSet: {igfss: igfsId}}, {multi: true}, function (err) { + if (mongo.processed(err, res)) + res.send(igfsId); + }); + } + }); + } + }); + }); + + /** + * Remove IGFS by ._id. + */ + router.post('/remove', function (req, res) { + mongo.Igfs.remove(req.body, function (err) { + if (mongo.processed(err, res)) + res.sendStatus(200); + }) + }); + + /** + * Remove all IGFSs. + */ + router.post('/remove/all', function (req, res) { + var user_id = req.currentUserId(); + + // Get owned space and all accessed space. + mongo.Space.find({$or: [{owner: user_id}, {usedBy: {$elemMatch: {account: user_id}}}]}, function (err, spaces) { + if (mongo.processed(err, res)) { + var space_ids = spaces.map(function (value) { + return value._id; + }); + + mongo.Igfs.remove({space: {$in: space_ids}}, function (err) { + if (err) + return res.status(500).send(err.message); + + mongo.Cluster.update({space: {$in: space_ids}}, {igfss: []}, {multi: true}, function (err) { + if (mongo.processed(err, res)) + res.sendStatus(200); + }); + }) + } + }); + }); + + resolve(router); + }); +}; + http://git-wip-us.apache.org/repos/asf/ignite/blob/721a1165/modules/control-center-web/src/main/js/serve/routes/notebooks.js ---------------------------------------------------------------------- diff --git a/modules/control-center-web/src/main/js/serve/routes/notebooks.js b/modules/control-center-web/src/main/js/serve/routes/notebooks.js new file mode 100644 index 0000000..af272fd --- /dev/null +++ b/modules/control-center-web/src/main/js/serve/routes/notebooks.js @@ -0,0 +1,159 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Fire me up! + +module.exports = { + implements: 'notebooks-routes', + inject: ['require(express)', 'mongo'] +}; + +module.exports.factory = function (express, mongo) { + return new Promise((resolve) => { + const router = express.Router(); + + /** + * Get notebooks names accessed for user account. + * + * @param req Request. + * @param res Response. + */ + router.post('/list', function (req, res) { + var user_id = req.currentUserId(); + + // Get owned space and all accessed space. + mongo.Space.find({$or: [{owner: user_id}, {usedBy: {$elemMatch: {account: user_id}}}]}, function (err, spaces) { + if (err) + return res.status(500).send(err.message); + + var space_ids = spaces.map(function (value) { + return value._id; + }); + + // Get all metadata for spaces. + mongo.Notebook.find({space: {$in: space_ids}}).select('_id name').sort('name').exec(function (err, notebooks) { + if (err) + return res.status(500).send(err.message); + + res.json(notebooks); + }); + }); + }); + + /** + * Get notebook accessed for user account. + * + * @param req Request. + * @param res Response. + */ + router.post('/get', function (req, res) { + var user_id = req.currentUserId(); + + // Get owned space and all accessed space. + mongo.Space.find({$or: [{owner: user_id}, {usedBy: {$elemMatch: {account: user_id}}}]}, function (err, spaces) { + if (err) + return res.status(500).send(err.message); + + var space_ids = spaces.map(function (value) { + return value._id; + }); + + // Get all metadata for spaces. + mongo.Notebook.findOne({space: {$in: space_ids}, _id: req.body.noteId}).exec(function (err, notebook) { + if (err) + return res.status(500).send(err.message); + + res.json(notebook); + }); + }); + }); + + /** + * Save notebook accessed for user account. + * + * @param req Request. + * @param res Response. + */ + router.post('/save', function (req, res) { + var note = req.body; + var noteId = note._id; + + if (noteId) + mongo.Notebook.update({_id: noteId}, note, {upsert: true}, function (err) { + if (err) + return res.status(500).send(err.message); + + res.send(noteId); + }); + else + mongo.Notebook.findOne({space: note.space, name: note.name}, function (err, note) { + if (err) + return res.status(500).send(err.message); + + if (note) + return res.status(500).send('Notebook with name: "' + note.name + '" already exist.'); + + (new mongo.Notebook(req.body)).save(function (err, note) { + if (err) + return res.status(500).send(err.message); + + res.send(note._id); + }); + }); + }); + + /** + * Remove notebook by ._id. + * + * @param req Request. + * @param res Response. + */ + router.post('/remove', function (req, res) { + mongo.Notebook.remove(req.body, function (err) { + if (err) + return res.status(500).send(err.message); + + res.sendStatus(200); + }); + }); + + /** + * Create new notebook for user account. + * + * @param req Request. + * @param res Response. + */ + router.post('/new', function (req, res) { + var user_id = req.currentUserId(); + + // Get owned space and all accessed space. + mongo.Space.findOne({owner: user_id}, function (err, space) { + if (err) + return res.status(500).send(err.message); + + (new mongo.Notebook({space: space.id, name: req.body.name})).save(function (err, note) { + if (err) + return res.status(500).send(err.message); + + return res.send(note._id); + }); + }); + }); + + resolve(router); + }); +}; http://git-wip-us.apache.org/repos/asf/ignite/blob/721a1165/modules/control-center-web/src/main/js/serve/routes/profile.js ---------------------------------------------------------------------- diff --git a/modules/control-center-web/src/main/js/serve/routes/profile.js b/modules/control-center-web/src/main/js/serve/routes/profile.js new file mode 100644 index 0000000..06db42a --- /dev/null +++ b/modules/control-center-web/src/main/js/serve/routes/profile.js @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Fire me up! + +module.exports = { + implements: 'profile-routes', + inject: ['require(lodash)', 'require(express)', 'mongo'] +}; + +module.exports.factory = function (_, express, mongo) { + return new Promise((resolve) => { + const router = express.Router(); + + function _updateUser(res, params) { + mongo.Account.update({_id: params._id}, params, {upsert: true}, function (err, user) { + // TODO IGNITE-843 Send error to admin. + if (err) + return res.status(500).send('Failed to update profile!'); + + if (params.email) + user.email = params.email; + + res.sendStatus(200); + }); + } + + function _checkEmail(res, user, params) { + if (params.email && user.email != params.email) { + mongo.Account.findOne({email: params.email}, function (err, userForEmail) { + // TODO send error to admin + if (err) + return res.status(500).send('Failed to check e-mail!'); + + if (userForEmail && userForEmail._id != user._id) + return res.status(500).send('User with this e-mail already registered!'); + + _updateUser(res, params); + }); + } + else + _updateUser(res, params); + } + + /** + * Save user profile. + */ + router.post('/save', function (req, res) { + var params = req.body; + + mongo.Account.findById(params._id, function (err, user) { + // TODO IGNITE-843 Send error to admin + if (err) + return res.status(500).send('Failed to find user!'); + + if (params.password) { + if (_.isEmpty(params.password)) + return res.status(500).send('Wrong value for new password!'); + + user.setPassword(params.password, function (err, user) { + if (err) + return res.status(500).send(err.message); + + user.save(function (err) { + if (err) + return res.status(500).send("Failed to change password!"); + + _checkEmail(res, user, params); + }); + }); + } + else + _checkEmail(res, user, params); + }); + }); + + resolve(router); + }); +}; http://git-wip-us.apache.org/repos/asf/ignite/blob/721a1165/modules/control-center-web/src/main/js/serve/routes/public.js ---------------------------------------------------------------------- diff --git a/modules/control-center-web/src/main/js/serve/routes/public.js b/modules/control-center-web/src/main/js/serve/routes/public.js new file mode 100644 index 0000000..1aa9bc0 --- /dev/null +++ b/modules/control-center-web/src/main/js/serve/routes/public.js @@ -0,0 +1,250 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +// Fire me up! + +module.exports = { + implements: 'public-routes', + inject: ['require(express)', 'require(passport)', 'require(nodemailer)', 'settings', 'mongo'] +}; + +module.exports.factory = function (express, passport, nodemailer, settings, mongo) { + return new Promise(function (resolve) { + const router = express.Router(); + + const _randomString = () => { + const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + const possibleLen = possible.length; + + let res = ''; + + for (let i = 0; i < settings.tokenLength; i++) + res += possible.charAt(Math.floor(Math.random() * possibleLen)); + + return res; + }; + + // GET user. + router.post('/user', function (req, res) { + var becomeUsed = req.session.viewedUser && req.user.admin; + + var user = req.user; + + if (becomeUsed) { + user = req.session.viewedUser; + + user.becomeUsed = true; + } + + res.json(user); + }); + + /** + * Register new account. + */ + router.post('/register', function (req, res) { + mongo.Account.count(function (err, cnt) { + if (err) + return res.status(401).send(err.message); + + req.body.admin = cnt == 0; + + var account = new mongo.Account(req.body); + + account.token = _randomString(); + + mongo.Account.register(account, req.body.password, function (err, account) { + if (err) + return res.status(401).send(err.message); + + if (!account) + return res.status(500).send('Failed to create account.'); + + new mongo.Space({name: 'Personal space', owner: account._id}).save(); + + req.logIn(account, {}, function (err) { + if (err) + return res.status(401).send(err.message); + + return res.sendStatus(200); + }); + }); + }); + }); + + /** + * Login in exist account. + */ + router.post('/login', function (req, res, next) { + passport.authenticate('local', function (err, user) { + if (err) + return res.status(401).send(err.message); + + if (!user) + return res.status(401).send('Invalid email or password'); + + req.logIn(user, {}, function (err) { + if (err) + return res.status(401).send(err.message); + + return res.sendStatus(200); + }); + })(req, res, next); + }); + + /** + * Logout. + */ + router.post('/logout', function (req, res) { + req.logout(); + + res.sendStatus(200); + }); + + /** + * Send e-mail to user with reset token. + */ + router.post('/password/forgot', function (req, res) { + var transporter = { + service: settings.smtp.service, + auth: { + user: settings.smtp.email, + pass: settings.smtp.password + } + }; + + if (transporter.service == '' || transporter.auth.user == '' || transporter.auth.pass == '') + return res.status(401).send('Can\'t send e-mail with instructions to reset password. Please ask webmaster to setup SMTP server!'); + + var token = _randomString(); + + mongo.Account.findOne({email: req.body.email}, function (err, user) { + if (!user) + return res.status(401).send('No account with that email address exists!'); + + if (err) + // TODO IGNITE-843 Send email to admin + return res.status(401).send('Failed to reset password!'); + + user.resetPasswordToken = token; + + user.save(function (err) { + if (err) + // TODO IGNITE-843 Send email to admin + return res.status(401).send('Failed to reset password!'); + + var mailer = nodemailer.createTransport(transporter); + + var mailOptions = { + from: settings.smtp.address(settings.smtp.username, settings.smtp.email), + to: settings.smtp.address(user.username, user.email), + subject: 'Password Reset', + text: 'You are receiving this because you (or someone else) have requested the reset of the password for your account.\n\n' + + 'Please click on the following link, or paste this into your browser to complete the process:\n\n' + + 'http://' + req.headers.host + '/password/reset?token=' + token + '\n\n' + + 'If you did not request this, please ignore this email and your password will remain unchanged.\n\n' + + '--------------\n' + + 'Apache Ignite Web Console\n' + }; + + mailer.sendMail(mailOptions, function (err) { + if (err) + return res.status(401).send('Failed to send e-mail with reset link! ' + err); + + return res.status(200).send('An e-mail has been sent with further instructions.'); + }); + }); + }); + }); + + /** + * Change password with given token. + */ + router.post('/password/reset', function (req, res) { + mongo.Account.findOne({resetPasswordToken: req.body.token}, function (err, user) { + if (!user) + return res.status(500).send('Invalid token for password reset!'); + + // TODO IGNITE-843 Send email to admin + if (err) + return res.status(500).send('Failed to reset password!'); + + user.setPassword(req.body.password, function (err, updatedUser) { + if (err) + return res.status(500).send(err.message); + + updatedUser.resetPasswordToken = undefined; + + updatedUser.save(function (err) { + if (err) + return res.status(500).send(err.message); + + var transporter = { + service: settings.smtp.service, + auth: { + user: settings.smtp.email, + pass: settings.smtp.password + } + }; + + var mailer = nodemailer.createTransport(transporter); + + var mailOptions = { + from: settings.smtp.address(settings.smtp.username, settings.smtp.email), + to: settings.smtp.address(user.username, user.email), + subject: 'Your password has been changed', + text: 'Hello,\n\n' + + 'This is a confirmation that the password for your account ' + user.email + ' has just been changed.\n\n' + + 'Now you can login: http://' + req.headers.host + '\n\n' + + '--------------\n' + + 'Apache Ignite Web Console\n' + }; + + mailer.sendMail(mailOptions, function (err) { + if (err) + return res.status(503).send('Password was changed, but failed to send confirmation e-mail!<br />' + err); + + return res.status(200).send(user.email); + }); + }); + }); + }); + }); + + /* GET reset password page. */ + router.post('/validate/token', function (req, res) { + var token = req.body.token; + + var data = {token: token}; + + mongo.Account.findOne({resetPasswordToken: token}, function (err, user) { + if (!user) + data.error = 'Invalid token for password reset!'; + else if (err) + data.error = err; + else + data.email = user.email; + + res.json(data); + }); + }); + + resolve(router); + }); +}; http://git-wip-us.apache.org/repos/asf/ignite/blob/721a1165/modules/control-center-web/src/main/js/serve/routes/routes.js ---------------------------------------------------------------------- diff --git a/modules/control-center-web/src/main/js/serve/routes/routes.js b/modules/control-center-web/src/main/js/serve/routes/routes.js new file mode 100644 index 0000000..268931f --- /dev/null +++ b/modules/control-center-web/src/main/js/serve/routes/routes.js @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +// Fire me up! + +module.exports = { + implements: 'routes', + inject: [ + 'public-routes', + 'admin-routes', + 'profile-routes', + 'clusters-routes', + 'domains-routes', + 'caches-routes', + 'igfs-routes', + 'notebooks-routes', + 'agent-routes', + 'ignite_modules/routes:*' // Loads all routes modules of all plugins + ] +}; + +module.exports.factory = function (publicRoutes, adminRoutes, profileRoutes, + clusterRoutes, domainRoutes, cacheRoutes, igfsRoutes, + notebookRoutes, agentRoutes, + pluginRoutes) { + return { + register: (app) => { + app.all('*', (req, res, next) => { + req.currentUserId = () => { + if (!req.user) + return null; + + if (req.session.viewedUser && req.user.admin) + return req.session.viewedUser._id; + + return req.user._id; + }; + + next(); + }); + + const _mustAuthenticated = (req, res, next) => { + req.isAuthenticated() ? next() : res.redirect('/'); + }; + + const _adminOnly = (req, res, next) => { + req.isAuthenticated() && req.user.admin ? next() : res.sendStatus(403); + }; + + // Registering the standard routes + app.use('/', publicRoutes); + app.use('/admin', _mustAuthenticated, _adminOnly, adminRoutes); + app.use('/profile', _mustAuthenticated, adminRoutes); + + app.all('/configuration/*', _mustAuthenticated); + + app.use('/configuration/clusters', clusterRoutes); + app.use('/configuration/domains', domainRoutes); + app.use('/configuration/caches', cacheRoutes); + app.use('/configuration/igfs', igfsRoutes); + + app.use('/notebooks', _mustAuthenticated, notebookRoutes); + app.use('/agent', _mustAuthenticated, agentRoutes); + + // Registering the routes of all plugin modules + for (var name in pluginRoutes) + if (pluginRoutes.hasOwnProperty(name)) + pluginRoutes[name].register(app, _mustAuthenticated, _adminOnly); + + // Catch 404 and forward to error handler. + app.use(function (req, res, next) { + var err = new Error('Not Found: ' + req.originalUrl); + + err.status = 404; + + next(err); + }); + + // Production error handler: no stacktraces leaked to user. + app.use(function (err, req, res) { + res.status(err.status || 500); + + res.render('error', { + message: err.message, + error: {} + }); + }); + } + }; +}; http://git-wip-us.apache.org/repos/asf/ignite/blob/721a1165/modules/control-center-web/src/main/js/serve/settings.js ---------------------------------------------------------------------- diff --git a/modules/control-center-web/src/main/js/serve/settings.js b/modules/control-center-web/src/main/js/serve/settings.js new file mode 100644 index 0000000..b85b683 --- /dev/null +++ b/modules/control-center-web/src/main/js/serve/settings.js @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Fire me up! + +module.exports = { + implements: 'settings', + inject: ['require(nconf)', 'require(fs)'] +}; + +module.exports.factory = function (nconf, fs) { + nconf.file({'file': './serve/config/default.json'}); + + /** + * Normalize a port into a number, string, or false. + */ + const _normalizePort = function (val) { + var port = parseInt(val, 10); + + // named pipe + if (isNaN(port)) + return val; + + // port number + if (port >= 0) + return port; + + return false; + }; + + return { + agent: { + file: 'ignite-web-agent-1.5.0.final', + port: _normalizePort(nconf.get('agent-server:port')), + SSLOptions: nconf.get('agent-server:ssl') && { + key: fs.readFileSync(nconf.get('agent-server:key')), + cert: fs.readFileSync(nconf.get('agent-server:cert')), + passphrase: nconf.get('agent-server:keyPassphrase') + } + }, + server : { + port: _normalizePort(nconf.get('server:port') || 80), + SSLOptions: nconf.get('server:ssl') && { + enable301Redirects: true, + trustXFPHeader: true, + port: _normalizePort(nconf.get('server:https-port') || 443), + key: fs.readFileSync(nconf.get('server:key')), + cert: fs.readFileSync(nconf.get('server:cert')), + passphrase: nconf.get('server:keyPassphrase') + } + }, + smtp: { + service: nconf.get('smtp:service'), + username: nconf.get('smtp:username'), + email: nconf.get('smtp:email'), + password: nconf.get('smtp:password'), + address: (username, email) => username ? '"' + username + '" <' + email + '>' : email + }, + mongoUrl: nconf.get('mongoDB:url'), + cookieTTL: 3600000 * 24 * 30, + sessionSecret: 'keyboard cat', + tokenLength: 20 + }; +};
