# ignite-843 Rename
Project: http://git-wip-us.apache.org/repos/asf/incubator-ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-ignite/commit/96522874 Tree: http://git-wip-us.apache.org/repos/asf/incubator-ignite/tree/96522874 Diff: http://git-wip-us.apache.org/repos/asf/incubator-ignite/diff/96522874 Branch: refs/heads/ignite-1155_1 Commit: 96522874ebc7809a8f4256ce2e4aa681c2b9241c Parents: cb2ecb5 Author: Andrey <anovi...@gridgain.com> Authored: Wed Jul 29 10:39:34 2015 +0700 Committer: Andrey <anovi...@gridgain.com> Committed: Wed Jul 29 10:39:34 2015 +0700 ---------------------------------------------------------------------- .../control-center-web/licenses/apache-2.0.txt | 202 +++ .../control-center-web/src/main/js/.gitignore | 4 + .../control-center-web/src/main/js/DEVNOTES.txt | 21 + modules/control-center-web/src/main/js/app.js | 156 +++ modules/control-center-web/src/main/js/bin/www | 85 ++ .../src/main/js/config/default.json | 8 + .../src/main/js/controllers/admin-controller.js | 68 + .../js/controllers/cache-viewer-controller.js | 77 ++ .../main/js/controllers/caches-controller.js | 349 +++++ .../main/js/controllers/clusters-controller.js | 308 +++++ .../src/main/js/controllers/common-module.js | 484 +++++++ .../main/js/controllers/metadata-controller.js | 709 ++++++++++ .../src/main/js/controllers/models/caches.json | 943 +++++++++++++ .../main/js/controllers/models/clusters.json | 909 +++++++++++++ .../main/js/controllers/models/metadata.json | 252 ++++ .../src/main/js/controllers/models/sql.json | 5 + .../src/main/js/controllers/models/summary.json | 163 +++ .../main/js/controllers/profile-controller.js | 51 + .../src/main/js/controllers/sql-controller.js | 84 ++ .../main/js/controllers/summary-controller.js | 170 +++ modules/control-center-web/src/main/js/db.js | 370 +++++ .../src/main/js/helpers/configuration-loader.js | 22 + .../src/main/js/helpers/data-structures.js | 84 ++ .../control-center-web/src/main/js/package.json | 49 + .../src/main/js/public/favicon.ico | Bin 0 -> 1150 bytes .../src/main/js/public/images/docker.png | Bin 0 -> 994 bytes .../src/main/js/public/images/java.png | Bin 0 -> 170 bytes .../src/main/js/public/images/logo.png | Bin 0 -> 8148 bytes .../src/main/js/public/images/xml.png | Bin 0 -> 232 bytes .../src/main/js/public/stylesheets/style.scss | 1270 ++++++++++++++++++ .../src/main/js/routes/admin.js | 79 ++ .../src/main/js/routes/caches.js | 105 ++ .../src/main/js/routes/clusters.js | 104 ++ .../src/main/js/routes/generator/common.js | 312 +++++ .../src/main/js/routes/generator/docker.js | 58 + .../src/main/js/routes/generator/java.js | 788 +++++++++++ .../src/main/js/routes/generator/xml.js | 736 ++++++++++ .../src/main/js/routes/metadata.js | 95 ++ .../src/main/js/routes/profile.js | 97 ++ .../src/main/js/routes/public.js | 123 ++ .../src/main/js/routes/sql.js | 24 + .../src/main/js/routes/summary.js | 108 ++ .../src/main/js/views/configuration/caches.jade | 74 + .../main/js/views/configuration/clusters.jade | 77 ++ .../main/js/views/configuration/metadata.jade | 121 ++ .../main/js/views/configuration/sidebar.jade | 39 + .../main/js/views/configuration/summary.jade | 113 ++ .../src/main/js/views/error.jade | 22 + .../src/main/js/views/includes/controls.jade | 350 +++++ .../src/main/js/views/includes/footer.jade | 22 + .../src/main/js/views/includes/header.jade | 39 + .../src/main/js/views/index.jade | 30 + .../src/main/js/views/login.jade | 55 + .../src/main/js/views/settings/admin.jade | 58 + .../src/main/js/views/settings/profile.jade | 58 + .../src/main/js/views/sql/sql.jade | 85 ++ .../src/main/js/views/templates/confirm.jade | 27 + .../src/main/js/views/templates/copy.jade | 31 + .../src/main/js/views/templates/layout.jade | 61 + .../src/main/js/views/templates/select.jade | 26 + .../src/main/js/views/templates/tab.jade | 26 + .../web-control-center/licenses/apache-2.0.txt | 202 --- .../web-control-center/src/main/js/.gitignore | 4 - .../web-control-center/src/main/js/DEVNOTES.txt | 21 - modules/web-control-center/src/main/js/app.js | 156 --- modules/web-control-center/src/main/js/bin/www | 85 -- .../src/main/js/config/default.json | 8 - .../src/main/js/controllers/admin-controller.js | 68 - .../js/controllers/cache-viewer-controller.js | 77 -- .../main/js/controllers/caches-controller.js | 349 ----- .../main/js/controllers/clusters-controller.js | 308 ----- .../src/main/js/controllers/common-module.js | 484 ------- .../main/js/controllers/metadata-controller.js | 709 ---------- .../src/main/js/controllers/models/caches.json | 943 ------------- .../main/js/controllers/models/clusters.json | 909 ------------- .../main/js/controllers/models/metadata.json | 252 ---- .../src/main/js/controllers/models/sql.json | 5 - .../src/main/js/controllers/models/summary.json | 163 --- .../main/js/controllers/profile-controller.js | 51 - .../src/main/js/controllers/sql-controller.js | 84 -- .../main/js/controllers/summary-controller.js | 170 --- modules/web-control-center/src/main/js/db.js | 370 ----- .../src/main/js/helpers/configuration-loader.js | 22 - .../src/main/js/helpers/data-structures.js | 84 -- .../web-control-center/src/main/js/package.json | 49 - .../src/main/js/public/favicon.ico | Bin 1150 -> 0 bytes .../src/main/js/public/images/docker.png | Bin 994 -> 0 bytes .../src/main/js/public/images/java.png | Bin 170 -> 0 bytes .../src/main/js/public/images/logo.png | Bin 8148 -> 0 bytes .../src/main/js/public/images/xml.png | Bin 232 -> 0 bytes .../src/main/js/public/stylesheets/style.scss | 1270 ------------------ .../src/main/js/routes/admin.js | 79 -- .../src/main/js/routes/caches.js | 105 -- .../src/main/js/routes/clusters.js | 104 -- .../src/main/js/routes/generator/common.js | 312 ----- .../src/main/js/routes/generator/docker.js | 58 - .../src/main/js/routes/generator/java.js | 788 ----------- .../src/main/js/routes/generator/xml.js | 736 ---------- .../src/main/js/routes/metadata.js | 95 -- .../src/main/js/routes/profile.js | 97 -- .../src/main/js/routes/public.js | 123 -- .../src/main/js/routes/sql.js | 24 - .../src/main/js/routes/summary.js | 108 -- .../src/main/js/views/configuration/caches.jade | 74 - .../main/js/views/configuration/clusters.jade | 77 -- .../main/js/views/configuration/metadata.jade | 121 -- .../main/js/views/configuration/sidebar.jade | 39 - .../main/js/views/configuration/summary.jade | 113 -- .../src/main/js/views/error.jade | 22 - .../src/main/js/views/includes/controls.jade | 350 ----- .../src/main/js/views/includes/footer.jade | 22 - .../src/main/js/views/includes/header.jade | 39 - .../src/main/js/views/index.jade | 30 - .../src/main/js/views/login.jade | 55 - .../src/main/js/views/settings/admin.jade | 58 - .../src/main/js/views/settings/profile.jade | 58 - .../src/main/js/views/sql/sql.jade | 85 -- .../src/main/js/views/templates/confirm.jade | 27 - .../src/main/js/views/templates/copy.jade | 31 - .../src/main/js/views/templates/layout.jade | 61 - .../src/main/js/views/templates/select.jade | 26 - .../src/main/js/views/templates/tab.jade | 26 - 122 files changed, 10786 insertions(+), 10786 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/96522874/modules/control-center-web/licenses/apache-2.0.txt ---------------------------------------------------------------------- diff --git a/modules/control-center-web/licenses/apache-2.0.txt b/modules/control-center-web/licenses/apache-2.0.txt new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/modules/control-center-web/licenses/apache-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/96522874/modules/control-center-web/src/main/js/.gitignore ---------------------------------------------------------------------- diff --git a/modules/control-center-web/src/main/js/.gitignore b/modules/control-center-web/src/main/js/.gitignore new file mode 100644 index 0000000..65f2596 --- /dev/null +++ b/modules/control-center-web/src/main/js/.gitignore @@ -0,0 +1,4 @@ +node_modules +*.idea +*.log +*.css \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/96522874/modules/control-center-web/src/main/js/DEVNOTES.txt ---------------------------------------------------------------------- diff --git a/modules/control-center-web/src/main/js/DEVNOTES.txt b/modules/control-center-web/src/main/js/DEVNOTES.txt new file mode 100644 index 0000000..e8c558c --- /dev/null +++ b/modules/control-center-web/src/main/js/DEVNOTES.txt @@ -0,0 +1,21 @@ +Ignite Web Control Center Instructions +====================================== + +How to deploy: + +1. Install locally NodeJS using installer from site https://nodejs.org for your OS. +2. Install locally MongoDB folow instructions from site http://docs.mongodb.org/manual/installation +3. Checkout ignite-843 branch. +4. Change directory '$IGNITE_HOME/modules/web-control-center/src/main/js'. +5. Run "npm install" in terminal for download all dependencies. + +Steps 1 - 5 should be executed once. + +How to run: + +1. Run MongoDB. + 1.1 In terminal change dir to $MONGO_ISNTALL_DIR/server/3.0/bin. + 1.2 Run "mongod". +2. In new terminal change directory '$IGNITE_HOME/modules/web-control-center/src/main/js'. +3. Start application by executing "npm start". +4. In browser open: http://localhost:3000 \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/96522874/modules/control-center-web/src/main/js/app.js ---------------------------------------------------------------------- diff --git a/modules/control-center-web/src/main/js/app.js b/modules/control-center-web/src/main/js/app.js new file mode 100644 index 0000000..a67afc8 --- /dev/null +++ b/modules/control-center-web/src/main/js/app.js @@ -0,0 +1,156 @@ +/* + * 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. + */ + +var flash = require('connect-flash'); +var express = require('express'); +var path = require('path'); +var favicon = require('serve-favicon'); +var logger = require('morgan'); +var cookieParser = require('cookie-parser'); +var bodyParser = require('body-parser'); +var session = require('express-session'); +var mongoStore = require('connect-mongo')(session); + +var publicRoutes = require('./routes/public'); +var clustersRouter = require('./routes/clusters'); +var cachesRouter = require('./routes/caches'); +var metadataRouter = require('./routes/metadata'); +var summary = require('./routes/summary'); +var adminRouter = require('./routes/admin'); +var profileRouter = require('./routes/profile'); +var sqlRouter = require('./routes/sql'); + +var passport = require('passport'); + +var db = require('./db'); + +var app = express(); + +// Views engine setup. +app.set('views', path.join(__dirname, 'views')); +app.set('view engine', 'jade'); + +// Site favicon. +app.use(favicon(__dirname + '/public/favicon.ico')); + +app.use(logger('dev')); + +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({extended: false})); + +app.use(require('node-sass-middleware')({ + /* Options */ + src: path.join(__dirname, 'public'), + dest: path.join(__dirname, 'public'), + debug: true, + outputStyle: 'nested' +})); + +app.use(express.static(path.join(__dirname, 'public'))); +app.use(express.static(path.join(__dirname, 'controllers'))); +app.use(express.static(path.join(__dirname, 'helpers'))); + +app.use(cookieParser('keyboard cat')); + +app.use(session({ + secret: 'keyboard cat', + resave: false, + saveUninitialized: true, + store: new mongoStore({ + mongooseConnection: db.mongoose.connection + }) +})); + +app.use(flash()); + +app.use(passport.initialize()); +app.use(passport.session()); + +passport.serializeUser(db.Account.serializeUser()); +passport.deserializeUser(db.Account.deserializeUser()); + +passport.use(db.Account.createStrategy()); + +var mustAuthenticated = function (req, res, next) { + req.isAuthenticated() ? next() : res.redirect('/'); +}; + +var adminOnly = function(req, res, next) { + req.isAuthenticated() && req.user.admin ? next() : res.sendStatus(403); +}; + +app.all('/configuration/*', mustAuthenticated); + +app.all('*', function(req, res, next) { + var becomeUsed = req.session.viewedUser && req.user.admin; + + res.locals.user = becomeUsed ? req.session.viewedUser : req.user; + res.locals.becomeUsed = becomeUsed; + + req.currentUserId = function() { + if (!req.user) + return null; + + if (req.session.viewedUser && req.user.admin) + return req.session.viewedUser._id; + + return req.user._id; + }; + + next(); +}); + +app.use('/', publicRoutes); +app.use('/admin', mustAuthenticated, adminOnly, adminRouter); +app.use('/profile', mustAuthenticated, profileRouter); + +app.use('/configuration/clusters', clustersRouter); +app.use('/configuration/caches', cachesRouter); +app.use('/configuration/metadata', metadataRouter); +app.use('/configuration/summary', summary); +app.use('/sql', sqlRouter); + +// 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); +}); + +// Error handlers. + +// Development error handler: will print stacktrace. +if (app.get('env') === 'development') { + app.use(function (err, req, res) { + res.status(err.status || 500); + res.render('error', { + message: err.message, + error: 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: {} + }); +}); + +module.exports = app; http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/96522874/modules/control-center-web/src/main/js/bin/www ---------------------------------------------------------------------- diff --git a/modules/control-center-web/src/main/js/bin/www b/modules/control-center-web/src/main/js/bin/www new file mode 100644 index 0000000..4cf0583 --- /dev/null +++ b/modules/control-center-web/src/main/js/bin/www @@ -0,0 +1,85 @@ +#!/usr/bin/env node + +/** + * Module dependencies. + */ +var app = require('../app'); +var config = require('../helpers/configuration-loader.js'); +var debug = require('debug')('ignite-web-control-center:server'); +var http = require('http'); + +/** + * Get port from environment and store in Express. + */ +var port = normalizePort(process.env.PORT || config.get('express:port')); +app.set('port', port); + +/** + * Create HTTP server. + */ +var server = http.createServer(app); + +/** + * Listen on provided port, on all network interfaces. + */ +server.listen(port); +server.on('error', onError); +server.on('listening', onListening); + +/** + * Normalize a port into a number, string, or false. + */ +function normalizePort(val) { + var port = parseInt(val, 10); + + if (isNaN(port)) { + // named pipe + return val; + } + + if (port >= 0) { + // port number + return port; + } + + return false; +} + +/** + * Event listener for HTTP server "error" event. + */ +function onError(error) { + if (error.syscall !== 'listen') { + throw error; + } + + var bind = typeof port === 'string' + ? 'Pipe ' + port + : 'Port ' + port; + + // handle specific listen errors with friendly messages + switch (error.code) { + case 'EACCES': + console.error(bind + ' requires elevated privileges'); + process.exit(1); + break; + case 'EADDRINUSE': + console.error(bind + ' is already in use'); + process.exit(1); + break; + default: + throw error; + } +} + +/** + * Event listener for HTTP server "listening" event. + */ +function onListening() { + var addr = server.address(); + var bind = typeof addr === 'string' + ? 'pipe ' + addr + : 'port ' + addr.port; + + debug('Listening on ' + bind); +} http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/96522874/modules/control-center-web/src/main/js/config/default.json ---------------------------------------------------------------------- diff --git a/modules/control-center-web/src/main/js/config/default.json b/modules/control-center-web/src/main/js/config/default.json new file mode 100644 index 0000000..72dbd4e --- /dev/null +++ b/modules/control-center-web/src/main/js/config/default.json @@ -0,0 +1,8 @@ +{ + "express": { + "port": 3000 + }, + "mongoDB": { + "url": "mongodb://localhost/web-control-center" + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/96522874/modules/control-center-web/src/main/js/controllers/admin-controller.js ---------------------------------------------------------------------- diff --git a/modules/control-center-web/src/main/js/controllers/admin-controller.js b/modules/control-center-web/src/main/js/controllers/admin-controller.js new file mode 100644 index 0000000..09490fe --- /dev/null +++ b/modules/control-center-web/src/main/js/controllers/admin-controller.js @@ -0,0 +1,68 @@ +/* + * 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. + */ + +controlCenterModule.controller('adminController', ['$scope', '$http', '$common', '$confirm', function ($scope, $http, $common, $confirm) { + $scope.users = null; + + function reload() { + $http.post('admin/list') + .success(function (data) { + $scope.users = data; + }) + .error(function (errMsg) { + $common.showError($common.errorMessage(errMsg)); + }); + } + + reload(); + + $scope.removeUser = function (user) { + $confirm.show('Are you sure you want to remove user: "' + user.username + '"?').then(function () { + $http.post('admin/remove', {userId: user._id}).success( + function () { + var i = _.findIndex($scope.users, function (u) { + return u._id == user._id; + }); + + if (i >= 0) + $scope.users.splice(i, 1); + + $common.showInfo('User has been removed: "' + user.username + '"'); + }).error(function (errMsg) { + $common.showError('Failed to remove user: "' + $common.errorMessage(errMsg) + '"'); + }); + }); + }; + + $scope.toggleAdmin = function (user) { + if (user.adminChanging) + return; + + user.adminChanging = true; + + $http.post('admin/save', {userId: user._id, adminFlag: user.admin}).success( + function () { + $common.showInfo('Admin right was successfully toggled for user: "' + user.username + '"'); + + user.adminChanging = false; + }).error(function (errMsg) { + $common.showError('Failed to toggle admin right for user: "' + $common.errorMessage(errMsg) + '"'); + + user.adminChanging = false; + }); + } +}]); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/96522874/modules/control-center-web/src/main/js/controllers/cache-viewer-controller.js ---------------------------------------------------------------------- diff --git a/modules/control-center-web/src/main/js/controllers/cache-viewer-controller.js b/modules/control-center-web/src/main/js/controllers/cache-viewer-controller.js new file mode 100644 index 0000000..6e0c130 --- /dev/null +++ b/modules/control-center-web/src/main/js/controllers/cache-viewer-controller.js @@ -0,0 +1,77 @@ +/* + * 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. + */ + +var demoResults = [ + { + id: 256, + s: 'com.foo.User@3213', + fields: { + id: 256, + firstName: 'Ivan', + lastName: 'Ivanov', + old: 23 + } + }, + + { + id: 384, + s: 'com.foo.User@23214', + fields: { + id: 384, + firstName: 'Sergey', + lastName: 'Petrov', + old: 28 + } + }, + + { + id: 923, + s: 'com.foo.User@93494', + fields: { + id: 923, + firstName: 'Andrey', + lastName: 'Sidorov', + old: 28 + } + } +]; + +var demoCaches = ['Users', 'Organizations', 'Cities']; + +controlCenterModule.controller('cacheViewerController', ['$scope', '$http', '$common', function ($scope, $http, $common) { + $scope.results = demoResults; + + $scope.caches = demoCaches; + + $scope.defCache = $scope.caches.length > 0 ? $scope.caches[0] : null; + + var sqlEditor = ace.edit('querySql'); + + sqlEditor.setOptions({ + highlightActiveLine: false, + showPrintMargin: false, + showGutter: true, + theme: "ace/theme/chrome", + mode: "ace/mode/sql", + fontSize: 14 + }); + + sqlEditor.setValue("select u.id from User u where u.name like 'aaaa';"); + + sqlEditor.selection.clearSelection() + +}]); http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/96522874/modules/control-center-web/src/main/js/controllers/caches-controller.js ---------------------------------------------------------------------- diff --git a/modules/control-center-web/src/main/js/controllers/caches-controller.js b/modules/control-center-web/src/main/js/controllers/caches-controller.js new file mode 100644 index 0000000..e995233 --- /dev/null +++ b/modules/control-center-web/src/main/js/controllers/caches-controller.js @@ -0,0 +1,349 @@ +/* + * 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. + */ + +controlCenterModule.controller('cachesController', ['$scope', '$http', '$common', '$confirm', '$copy', '$table', function ($scope, $http, $common, $confirm, $copy, $table) { + $scope.joinTip = $common.joinTip; + $scope.getModel = $common.getModel; + $scope.javaBuildInTypes = $common.javaBuildInTypes; + + $scope.tableReset = $table.tableReset; + $scope.tableNewItem = $table.tableNewItem; + $scope.tableNewItemActive = $table.tableNewItemActive; + $scope.tableEditing = $table.tableEditing; + $scope.tableStartEdit = $table.tableStartEdit; + $scope.tableRemove = $table.tableRemove; + + $scope.tableSimpleSave = $table.tableSimpleSave; + $scope.tableSimpleSaveVisible = $table.tableSimpleSaveVisible; + $scope.tableSimpleUp = $table.tableSimpleUp; + $scope.tableSimpleDown = $table.tableSimpleDown; + $scope.tableSimpleDownVisible = $table.tableSimpleDownVisible; + + $scope.tablePairSave = $table.tablePairSave; + $scope.tablePairSaveVisible = $table.tablePairSaveVisible; + + $scope.atomicities = [ + {value: 'ATOMIC', label: 'ATOMIC'}, + {value: 'TRANSACTIONAL', label: 'TRANSACTIONAL'} + ]; + + $scope.modes = [ + {value: 'PARTITIONED', label: 'PARTITIONED'}, + {value: 'REPLICATED', label: 'REPLICATED'}, + {value: 'LOCAL', label: 'LOCAL'} + ]; + + $scope.atomicWriteOrderModes = [ + {value: 'CLOCK', label: 'CLOCK'}, + {value: 'PRIMARY', label: 'PRIMARY'} + ]; + + $scope.memoryModes = [ + {value: 'ONHEAP_TIERED', label: 'ONHEAP_TIERED'}, + {value: 'OFFHEAP_TIERED', label: 'OFFHEAP_TIERED'}, + {value: 'OFFHEAP_VALUES', label: 'OFFHEAP_VALUES'} + ]; + + $scope.evictionPolicies = [ + {value: 'LRU', label: 'LRU'}, + {value: 'RND', label: 'Random'}, + {value: 'FIFO', label: 'FIFO'}, + {value: 'SORTED', label: 'Sorted'}, + {value: undefined, label: 'Not set'} + ]; + + $scope.rebalanceModes = [ + {value: 'SYNC', label: 'SYNC'}, + {value: 'ASYNC', label: 'ASYNC'}, + {value: 'NONE', label: 'NONE'} + ]; + + $scope.cacheStoreFactories = [ + {value: 'CacheJdbcPojoStoreFactory', label: 'JDBC POJO store factory'}, + {value: 'CacheJdbcBlobStoreFactory', label: 'JDBC BLOB store factory'}, + {value: 'CacheHibernateBlobStoreFactory', label: 'Hibernate BLOB store factory'}, + {value: undefined, label: 'Not set'} + ]; + + $scope.cacheStoreJdbcDialects = [ + {value: 'Oracle', label: 'Oracle'}, + {value: 'DB2', label: 'IBM DB2'}, + {value: 'SQLServer', label: 'Microsoft SQL Server'}, + {value: 'MySQL', label: 'My SQL'}, + {value: 'PostgreSQL', label: 'Postgre SQL'}, + {value: 'H2', label: 'H2 database'} + ]; + + $scope.general = []; + $scope.advanced = []; + + $http.get('/models/caches.json') + .success(function (data) { + $scope.screenTip = data.screenTip; + $scope.general = data.general; + $scope.advanced = data.advanced; + }) + .error(function (errMsg) { + $common.showError(errMsg); + }); + + $scope.caches = []; + $scope.queryMetadata = []; + $scope.storeMetadata = []; + + $scope.required = function (field) { + var model = $common.isDefined(field.path) ? field.path + '.' + field.model : field.model; + + var backupItem = $scope.backupItem; + + var memoryMode = backupItem.memoryMode; + + var onHeapTired = memoryMode == 'ONHEAP_TIERED'; + var offHeapTired = memoryMode == 'OFFHEAP_TIERED'; + + var offHeapMaxMemory = backupItem.offHeapMaxMemory; + + if (model == 'offHeapMaxMemory' && offHeapTired) + return true; + + if (model == 'evictionPolicy.kind' && onHeapTired) + return backupItem.swapEnabled || ($common.isDefined(offHeapMaxMemory) && offHeapMaxMemory >= 0); + + return false; + }; + + $scope.tableSimpleValid = function (item, field, fx, index) { + var model = item[field.model]; + + if ($common.isDefined(model)) { + var idx = _.indexOf(model, fx); + + // Found itself. + if (index >= 0 && index == idx) + return true; + + // Found duplicate. + if (idx >= 0) { + $common.showError('SQL function such class name already exists!'); + + return false; + } + } + + return true; + }; + + $scope.tablePairValid = function (item, field, keyCls, valCls, index) { + var model = item[field.model]; + + if ($common.isDefined(model)) { + var idx = _.findIndex(model, function (pair) { + return pair.keyClass == keyCls + }); + + // Found itself. + if (index >= 0 && index == idx) + return true; + + // Found duplicate. + if (idx >= 0) { + $common.showError('Indexed type with such key class already exists!'); + + return false; + } + } + + return true; + }; + + // When landing on the page, get caches and show them. + $http.post('caches/list') + .success(function (data) { + $scope.spaces = data.spaces; + $scope.caches = data.caches; + + _.forEach(data.metadatas, function (meta) { + var kind = meta.kind; + + if (kind == 'query' || kind == 'both') + $scope.queryMetadata.push(meta); + + if (kind == 'store' || kind == 'both') + $scope.storeMetadata.push(meta); + }); + + var restoredItem = angular.fromJson(sessionStorage.cacheBackupItem); + + if (restoredItem) { + if (restoredItem._id) { + var idx = _.findIndex($scope.caches, function (cache) { + return cache._id == restoredItem._id; + }); + + if (idx >= 0) { + $scope.selectedItem = $scope.caches[idx]; + $scope.backupItem = restoredItem; + } + else + sessionStorage.removeItem('cacheBackupItem'); + } + else + $scope.backupItem = restoredItem; + } + else if ($scope.caches.length > 0) + $scope.selectItem($scope.caches[0]); + + $scope.$watch('backupItem', function (val) { + if (val) + sessionStorage.cacheBackupItem = angular.toJson(val); + }, true); + }) + .error(function (errMsg) { + $common.showError(errMsg); + }); + + $scope.selectItem = function (item) { + $table.tableReset(); + + $scope.selectedItem = item; + $scope.backupItem = angular.copy(item); + }; + + // Add new cache. + $scope.createItem = function () { + $table.tableReset(); + + $scope.backupItem = {mode: 'PARTITIONED', atomicityMode: 'ATOMIC', readFromBackup: true, copyOnRead: true}; + $scope.backupItem.queryMetadata = []; + $scope.backupItem.spaceMetadata = []; + $scope.backupItem.space = $scope.spaces[0]._id; + }; + + // Check cache logical consistency. + function validate(item) { + var cacheStoreFactorySelected = item.cacheStoreFactory && item.cacheStoreFactory.kind; + + if (cacheStoreFactorySelected && !(item.readThrough || item.writeThrough)) { + $common.showError('Store is configured but read/write through are not enabled!'); + + return false; + } + + if ((item.readThrough || item.writeThrough) && !cacheStoreFactorySelected) { + $common.showError('Read / write through are enabled but store is not configured!'); + + return false; + } + + if (item.writeBehindEnabled && !cacheStoreFactorySelected) { + $common.showError('Write behind enabled but store is not configured!'); + + return false; + } + + return true; + } + + // Save cache into database. + function save(item) { + $http.post('caches/save', item) + .success(function (_id) { + var idx = _.findIndex($scope.caches, function (cache) { + return cache._id == _id; + }); + + if (idx >= 0) + angular.extend($scope.caches[idx], item); + else { + item._id = _id; + + $scope.caches.push(item); + } + + $scope.selectItem(item); + + $common.showInfo('Cache "' + item.name + '" saved.'); + }) + .error(function (errMsg) { + $common.showError(errMsg); + }); + } + + // Save cache. + $scope.saveItem = function () { + $table.tableReset(); + + var item = $scope.backupItem; + + if (validate(item)) + save(item); + }; + + // Save cache with new name. + $scope.saveItemAs = function () { + $table.tableReset(); + + if (validate($scope.backupItem)) + $copy.show($scope.backupItem.name).then(function (newName) { + var item = angular.copy($scope.backupItem); + + item._id = undefined; + item.name = newName; + + save(item); + }); + }; + + // Remove cache from db. + $scope.removeItem = function () { + $table.tableReset(); + + var selectedItem = $scope.selectedItem; + + $confirm.show('Are you sure you want to remove cache: "' + selectedItem.name + '"?').then( + function () { + var _id = selectedItem._id; + + $http.post('caches/remove', {_id: _id}) + .success(function () { + $common.showInfo('Cache has been removed: ' + selectedItem.name); + + var caches = $scope.caches; + + var idx = _.findIndex(caches, function (cache) { + return cache._id == _id; + }); + + if (idx >= 0) { + caches.splice(idx, 1); + + if (caches.length > 0) + $scope.selectItem(caches[0]); + else { + $scope.selectedItem = undefined; + $scope.backupItem = undefined; + } + } + }) + .error(function (errMsg) { + $common.showError(errMsg); + }); + } + ); + }; + }] +); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/96522874/modules/control-center-web/src/main/js/controllers/clusters-controller.js ---------------------------------------------------------------------- diff --git a/modules/control-center-web/src/main/js/controllers/clusters-controller.js b/modules/control-center-web/src/main/js/controllers/clusters-controller.js new file mode 100644 index 0000000..0b18868 --- /dev/null +++ b/modules/control-center-web/src/main/js/controllers/clusters-controller.js @@ -0,0 +1,308 @@ +/* + * 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. + */ + +controlCenterModule.controller('clustersController', ['$scope', '$http', '$common', '$confirm', '$copy', '$table', function ($scope, $http, $common, $confirm, $copy, $table) { + $scope.joinTip = $common.joinTip; + $scope.getModel = $common.getModel; + + $scope.tableReset = $table.tableReset; + $scope.tableNewItem = $table.tableNewItem; + $scope.tableNewItemActive = $table.tableNewItemActive; + $scope.tableEditing = $table.tableEditing; + $scope.tableStartEdit = $table.tableStartEdit; + $scope.tableRemove = $table.tableRemove; + + $scope.tableSimpleSave = $table.tableSimpleSave; + $scope.tableSimpleSaveVisible = $table.tableSimpleSaveVisible; + $scope.tableSimpleUp = $table.tableSimpleUp; + $scope.tableSimpleDown = $table.tableSimpleDown; + $scope.tableSimpleDownVisible = $table.tableSimpleDownVisible; + + $scope.templates = [ + {value: {discovery: {kind: 'Multicast', Vm: {addresses: ['127.0.0.1:47500..47510']}, Multicast: {}}},label: 'multicast'}, + {value: {discovery: {kind: 'Vm', Vm: {addresses: ['127.0.0.1:47500..47510']}}}, label: 'local'} + ]; + + $scope.discoveries = [ + {value: 'Vm', label: 'static IPs'}, + {value: 'Multicast', label: 'multicast'}, + {value: 'S3', label: 'AWS S3'}, + {value: 'Cloud', label: 'apache jclouds'}, + {value: 'GoogleStorage', label: 'google cloud storage'}, + {value: 'Jdbc', label: 'JDBC'}, + {value: 'SharedFs', label: 'shared filesystem'} + ]; + + $scope.swapSpaceSpis = [ + {value: 'FileSwapSpaceSpi', label: 'File-based swap'}, + {value: undefined, label: 'Not set'} + ]; + + $scope.events = []; + + for (var eventGroupName in eventGroups) { + if (eventGroups.hasOwnProperty(eventGroupName)) { + $scope.events.push({value: eventGroupName, label: eventGroupName}); + } + } + + $scope.cacheModes = [ + {value: 'LOCAL', label: 'LOCAL'}, + {value: 'REPLICATED', label: 'REPLICATED'}, + {value: 'PARTITIONED', label: 'PARTITIONED'} + ]; + + $scope.deploymentModes = [ + {value: 'PRIVATE', label: 'PRIVATE'}, + {value: 'ISOLATED', label: 'ISOLATED'}, + {value: 'SHARED', label: 'SHARED'}, + {value: 'CONTINUOUS', label: 'CONTINUOUS'} + ]; + + $scope.transactionConcurrency = [ + {value: 'OPTIMISTIC', label: 'OPTIMISTIC'}, + {value: 'PESSIMISTIC', label: 'PESSIMISTIC'} + ]; + + $scope.transactionIsolation = [ + {value: 'READ_COMMITTED', label: 'READ_COMMITTED'}, + {value: 'REPEATABLE_READ', label: 'REPEATABLE_READ'}, + {value: 'SERIALIZABLE', label: 'SERIALIZABLE'} + ]; + + $scope.segmentationPolicy = [ + {value: 'RESTART_JVM', label: 'RESTART_JVM'}, + {value: 'STOP', label: 'STOP'}, + {value: 'NOOP', label: 'NOOP'} + ]; + + $scope.marshallers = [ + {value: 'OptimizedMarshaller', label: 'OptimizedMarshaller'}, + {value: 'JdkMarshaller', label: 'JdkMarshaller'} + ]; + + $scope.tableSimpleValid = function (item, field, val, index) { + var model = $common.getModel(item, field)[field.model]; + + if ($common.isDefined(model)) { + var idx = _.indexOf(model, val); + + // Found itself. + if (index >= 0 && index == idx) + return true; + + // Found duplicate. + if (idx >= 0) { + var msg = 'Such IP address already exists!'; + + if (field.model == 'regions') + msg = 'Such region already exists!'; + if (field.model == 'zones') + msg = 'Such zone already exists!'; + + $common.showError(msg); + + return false; + } + } + + return true; + }; + + $scope.clusters = []; + + $http.get('/models/clusters.json') + .success(function (data) { + $scope.screenTip = data.screenTip; + $scope.templateTip = data.templateTip; + + $scope.general = data.general; + $scope.advanced = data.advanced; + }) + .error(function (errMsg) { + $common.showError(errMsg); + }); + + // When landing on the page, get clusters and show them. + $http.post('clusters/list') + .success(function (data) { + $scope.caches = data.caches; + $scope.spaces = data.spaces; + $scope.clusters = data.clusters; + + var restoredItem = angular.fromJson(sessionStorage.clusterBackupItem); + + if (restoredItem) { + if (restoredItem._id) { + var idx = _.findIndex($scope.clusters, function (cluster) { + return cluster._id == restoredItem._id; + }); + + if (idx >= 0) { + $scope.selectedItem = $scope.clusters[idx]; + $scope.backupItem = restoredItem; + } + else + sessionStorage.removeItem('clusterBackupItem'); + } + else + $scope.backupItem = restoredItem; + } + else if ($scope.clusters.length > 0) + $scope.selectItem($scope.clusters[0]); + + $scope.$watch('backupItem', function (val) { + if (val) + sessionStorage.clusterBackupItem = angular.toJson(val); + }, true); + }) + .error(function (errMsg) { + $common.showError(errMsg); + }); + + $scope.selectItem = function (item) { + $table.tableReset(); + + $scope.selectedItem = item; + $scope.backupItem = angular.copy(item); + }; + + // Add new cluster. + $scope.createItem = function () { + $table.tableReset(); + + $scope.backupItem = angular.copy($scope.create.template); + $scope.backupItem.caches = []; + $scope.backupItem.space = $scope.spaces[0]._id; + }; + + $scope.indexOfCache = function (cacheId) { + return _.findIndex($scope.caches, function (cache) { + return cache.value == cacheId; + }); + }; + + // Check cluster logical consistency. + function validate(item) { + if (!item.swapSpaceSpi || !item.swapSpaceSpi.kind && item.caches) { + for (var i = 0; i < item.caches.length; i++) { + var idx = $scope.indexOfCache(item.caches[i]); + + if (idx >= 0) { + var cache = $scope.caches[idx]; + + if (cache.swapEnabled) { + $common.showError('Swap space SPI is not configured, but cache "' + cache.label + '" configured to use swap!'); + + return false; + } + } + } + } + + return true; + } + + // Save cluster in database. + function save(item) { + $http.post('clusters/save', item) + .success(function (_id) { + var idx = _.findIndex($scope.clusters, function (cluster) { + return cluster._id == _id; + }); + + if (idx >= 0) + angular.extend($scope.clusters[idx], item); + else { + item._id = _id; + + $scope.clusters.push(item); + } + + $scope.selectItem(item); + + $common.showInfo('Cluster "' + item.name + '" saved.'); + }) + .error(function (errMsg) { + $common.showError(errMsg); + }); + } + + // Save cluster. + $scope.saveItem = function () { + $table.tableReset(); + + var item = $scope.backupItem; + + if (validate(item)) + save(item); + }; + + // Save cluster with new name. + $scope.saveItemAs = function () { + $table.tableReset(); + + if (validate($scope.backupItem)) + $copy.show($scope.backupItem.name).then(function (newName) { + var item = angular.copy($scope.backupItem); + + item._id = undefined; + item.name = newName; + + save(item); + }); + }; + + // Remove cluster from db. + $scope.removeItem = function () { + $table.tableReset(); + + var selectedItem = $scope.selectedItem; + + $confirm.show('Are you sure you want to remove cluster: "' + selectedItem.name + '"?').then( + function () { + var _id = selectedItem._id; + + $http.post('clusters/remove', {_id: _id}) + .success(function () { + $common.showInfo('Cluster has been removed: ' + selectedItem.name); + + var clusters = $scope.clusters; + + var idx = _.findIndex(clusters, function (cluster) { + return cluster._id == _id; + }); + + if (idx >= 0) { + clusters.splice(idx, 1); + + if (clusters.length > 0) + $scope.selectItem(clusters[0]); + else { + $scope.selectedItem = undefined; + $scope.backupItem = undefined; + } + } + }) + .error(function (errMsg) { + $common.showError(errMsg); + }); + } + ); + }; + }] +); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/96522874/modules/control-center-web/src/main/js/controllers/common-module.js ---------------------------------------------------------------------- diff --git a/modules/control-center-web/src/main/js/controllers/common-module.js b/modules/control-center-web/src/main/js/controllers/common-module.js new file mode 100644 index 0000000..df2ff19 --- /dev/null +++ b/modules/control-center-web/src/main/js/controllers/common-module.js @@ -0,0 +1,484 @@ +/* + * 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. + */ + +var controlCenterModule = angular.module('ignite-web-control-center', ['smart-table', 'mgcrea.ngStrap', 'ui.ace', 'ngSanitize']); + +// Modal popup configuration. +controlCenterModule.config(function ($modalProvider) { + angular.extend($modalProvider.defaults, { + html: true + }); +}); + +// Tooltips configuration. +controlCenterModule.config(function ($tooltipProvider) { + angular.extend($tooltipProvider.defaults, { + container: 'body', + placement: 'right', + html: 'true', + trigger: 'click hover' + }); +}); + +// Comboboxes configuration. +controlCenterModule.config(function ($selectProvider) { + angular.extend($selectProvider.defaults, { + maxLength: '1', + allText: 'Select All', + noneText: 'Clear All', + templateUrl: '/select', + iconCheckmark: 'fa fa-check', + caretHtml: '<span class="caret"></span>' + }); +}); + +// Alerts configuration. +controlCenterModule.config(function ($alertProvider) { + angular.extend($alertProvider.defaults, { + container: 'body', + placement: 'top-right', + duration: '5', + type: 'danger' + }); +}); + +// Common functions to be used in controllers. +controlCenterModule.service('$common', ['$alert', function ($alert) { + var msgModal = undefined; + + function errorMessage(errMsg) { + return errMsg ? errMsg : 'Internal server error.'; + } + + function isDefined(v) { + return !(v === undefined || v === null); + } + + return { + getModel: function (obj, field) { + var path = field.path; + + if (!isDefined(path)) + return obj; + + path = path.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties + path = path.replace(/^\./, ''); // strip a leading dot + + var segs = path.split('.'); + var root = obj; + + while (segs.length > 0) { + var pathStep = segs.shift(); + + if (typeof root[pathStep] === 'undefined') + root[pathStep] = {}; + + root = root[pathStep]; + } + + return root; + }, + joinTip: function (arr) { + if (!arr) { + return arr; + } + + var lines = arr.map(function (line) { + var rtrimmed = line.replace(/\s+$/g, ''); + + if (rtrimmed.indexOf('>', this.length - 1) == -1) { + rtrimmed = rtrimmed + '<br/>'; + } + + return rtrimmed; + }); + + return lines.join(""); + }, + isDefined: isDefined, + isNonEmpty: function (s) { + return isDefined(s) && s.trim().length > 0; + }, + errorMessage: errorMessage, + showError: function (msg) { + if (msgModal) + msgModal.hide(); + + msgModal = $alert({title: errorMessage(msg)}); + }, + showInfo: function (msg) { + if (msgModal) + msgModal.hide(); + + msgModal = $alert({ + type: 'success', + title: msg, + duration: 2 + }); + }, + javaBuildInTypes: [ + 'Boolean', 'Byte', 'Date', 'Double', 'Float', 'Integer', 'Long', 'Short', 'String', 'Time', 'Timestamp', 'UUID' + ] + } +}]); + +// Confirm popup service. +controlCenterModule.service('$confirm', function ($modal, $rootScope, $q) { + var scope = $rootScope.$new(); + + var deferred; + + scope.ok = function () { + deferred.resolve(); + + confirmModal.hide(); + }; + + var confirmModal = $modal({templateUrl: '/confirm', scope: scope, placement: 'center', show: false}); + + var parentShow = confirmModal.show; + + confirmModal.show = function (content) { + scope.content = content || 'Confirm deletion?'; + + deferred = $q.defer(); + + parentShow(); + + return deferred.promise; + }; + + return confirmModal; +}); + +// "Save as" popup service. +controlCenterModule.service('$copy', function ($modal, $rootScope, $q) { + var scope = $rootScope.$new(); + + var deferred; + + scope.ok = function (newName) { + deferred.resolve(newName); + + copyModal.hide(); + }; + + var copyModal = $modal({templateUrl: '/copy', scope: scope, placement: 'center', show: false}); + + var parentShow = copyModal.show; + + copyModal.show = function (oldName) { + scope.newName = oldName + '(1)'; + + deferred = $q.defer(); + + parentShow(); + + return deferred.promise; + }; + + return copyModal; +}); + +// Tables support service. +controlCenterModule.service('$table', ['$common', function ($common) { + function _swapSimpleItems(a, ix1, ix2) { + var tmp = a[ix1]; + + a[ix1] = a[ix2]; + a[ix2] = tmp; + } + + function _model(item, field) { + return $common.getModel(item, field); + } + + var table = {name: 'none', editIndex: -1}; + + function _tableReset() { + table.name = 'none'; + table.editIndex = -1; + } + + function _tableState(name, editIndex) { + table.name = name; + table.editIndex = editIndex; + } + + return { + tableState: function (name, editIndex) { + _tableState(name, editIndex); + }, + tableReset: function () { + _tableReset(); + }, + tableNewItem: function (field) { + _tableState(field.model, -1); + }, + tableNewItemActive: function (field) { + return table.name == field.model && table.editIndex < 0; + }, + tableEditing: function (field, index) { + return table.name == field.model && table.editIndex == index; + }, + tableStartEdit: function (item, field, index) { + _tableState(field.model, index); + + return _model(item, field)[field.model][index]; + }, + tableRemove: function (item, field, index) { + _tableReset(); + + _model(item, field)[field.model].splice(index, 1); + }, + tableSimpleSave: function (valueValid, item, field, newValue, index) { + if (valueValid(item, field, newValue, index)) { + _tableReset(); + + if (index < 0) { + if (_model(item, field)[field.model]) + _model(item, field)[field.model].push(newValue); + else + _model(item, field)[field.model] = [newValue]; + } + else + _model(item, field)[field.model][index] = newValue; + } + }, + tableSimpleSaveVisible: function (newValue) { + return $common.isNonEmpty(newValue); + }, + tableSimpleUp: function (item, field, index) { + _tableReset(); + + _swapSimpleItems(_model(item, field)[field.model], index, index - 1); + }, + tableSimpleDown: function (item, field, index) { + _tableReset(); + + _swapSimpleItems(_model(item, field)[field.model], index, index + 1); + }, + tableSimpleDownVisible: function (item, field, index) { + return index < _model(item, field)[field.model].length - 1; + }, + tablePairSave: function (pairValid, item, field, newKey, newValue, index) { + if (pairValid(item, field, newKey, newValue, index)) { + _tableReset(); + + var pair = {}; + + if (index < 0) { + pair[field.keyName] = newKey; + pair[field.valueName] = newValue; + + if (item[field.model]) + item[field.model].push(pair); + else + item[field.model] = [pair]; + } + else { + pair = item[field.model][index]; + + pair[field.keyName] = newKey; + pair[field.valueName] = newValue; + } + } + }, + tablePairSaveVisible: function (newKey, newValue) { + return $common.isNonEmpty(newKey) && $common.isNonEmpty(newValue); + } + } +}]); + + +// Filter to decode name using map(value, label). +controlCenterModule.filter('displayValue', function () { + return function (v, m, dflt) { + var i = _.findIndex(m, function (item) { + return item.value == v; + }); + + if (i >= 0) { + return m[i].label; + } + + if (dflt) { + return dflt; + } + + return 'Unknown value'; + } +}); + +/** + * Filter for replacing all occurrences of {@code org.apache.ignite.} with {@code o.a.i.}, + * {@code org.apache.ignite.internal.} with {@code o.a.i.i.}, + * {@code org.apache.ignite.internal.visor.} with {@code o.a.i.i.v.} and + * {@code org.apache.ignite.scalar.} with {@code o.a.i.s.}. + * + * @param s String to replace in. + * @return Replaces string. + */ +controlCenterModule.filter('compact', function () { + return function (s) { + return s.replace("org.apache.ignite.internal.visor.", "o.a.i.i.v."). + replace("org.apache.ignite.internal.", "o.a.i.i."). + replace("org.apache.ignite.scalar.", "o.a.i.s."). + replace("org.apache.ignite.", "o.a.i."); + } +}); + +// Directive to enable validation for IP addresses. +controlCenterModule.directive('ipaddress', function () { + const ip = '(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])'; + const port = '([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])'; + const portRange = '(:' + port + '(..' + port + ')?)?'; + const host = '(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])'; + + return { + require: 'ngModel', + link: function (scope, elem, attrs, ctrl) { + ctrl.$validators.ipaddress = function (modelValue, viewValue) { + if (ctrl.$isEmpty(modelValue) || !attrs['ipaddress']) + return true; + + return viewValue.match(new RegExp('(^' + ip + portRange + '$)|(^' + host + portRange + '$)')) != null; + } + } + } +}); + +// Directive to enable validation to match specified value. +controlCenterModule.directive('match', function ($parse) { + return { + require: 'ngModel', + link: function (scope, elem, attrs, ctrl) { + scope.$watch(function () { + return $parse(attrs.match)(scope) === ctrl.$modelValue; + }, function (currentValue) { + ctrl.$setValidity('mismatch', currentValue); + }); + } + }; +}); + +// Directive to bind ENTER key press with some user action. +controlCenterModule.directive('ngEnter', function() { + return function(scope, element, attrs) { + element.bind('keydown keypress', function(event) { + if (event.which === 13) { + scope.$apply(function() { + scope.$eval(attrs.ngEnter); + }); + + event.preventDefault(); + } + }); + }; +}); + +// Directive to bind ESC key press with some user action. +controlCenterModule.directive('ngEscape', function() { + return function(scope, element, attrs) { + element.bind('keydown keyup', function(event) { + if (event.which === 27) { + scope.$apply(function() { + scope.$eval(attrs.ngEscape); + }); + + event.preventDefault(); + } + }); + }; +}); + +// Factory function to focus element. +controlCenterModule.factory('focus', function ($timeout, $window) { + return function (id) { + // Timeout makes sure that is invoked after any other event has been triggered. + // E.g. click events that need to run before the focus or inputs elements that are + // in a disabled state but are enabled when those events are triggered. + $timeout(function () { + var element = $window.document.getElementById(id); + + if (element) + element.focus(); + }); + }; +}); + +// Directive to mark elements to focus. +controlCenterModule.directive('eventFocus', function (focus) { + return function (scope, elem, attr) { + elem.on(attr.eventFocus, function () { + focus(attr.eventFocusId); + }); + + // Removes bound events in the element itself when the scope is destroyed + scope.$on('$destroy', function () { + element.off(attr.eventFocus); + }); + }; +}); + +// Navigation bar controller. +controlCenterModule.controller('activeLink', [ + '$scope', function ($scope) { + $scope.isActive = function (path) { + return window.location.pathname.substr(0, path.length) == path; + }; + }]); + +// Login popup controller. +controlCenterModule.controller('auth', [ + '$scope', '$modal', '$alert', '$http', '$window', '$common', + function ($scope, $modal, $alert, $http, $window, $common) { + $scope.errorMessage = $common.errorMessage; + + $scope.action = 'login'; + + $scope.valid = false; + + $scope.userDropdown = [{"text": "Profile", "href": "/profile"}]; + + if (!$scope.becomeUsed) { + if ($scope.user && $scope.user.admin) + $scope.userDropdown.push({"text": "Admin Panel", "href": "/admin"}); + + $scope.userDropdown.push({"text": "Log Out", "href": "/logout"}); + } + + // Pre-fetch an external template populated with a custom scope + var authModal = $modal({scope: $scope, templateUrl: '/login', show: false}); + + $scope.login = function () { + // Show when some event occurs (use $promise property to ensure the template has been loaded) + authModal.$promise.then(authModal.show); + }; + + $scope.auth = function (action, user_info) { + $http.post('/' + action, user_info) + .success(function () { + authModal.hide(); + + $window.location = '/configuration/clusters'; + }) + .error(function (data) { + $alert({placement: 'top', container: '#errors-container', title: $scope.errorMessage(data)}); + }); + }; + }]); \ No newline at end of file