ignite-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From voze...@apache.org
Subject [43/69] [abbrv] ignite git commit: Web Console beta-3.
Date Tue, 13 Sep 2016 09:53:35 GMT
http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/app/settings.js
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/app/settings.js b/modules/web-console/backend/app/settings.js
new file mode 100644
index 0000000..b3609e8
--- /dev/null
+++ b/modules/web-console/backend/app/settings.js
@@ -0,0 +1,80 @@
+/*
+ * 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 with server-side configuration.
+ */
+module.exports = {
+    implements: 'settings',
+    inject: ['nconf', 'require(fs)']
+};
+
+module.exports.factory = function(nconf, fs) {
+    /**
+     * Normalize a port into a number, string, or false.
+     */
+    const _normalizePort = function(val) {
+        const port = parseInt(val, 10);
+
+        // named pipe
+        if (isNaN(port))
+            return val;
+
+        // port number
+        if (port >= 0)
+            return port;
+
+        return false;
+    };
+
+    const mailConfig = nconf.get('mail') || {};
+
+    return {
+        agent: {
+            dists: 'agent_dists',
+            port: _normalizePort(nconf.get('agentServer:port') || 3002),
+            legacyPort: _normalizePort(nconf.get('agentServer:legacyPort')),
+            SSLOptions: nconf.get('agentServer:ssl') && {
+                key: fs.readFileSync(nconf.get('agentServer:key')),
+                cert: fs.readFileSync(nconf.get('agentServer:cert')),
+                passphrase: nconf.get('agentServer:keyPassphrase')
+            }
+        },
+        server: {
+            port: _normalizePort(nconf.get('server:port') || 3000),
+            SSLOptions: nconf.get('server:ssl') && {
+                enable301Redirects: true,
+                trustXFPHeader: true,
+                key: fs.readFileSync(nconf.get('server:key')),
+                cert: fs.readFileSync(nconf.get('server:cert')),
+                passphrase: nconf.get('server:keyPassphrase')
+            }
+        },
+        smtp: {
+            ...mailConfig,
+            address: (username, email) => username ? '"' + username + '" <' + email + '>' : email
+        },
+        mongoUrl: nconf.get('mongodb:url') || 'mongodb://localhost/console',
+        cookieTTL: 3600000 * 24 * 30,
+        sessionSecret: nconf.get('server:sessionSecret') || 'keyboard cat',
+        tokenLength: 20
+    };
+};

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/config/settings.json.sample
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/config/settings.json.sample b/modules/web-console/backend/config/settings.json.sample
new file mode 100644
index 0000000..41f1152
--- /dev/null
+++ b/modules/web-console/backend/config/settings.json.sample
@@ -0,0 +1,30 @@
+{
+    "server": {
+        "port": 3000,
+        "sessionSecret": "CHANGE ME",
+        "ssl": false,
+        "key": "serve/keys/test.key",
+        "cert": "serve/keys/test.crt",
+        "keyPassphrase": "password"
+    },
+    "mongodb": {
+        "url": "mongodb://localhost/console"
+    },
+    "agentServer": {
+        "port": 3001,
+        "ssl": false,
+        "key": "serve/keys/test.key",
+        "cert": "serve/keys/test.crt",
+        "keyPassphrase": "password"
+    },
+    "mail": {
+        "service": "",
+        "sign": "Kind regards,<br>Apache Ignite Team",
+        "greeting": "Apache Ignite Web Console",
+        "from": "Apache Ignite Web Console <someusername@somecompany.tld>",
+        "auth": {
+            "user": "someusername@somecompany.tld",
+            "pass": ""
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/errors/AppErrorException.js
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/errors/AppErrorException.js b/modules/web-console/backend/errors/AppErrorException.js
new file mode 100644
index 0000000..208b09b
--- /dev/null
+++ b/modules/web-console/backend/errors/AppErrorException.js
@@ -0,0 +1,36 @@
+/*
+ * 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';
+
+class AppErrorException extends Error {
+    constructor(message) {
+        super(message);
+
+        this.name = this.constructor.name;
+        this.code = 400;
+        this.httpCode = 400;
+        this.message = message;
+
+        if (typeof Error.captureStackTrace === 'function')
+            Error.captureStackTrace(this, this.constructor);
+        else
+            this.stack = (new Error(message)).stack;
+    }
+}
+
+module.exports = AppErrorException;

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/errors/AuthFailedException.js
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/errors/AuthFailedException.js b/modules/web-console/backend/errors/AuthFailedException.js
new file mode 100644
index 0000000..3208e1d
--- /dev/null
+++ b/modules/web-console/backend/errors/AuthFailedException.js
@@ -0,0 +1,30 @@
+/*
+ * 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';
+
+import AppErrorException from './AppErrorException';
+
+class AuthFailedException extends AppErrorException {
+    constructor(message) {
+        super(message);
+
+        this.httpCode = 401;
+    }
+}
+
+module.exports = AuthFailedException;

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/errors/DuplicateKeyException.js
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/errors/DuplicateKeyException.js b/modules/web-console/backend/errors/DuplicateKeyException.js
new file mode 100644
index 0000000..b228d0c
--- /dev/null
+++ b/modules/web-console/backend/errors/DuplicateKeyException.js
@@ -0,0 +1,28 @@
+/*
+ * 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';
+
+import AppErrorException from './AppErrorException';
+
+class DuplicateKeyException extends AppErrorException {
+    constructor(message) {
+        super(message);
+    }
+}
+
+module.exports = DuplicateKeyException;

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/errors/IllegalAccessError.js
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/errors/IllegalAccessError.js b/modules/web-console/backend/errors/IllegalAccessError.js
new file mode 100644
index 0000000..4fcd2d4
--- /dev/null
+++ b/modules/web-console/backend/errors/IllegalAccessError.js
@@ -0,0 +1,29 @@
+/*
+ * 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';
+
+import AppErrorException from './AppErrorException';
+
+class IllegalAccessError extends AppErrorException {
+    constructor(message) {
+        super(message);
+        this.httpCode = 401;
+    }
+}
+
+module.exports = IllegalAccessError;

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/errors/IllegalArgumentException.js
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/errors/IllegalArgumentException.js b/modules/web-console/backend/errors/IllegalArgumentException.js
new file mode 100644
index 0000000..0487d05
--- /dev/null
+++ b/modules/web-console/backend/errors/IllegalArgumentException.js
@@ -0,0 +1,29 @@
+/*
+ * 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';
+
+import AppErrorException from './AppErrorException';
+
+class IllegalArgumentException extends AppErrorException {
+    constructor(message) {
+        super(message);
+        this.httpCode = 400;
+    }
+}
+
+module.exports = IllegalArgumentException;

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/errors/MissingResourceException.js
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/errors/MissingResourceException.js b/modules/web-console/backend/errors/MissingResourceException.js
new file mode 100644
index 0000000..799775b
--- /dev/null
+++ b/modules/web-console/backend/errors/MissingResourceException.js
@@ -0,0 +1,30 @@
+/*
+ * 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';
+
+import AppErrorException from './AppErrorException';
+
+class MissingResourceException extends AppErrorException {
+    constructor(message) {
+        super(message);
+
+        this.httpCode = 404;
+    }
+}
+
+module.exports = MissingResourceException;

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/errors/ServerErrorException.js
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/errors/ServerErrorException.js b/modules/web-console/backend/errors/ServerErrorException.js
new file mode 100644
index 0000000..439755e
--- /dev/null
+++ b/modules/web-console/backend/errors/ServerErrorException.js
@@ -0,0 +1,36 @@
+/*
+ * 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';
+
+class ServerErrorException extends Error {
+    constructor(message) {
+        super(message);
+
+        this.name = this.constructor.name;
+        this.code = 500;
+        this.httpCode = 500;
+        this.message = message;
+
+        if (typeof Error.captureStackTrace === 'function')
+            Error.captureStackTrace(this, this.constructor);
+        else
+            this.stack = (new Error(message)).stack;
+    }
+}
+
+module.exports = ServerErrorException;

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/errors/index.js
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/errors/index.js b/modules/web-console/backend/errors/index.js
new file mode 100644
index 0000000..0af5cd5
--- /dev/null
+++ b/modules/web-console/backend/errors/index.js
@@ -0,0 +1,39 @@
+/*
+ * 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!
+
+import AppErrorException from './AppErrorException';
+import IllegalArgumentException from './IllegalArgumentException';
+import DuplicateKeyException from './DuplicateKeyException';
+import ServerErrorException from './ServerErrorException';
+import MissingResourceException from './MissingResourceException';
+import AuthFailedException from './AuthFailedException';
+
+module.exports = {
+    implements: 'errors',
+    factory: () => ({
+        AppErrorException,
+        IllegalArgumentException,
+        DuplicateKeyException,
+        ServerErrorException,
+        MissingResourceException,
+        AuthFailedException
+    })
+};

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/index.js
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/index.js b/modules/web-console/backend/index.js
new file mode 100644
index 0000000..dcb3f41
--- /dev/null
+++ b/modules/web-console/backend/index.js
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+require('babel-core/register');
+require('./app/index.js');

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/injector.js
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/injector.js b/modules/web-console/backend/injector.js
new file mode 100644
index 0000000..62f8980
--- /dev/null
+++ b/modules/web-console/backend/injector.js
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+import fireUp from 'fire-up';
+
+module.exports = fireUp.newInjector({
+    basePath: __dirname,
+    modules: [
+        './app/**/*.js',
+        './config/**/*.js',
+        './errors/**/*.js',
+        './middlewares/**/*.js',
+        './routes/**/*.js',
+        './services/**/*.js'
+    ]
+});

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/middlewares/api.js
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/middlewares/api.js b/modules/web-console/backend/middlewares/api.js
new file mode 100644
index 0000000..9c6395e
--- /dev/null
+++ b/modules/web-console/backend/middlewares/api.js
@@ -0,0 +1,44 @@
+/*
+ * 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: 'middlewares:api',
+    factory: () => {
+        return (req, res, next) => {
+            res.api = {
+                error(err) {
+                    // TODO: removed code from error
+                    res.status(err.httpCode || err.code || 500).send(err.message);
+                },
+                ok(data) {
+                    res.status(200).json(data);
+                },
+                serverError(err) {
+                    err.httpCode = 500;
+
+                    res.api.error(err);
+                }
+            };
+
+            next();
+        };
+    }
+};

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/middlewares/host.js
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/middlewares/host.js b/modules/web-console/backend/middlewares/host.js
new file mode 100644
index 0000000..5ddd918
--- /dev/null
+++ b/modules/web-console/backend/middlewares/host.js
@@ -0,0 +1,39 @@
+/*
+ * 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: 'middlewares:host',
+    factory: () => {
+        return (req, res, next) => {
+            req.origin = function() {
+                if (req.headers.origin)
+                    return req.headers.origin;
+
+                if (req.headers['x-forwarded-server'])
+                    return `${req.headers['x-forwarded-proto'] || 'http'}://${req.headers['x-forwarded-server']}`;
+
+                return `${req.protocol}://${req.get('host')}`;
+            };
+
+            next();
+        };
+    }
+};

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/middlewares/user.js
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/middlewares/user.js b/modules/web-console/backend/middlewares/user.js
new file mode 100644
index 0000000..8923211
--- /dev/null
+++ b/modules/web-console/backend/middlewares/user.js
@@ -0,0 +1,36 @@
+/*
+ * 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: 'middlewares:user',
+    factory: () => {
+        return (req, res, next) => {
+            req.currentUserId = function() {
+                if (req.session.viewedUser && req.user.admin)
+                    return req.session.viewedUser._id;
+
+                return req.user._id;
+            };
+
+            next();
+        };
+    }
+};

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/package.json
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/package.json b/modules/web-console/backend/package.json
new file mode 100644
index 0000000..598dc00
--- /dev/null
+++ b/modules/web-console/backend/package.json
@@ -0,0 +1,71 @@
+{
+  "name": "ignite-web-console",
+  "version": "1.0.0",
+  "description": "Interactive Web console for configuration, executing SQL queries and monitoring of Apache Ignite Cluster",
+  "private": true,
+  "scripts": {
+    "ci-test": "cross-env NODE_ENV=test CONFIG_PATH='./test/config/settings.json' mocha -u tdd --require babel-core/register --reporter mocha-teamcity-reporter --recursive ./test/unit",
+    "test": "cross-env NODE_ENV=test CONFIG_PATH='./test/config/settings.json' mocha -u tdd --require babel-core/register  --recursive ./test/unit",
+    "eslint": "eslint --env node --format node_modules/eslint-friendly-formatter ./ -- --eff-by-issue",
+    "start": "node ./index.js"
+  },
+  "author": "",
+  "contributors": [
+    {
+      "name": "",
+      "email": ""
+    }
+  ],
+  "license": "Apache-2.0",
+  "keywords": "grid",
+  "homepage": "https://ignite.apache.org/",
+  "engines": {
+    "npm": "^3.x.x",
+    "node": "^4.x.x"
+  },
+  "os": [
+    "darwin",
+    "linux",
+    "win32"
+  ],
+  "dependencies": {
+    "body-parser": "^1.15.0",
+    "connect-mongo": "^1.1.0",
+    "cookie-parser": "~1.4.0",
+    "es6-promise": "^3.0.2",
+    "express": "^4.14.0",
+    "express-session": "^1.12.0",
+    "fire-up": "^1.0.0",
+    "glob": "^7.0.3",
+    "jszip": "^3.0.0",
+    "lodash": "^4.8.2",
+    "mongoose": "^4.4.11",
+    "morgan": "^1.7.0",
+    "nconf": "^0.8.2",
+    "nodemailer": "^2.3.0",
+    "passport": "^0.3.2",
+    "passport-local": "^1.0.0",
+    "passport-local-mongoose": "^4.0.0",
+    "passport.socketio": "^3.6.1",
+    "socket.io": "^1.4.5",
+    "ws": "^0.8.0"
+  },
+  "devDependencies": {
+    "babel-core": "^6.7.6",
+    "babel-eslint": "^6.0.4",
+    "babel-plugin-add-module-exports": "^0.2.1",
+    "babel-plugin-transform-builtin-extend": "^1.1.0",
+    "babel-plugin-transform-runtime": "^6.7.5",
+    "babel-polyfill": "^6.7.4",
+    "babel-preset-es2015": "^6.9.0",
+    "babel-preset-stage-1": "^6.5.0",
+    "babel-runtime": "^6.6.1",
+    "chai": "^3.5.0",
+    "cross-env": "^1.0.7",
+    "eslint": "^2.9.0",
+    "eslint-friendly-formatter": "^2.0.5",
+    "jasmine-core": "^2.4.1",
+    "mocha": "~2.5.3",
+    "mocha-teamcity-reporter": "^1.0.0"
+  }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/routes/admin.js
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/routes/admin.js b/modules/web-console/backend/routes/admin.js
new file mode 100644
index 0000000..70736d0
--- /dev/null
+++ b/modules/web-console/backend/routes/admin.js
@@ -0,0 +1,84 @@
+/*
+ * 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/admin',
+    inject: ['require(lodash)', 'require(express)', 'settings', 'mongo', 'services/spaces', 'services/mails', 'services/sessions', 'services/users']
+};
+
+/**
+ * @param _
+ * @param express
+ * @param settings
+ * @param mongo
+ * @param spacesService
+ * @param {MailsService} mailsService
+ * @param {SessionsService} sessionsService
+ * @param {UsersService} usersService
+ * @returns {Promise}
+ */
+module.exports.factory = function(_, express, settings, mongo, spacesService, mailsService, sessionsService, usersService) {
+    return new Promise((factoryResolve) => {
+        const router = new express.Router();
+
+        /**
+         * Get list of user accounts.
+         */
+        router.post('/list', (req, res) => {
+            usersService.list()
+                .then(res.api.ok)
+                .catch(res.api.error);
+        });
+
+        // Remove user.
+        router.post('/remove', (req, res) => {
+            usersService.remove(req.origin(), req.body.userId)
+                .then(res.api.ok)
+                .catch(res.api.error);
+        });
+
+        // Save user.
+        router.post('/save', (req, res) => {
+            const params = req.body;
+
+            mongo.Account.findByIdAndUpdate(params.userId, {admin: params.adminFlag}).exec()
+                .then(res.api.ok)
+                .catch(res.api.error);
+        });
+
+        // Become user.
+        router.get('/become', (req, res) => {
+            sessionsService.become(req.session, req.query.viewedUserId)
+                .then(res.api.ok)
+                .catch(res.api.error);
+        });
+
+        // Revert to your identity.
+        router.get('/revert/identity', (req, res) => {
+            sessionsService.revert(req.session)
+                .then(res.api.ok)
+                .catch(res.api.error);
+        });
+
+        factoryResolve(router);
+    });
+};
+

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/routes/agent.js
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/routes/agent.js b/modules/web-console/backend/routes/agent.js
new file mode 100644
index 0000000..3f90fbd
--- /dev/null
+++ b/modules/web-console/backend/routes/agent.js
@@ -0,0 +1,53 @@
+/*
+ * 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/agents',
+    inject: ['require(lodash)', 'require(express)', 'services/agents']
+};
+
+/**
+ * @param _
+ * @param express
+ * @param {AgentsService} agentsService
+ * @returns {Promise}
+ */
+module.exports.factory = function(_, express, agentsService) {
+    return new Promise((resolveFactory) => {
+        const router = new express.Router();
+
+        /* Get grid topology. */
+        router.get('/download/zip', (req, res) => {
+            const host = req.hostname.match(/:/g) ? req.hostname.slice(0, req.hostname.indexOf(':')) : req.hostname;
+
+            agentsService.getArchive(host, req.user.token)
+                .then(({fileName, buffer}) => {
+                    // Set the archive name.
+                    res.attachment(fileName);
+
+                    res.send(buffer);
+                })
+                .catch(res.api.error);
+        });
+
+        resolveFactory(router);
+    });
+};

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/routes/caches.js
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/routes/caches.js b/modules/web-console/backend/routes/caches.js
new file mode 100644
index 0000000..e040fda
--- /dev/null
+++ b/modules/web-console/backend/routes/caches.js
@@ -0,0 +1,65 @@
+/*
+ * 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/caches',
+    inject: ['require(lodash)', 'require(express)', 'mongo', 'services/caches']
+};
+
+module.exports.factory = function(_, express, mongo, cachesService) {
+    return new Promise((factoryResolve) => {
+        const router = new express.Router();
+
+        /**
+         * Save cache.
+         */
+        router.post('/save', (req, res) => {
+            const cache = req.body;
+
+            cachesService.merge(cache)
+                .then((savedCache) => res.api.ok(savedCache._id))
+                .catch(res.api.error);
+        });
+
+        /**
+         * Remove cache by ._id.
+         */
+        router.post('/remove', (req, res) => {
+            const cacheId = req.body._id;
+
+            cachesService.remove(cacheId)
+                .then(res.api.ok)
+                .catch(res.api.error);
+        });
+
+        /**
+         * Remove all caches.
+         */
+        router.post('/remove/all', (req, res) => {
+            cachesService.removeAll(req.currentUserId(), req.header('IgniteDemoMode'))
+                .then(res.api.ok)
+                .catch(res.api.error);
+        });
+
+        factoryResolve(router);
+    });
+};
+

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/routes/clusters.js
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/routes/clusters.js b/modules/web-console/backend/routes/clusters.js
new file mode 100644
index 0000000..97a446a
--- /dev/null
+++ b/modules/web-console/backend/routes/clusters.js
@@ -0,0 +1,64 @@
+/*
+ * 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/clusters',
+    inject: ['require(lodash)', 'require(express)', 'mongo', 'services/clusters']
+};
+
+module.exports.factory = function(_, express, mongo, clustersService) {
+    return new Promise((factoryResolve) => {
+        const router = new express.Router();
+
+        /**
+         * Save cluster.
+         */
+        router.post('/save', (req, res) => {
+            const cluster = req.body;
+
+            clustersService.merge(cluster)
+                .then((savedCluster) => res.api.ok(savedCluster._id))
+                .catch(res.api.error);
+        });
+
+        /**
+         * Remove cluster by ._id.
+         */
+        router.post('/remove', (req, res) => {
+            const clusterId = req.body._id;
+
+            clustersService.remove(clusterId)
+                .then(res.api.ok)
+                .catch(res.api.error);
+        });
+
+        /**
+         * Remove all clusters.
+         */
+        router.post('/remove/all', (req, res) => {
+            clustersService.removeAll(req.currentUserId(), req.header('IgniteDemoMode'))
+                .then(res.api.ok)
+                .catch(res.api.error);
+        });
+
+        factoryResolve(router);
+    });
+};

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/routes/configuration.js
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/routes/configuration.js b/modules/web-console/backend/routes/configuration.js
new file mode 100644
index 0000000..c3ff5d6
--- /dev/null
+++ b/modules/web-console/backend/routes/configuration.js
@@ -0,0 +1,41 @@
+/*
+ * 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/configurations',
+    inject: ['require(lodash)', 'require(express)', 'mongo', 'services/configurations']
+};
+
+module.exports.factory = function(_, express, mongo, configurationsService) {
+    return new Promise((factoryResolve) => {
+        const router = new express.Router();
+        /**
+         * Get all user configuration in current space.
+         */
+        router.get('/list', (req, res) => {
+            configurationsService.list(req.currentUserId(), req.header('IgniteDemoMode'))
+                .then(res.api.ok)
+                .catch(res.api.error);
+        });
+
+        factoryResolve(router);
+    });
+};

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/routes/demo.js
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/routes/demo.js b/modules/web-console/backend/routes/demo.js
new file mode 100644
index 0000000..724b5c1
--- /dev/null
+++ b/modules/web-console/backend/routes/demo.js
@@ -0,0 +1,133 @@
+/*
+ * 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!
+
+import clusters from './demo/clusters.json';
+import caches from './demo/caches.json';
+import domains from './demo/domains.json';
+import igfss from './demo/igfss.json';
+
+module.exports = {
+    implements: 'routes/demo',
+    inject: ['require(lodash)', 'require(express)', 'settings', 'mongo', 'services/spaces', 'errors']
+};
+
+module.exports.factory = (_, express, settings, mongo, spacesService, errors) => {
+    return new Promise((factoryResolve) => {
+        const router = new express.Router();
+
+        /**
+         * Reset demo configuration.
+         */
+        router.post('/reset', (req, res) => {
+            spacesService.spaces(req.user._id, true)
+                .then((spaces) => {
+                    if (spaces.length) {
+                        const spaceIds = spaces.map((space) => space._id);
+
+                        return Promise.all([
+                            mongo.Cluster.remove({space: {$in: spaceIds}}).exec(),
+                            mongo.Cache.remove({space: {$in: spaceIds}}).exec(),
+                            mongo.DomainModel.remove({space: {$in: spaceIds}}).exec(),
+                            mongo.Igfs.remove({space: {$in: spaceIds}}).exec()
+                        ]).then(() => spaces[0]);
+                    }
+                })
+                .catch((err) => {
+                    if (err instanceof errors.MissingResourceException)
+                        return spacesService.createDemoSpace(req.user._id);
+                })
+                .then((space) => {
+                    return Promise.all(_.map(clusters, (cluster) => {
+                        const clusterDoc = new mongo.Cluster(cluster);
+
+                        clusterDoc.space = space._id;
+
+                        return clusterDoc.save();
+                    }));
+                })
+                .then((clusterDocs) => {
+                    return _.map(clusterDocs, (cluster) => {
+                        const addCacheToCluster = (cacheDoc) => cluster.caches.push(cacheDoc._id);
+                        const addIgfsToCluster = (igfsDoc) => cluster.igfss.push(igfsDoc._id);
+
+                        if (cluster.name.endsWith('-caches')) {
+                            const cachePromises = _.map(caches, (cacheData) => {
+                                const cache = new mongo.Cache(cacheData);
+
+                                cache.space = cluster.space;
+                                cache.clusters.push(cluster._id);
+
+                                return cache.save()
+                                    .then((cacheDoc) => {
+                                        const domainData = _.find(domains, (item) =>
+                                            item.databaseTable === cacheDoc.name.slice(0, -5).toUpperCase());
+
+                                        if (domainData) {
+                                            const domain = new mongo.DomainModel(domainData);
+
+                                            domain.space = cacheDoc.space;
+                                            domain.caches.push(cacheDoc._id);
+
+                                            return domain.save()
+                                                .then((domainDoc) => {
+                                                    cacheDoc.domains.push(domainDoc._id);
+
+                                                    return cacheDoc.save();
+                                                });
+                                        }
+
+                                        return cacheDoc;
+                                    });
+                            });
+
+                            return Promise.all(cachePromises)
+                                .then((cacheDocs) => {
+                                    _.forEach(cacheDocs, addCacheToCluster);
+
+                                    return cluster.save();
+                                });
+                        }
+
+                        if (cluster.name.endsWith('-igfs')) {
+                            return Promise.all(_.map(igfss, (igfs) => {
+                                const igfsDoc = new mongo.Igfs(igfs);
+
+                                igfsDoc.space = cluster.space;
+                                igfsDoc.clusters.push(cluster._id);
+
+                                return igfsDoc.save();
+                            }))
+                            .then((igfsDocs) => {
+                                _.forEach(igfsDocs, addIgfsToCluster);
+
+                                return cluster.save();
+                            });
+                        }
+                    });
+                })
+                .then(() => res.sendStatus(200))
+                .catch((err) => res.status(500).send(err.message));
+        });
+
+        factoryResolve(router);
+    });
+};
+

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/routes/demo/caches.json
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/routes/demo/caches.json b/modules/web-console/backend/routes/demo/caches.json
new file mode 100644
index 0000000..f7a8690
--- /dev/null
+++ b/modules/web-console/backend/routes/demo/caches.json
@@ -0,0 +1,87 @@
+[
+  {
+    "name": "CarCache",
+    "cacheMode": "PARTITIONED",
+    "atomicityMode": "ATOMIC",
+    "readThrough": true,
+    "writeThrough": true,
+    "sqlFunctionClasses": [],
+    "cacheStoreFactory": {
+      "kind": "CacheJdbcPojoStoreFactory",
+      "CacheJdbcPojoStoreFactory": {
+        "dataSourceBean": "dsH2",
+        "dialect": "H2"
+      }
+    },
+    "domains": [],
+    "clusters": []
+  },
+  {
+    "name": "ParkingCache",
+    "cacheMode": "PARTITIONED",
+    "atomicityMode": "ATOMIC",
+    "readThrough": true,
+    "writeThrough": true,
+    "sqlFunctionClasses": [],
+    "cacheStoreFactory": {
+      "kind": "CacheJdbcPojoStoreFactory",
+      "CacheJdbcPojoStoreFactory": {
+        "dataSourceBean": "dsH2",
+        "dialect": "H2"
+      }
+    },
+    "domains": [],
+    "clusters": []
+  },
+  {
+    "name": "CountryCache",
+    "cacheMode": "PARTITIONED",
+    "atomicityMode": "ATOMIC",
+    "readThrough": true,
+    "writeThrough": true,
+    "sqlFunctionClasses": [],
+    "cacheStoreFactory": {
+      "kind": "CacheJdbcPojoStoreFactory",
+      "CacheJdbcPojoStoreFactory": {
+        "dataSourceBean": "dsH2",
+        "dialect": "H2"
+      }
+    },
+    "domains": [],
+    "clusters": []
+  },
+  {
+    "name": "DepartmentCache",
+    "cacheMode": "PARTITIONED",
+    "atomicityMode": "ATOMIC",
+    "readThrough": true,
+    "writeThrough": true,
+    "sqlFunctionClasses": [],
+    "cacheStoreFactory": {
+      "kind": "CacheJdbcPojoStoreFactory",
+      "CacheJdbcPojoStoreFactory": {
+        "dataSourceBean": "dsH2",
+        "dialect": "H2"
+      }
+    },
+    "domains": [],
+    "clusters": []
+  },
+  {
+    "name": "EmployeeCache",
+    "cacheMode": "PARTITIONED",
+    "atomicityMode": "ATOMIC",
+    "readThrough": true,
+    "writeThrough": true,
+    "sqlFunctionClasses": [],
+    "cacheStoreFactory": {
+      "kind": "CacheJdbcPojoStoreFactory",
+      "CacheJdbcPojoStoreFactory": {
+        "dataSourceBean": "dsH2",
+        "dialect": "H2"
+      }
+    },
+    "domains": [],
+    "clusters": []
+  }
+]

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/routes/demo/clusters.json
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/routes/demo/clusters.json b/modules/web-console/backend/routes/demo/clusters.json
new file mode 100644
index 0000000..014b519
--- /dev/null
+++ b/modules/web-console/backend/routes/demo/clusters.json
@@ -0,0 +1,50 @@
+[
+  {
+    "name": "cluster-igfs",
+    "connector": {
+      "noDelay": true
+    },
+    "communication": {
+      "tcpNoDelay": true
+    },
+    "igfss": [],
+    "caches": [],
+    "binaryConfiguration": {
+      "compactFooter": true,
+      "typeConfigurations": []
+    },
+    "discovery": {
+      "kind": "Multicast",
+      "Multicast": {
+        "addresses": ["127.0.0.1:47500..47510"]
+      },
+      "Vm": {
+        "addresses": ["127.0.0.1:47500..47510"]
+      }
+    }
+  },
+  {
+    "name": "cluster-caches",
+    "connector": {
+      "noDelay": true
+    },
+    "communication": {
+      "tcpNoDelay": true
+    },
+    "igfss": [],
+    "caches": [],
+    "binaryConfiguration": {
+      "compactFooter": true,
+      "typeConfigurations": []
+    },
+    "discovery": {
+      "kind": "Multicast",
+      "Multicast": {
+        "addresses": ["127.0.0.1:47500..47510"]
+      },
+      "Vm": {
+        "addresses": ["127.0.0.1:47500..47510"]
+      }
+    }
+  }
+]

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/routes/demo/domains.json
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/routes/demo/domains.json b/modules/web-console/backend/routes/demo/domains.json
new file mode 100644
index 0000000..980d8d1
--- /dev/null
+++ b/modules/web-console/backend/routes/demo/domains.json
@@ -0,0 +1,307 @@
+[
+  {
+    "keyType": "Integer",
+    "valueType": "model.Parking",
+    "queryMetadata": "Configuration",
+    "databaseSchema": "CARS",
+    "databaseTable": "PARKING",
+    "indexes": [],
+    "aliases": [],
+    "fields": [
+      {
+        "name": "name",
+        "className": "String"
+      },
+      {
+        "name": "capacity",
+        "className": "Integer"
+      }
+    ],
+    "valueFields": [
+      {
+        "databaseFieldName": "NAME",
+        "databaseFieldType": "VARCHAR",
+        "javaFieldName": "name",
+        "javaFieldType": "String"
+      },
+      {
+        "databaseFieldName": "CAPACITY",
+        "databaseFieldType": "INTEGER",
+        "javaFieldName": "capacity",
+        "javaFieldType": "int"
+      }
+    ],
+    "keyFields": [
+      {
+        "databaseFieldName": "ID",
+        "databaseFieldType": "INTEGER",
+        "javaFieldName": "id",
+        "javaFieldType": "int"
+      }
+    ],
+    "caches": []
+  },
+  {
+    "keyType": "Integer",
+    "valueType": "model.Department",
+    "queryMetadata": "Configuration",
+    "databaseSchema": "PUBLIC",
+    "databaseTable": "DEPARTMENT",
+    "indexes": [],
+    "aliases": [],
+    "fields": [
+      {
+        "name": "countryId",
+        "className": "Integer"
+      },
+      {
+        "name": "name",
+        "className": "String"
+      }
+    ],
+    "valueFields": [
+      {
+        "databaseFieldName": "COUNTRY_ID",
+        "databaseFieldType": "INTEGER",
+        "javaFieldName": "countryId",
+        "javaFieldType": "int"
+      },
+      {
+        "databaseFieldName": "NAME",
+        "databaseFieldType": "VARCHAR",
+        "javaFieldName": "name",
+        "javaFieldType": "String"
+      }
+    ],
+    "keyFields": [
+      {
+        "databaseFieldName": "ID",
+        "databaseFieldType": "INTEGER",
+        "javaFieldName": "id",
+        "javaFieldType": "int"
+      }
+    ],
+    "caches": []
+  },
+  {
+    "keyType": "Integer",
+    "valueType": "model.Employee",
+    "queryMetadata": "Configuration",
+    "databaseSchema": "PUBLIC",
+    "databaseTable": "EMPLOYEE",
+    "indexes": [
+      {
+        "name": "EMP_NAMES",
+        "indexType": "SORTED",
+        "fields": [
+          {
+            "name": "firstName",
+            "direction": true
+          },
+          {
+            "name": "lastName",
+            "direction": true
+          }
+        ]
+      },
+      {
+        "name": "EMP_SALARY",
+        "indexType": "SORTED",
+        "fields": [
+          {
+            "name": "salary",
+            "direction": true
+          }
+        ]
+      }
+    ],
+    "aliases": [],
+    "fields": [
+      {
+        "name": "departmentId",
+        "className": "Integer"
+      },
+      {
+        "name": "managerId",
+        "className": "Integer"
+      },
+      {
+        "name": "firstName",
+        "className": "String"
+      },
+      {
+        "name": "lastName",
+        "className": "String"
+      },
+      {
+        "name": "email",
+        "className": "String"
+      },
+      {
+        "name": "phoneNumber",
+        "className": "String"
+      },
+      {
+        "name": "hireDate",
+        "className": "Date"
+      },
+      {
+        "name": "job",
+        "className": "String"
+      },
+      {
+        "name": "salary",
+        "className": "Double"
+      }
+    ],
+    "valueFields": [
+      {
+        "databaseFieldName": "DEPARTMENT_ID",
+        "databaseFieldType": "INTEGER",
+        "javaFieldName": "departmentId",
+        "javaFieldType": "int"
+      },
+      {
+        "databaseFieldName": "MANAGER_ID",
+        "databaseFieldType": "INTEGER",
+        "javaFieldName": "managerId",
+        "javaFieldType": "Integer"
+      },
+      {
+        "databaseFieldName": "FIRST_NAME",
+        "databaseFieldType": "VARCHAR",
+        "javaFieldName": "firstName",
+        "javaFieldType": "String"
+      },
+      {
+        "databaseFieldName": "LAST_NAME",
+        "databaseFieldType": "VARCHAR",
+        "javaFieldName": "lastName",
+        "javaFieldType": "String"
+      },
+      {
+        "databaseFieldName": "EMAIL",
+        "databaseFieldType": "VARCHAR",
+        "javaFieldName": "email",
+        "javaFieldType": "String"
+      },
+      {
+        "databaseFieldName": "PHONE_NUMBER",
+        "databaseFieldType": "VARCHAR",
+        "javaFieldName": "phoneNumber",
+        "javaFieldType": "String"
+      },
+      {
+        "databaseFieldName": "HIRE_DATE",
+        "databaseFieldType": "DATE",
+        "javaFieldName": "hireDate",
+        "javaFieldType": "Date"
+      },
+      {
+        "databaseFieldName": "JOB",
+        "databaseFieldType": "VARCHAR",
+        "javaFieldName": "job",
+        "javaFieldType": "String"
+      },
+      {
+        "databaseFieldName": "SALARY",
+        "databaseFieldType": "DOUBLE",
+        "javaFieldName": "salary",
+        "javaFieldType": "Double"
+      }
+    ],
+    "keyFields": [
+      {
+        "databaseFieldName": "ID",
+        "databaseFieldType": "INTEGER",
+        "javaFieldName": "id",
+        "javaFieldType": "int"
+      }
+    ],
+    "caches": []
+  },
+  {
+    "keyType": "Integer",
+    "valueType": "model.Country",
+    "queryMetadata": "Configuration",
+    "databaseSchema": "PUBLIC",
+    "databaseTable": "COUNTRY",
+    "indexes": [],
+    "aliases": [],
+    "fields": [
+      {
+        "name": "name",
+        "className": "String"
+      },
+      {
+        "name": "population",
+        "className": "Integer"
+      }
+    ],
+    "valueFields": [
+      {
+        "databaseFieldName": "NAME",
+        "databaseFieldType": "VARCHAR",
+        "javaFieldName": "name",
+        "javaFieldType": "String"
+      },
+      {
+        "databaseFieldName": "POPULATION",
+        "databaseFieldType": "INTEGER",
+        "javaFieldName": "population",
+        "javaFieldType": "int"
+      }
+    ],
+    "keyFields": [
+      {
+        "databaseFieldName": "ID",
+        "databaseFieldType": "INTEGER",
+        "javaFieldName": "id",
+        "javaFieldType": "int"
+      }
+    ],
+    "caches": []
+  },
+  {
+    "keyType": "Integer",
+    "valueType": "model.Car",
+    "queryMetadata": "Configuration",
+    "databaseSchema": "CARS",
+    "databaseTable": "CAR",
+    "indexes": [],
+    "aliases": [],
+    "fields": [
+      {
+        "name": "parkingId",
+        "className": "Integer"
+      },
+      {
+        "name": "name",
+        "className": "String"
+      }
+    ],
+    "valueFields": [
+      {
+        "databaseFieldName": "PARKING_ID",
+        "databaseFieldType": "INTEGER",
+        "javaFieldName": "parkingId",
+        "javaFieldType": "int"
+      },
+      {
+        "databaseFieldName": "NAME",
+        "databaseFieldType": "VARCHAR",
+        "javaFieldName": "name",
+        "javaFieldType": "String"
+      }
+    ],
+    "keyFields": [
+      {
+        "databaseFieldName": "ID",
+        "databaseFieldType": "INTEGER",
+        "javaFieldName": "id",
+        "javaFieldType": "int"
+      }
+    ],
+    "caches": []
+  }
+]

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/routes/demo/igfss.json
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/routes/demo/igfss.json b/modules/web-console/backend/routes/demo/igfss.json
new file mode 100644
index 0000000..cd128a6
--- /dev/null
+++ b/modules/web-console/backend/routes/demo/igfss.json
@@ -0,0 +1,10 @@
+[
+  {
+    "ipcEndpointEnabled": true,
+    "fragmentizerEnabled": true,
+    "name": "igfs",
+    "dataCacheName": "igfs-data",
+    "metaCacheName": "igfs-meta",
+    "clusters": []
+  }
+]

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/routes/domains.js
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/routes/domains.js b/modules/web-console/backend/routes/domains.js
new file mode 100644
index 0000000..db1d892
--- /dev/null
+++ b/modules/web-console/backend/routes/domains.js
@@ -0,0 +1,76 @@
+/*
+ * 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/domains',
+    inject: ['require(lodash)', 'require(express)', 'mongo', 'services/domains']
+};
+
+module.exports.factory = (_, express, mongo, domainsService) => {
+    return new Promise((factoryResolve) => {
+        const router = new express.Router();
+
+        /**
+         * Save domain model.
+         */
+        router.post('/save', (req, res) => {
+            const domain = req.body;
+
+            domainsService.batchMerge([domain])
+                .then(res.api.ok)
+                .catch(res.api.error);
+        });
+
+        /**
+         * Batch save domain models.
+         */
+        router.post('/save/batch', (req, res) => {
+            const domains = req.body;
+
+            domainsService.batchMerge(domains)
+                .then(res.api.ok)
+                .catch(res.api.error);
+        });
+
+        /**
+         * Remove domain model by ._id.
+         */
+        router.post('/remove', (req, res) => {
+            const domainId = req.body._id;
+
+            domainsService.remove(domainId)
+                .then(res.api.ok)
+                .catch(res.api.error);
+        });
+
+        /**
+         * Remove all domain models.
+         */
+        router.post('/remove/all', (req, res) => {
+            domainsService.removeAll(req.currentUserId(), req.header('IgniteDemoMode'))
+                .then(res.api.ok)
+                .catch(res.api.error);
+        });
+
+        factoryResolve(router);
+    });
+};
+

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/routes/igfss.js
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/routes/igfss.js b/modules/web-console/backend/routes/igfss.js
new file mode 100644
index 0000000..c88d627
--- /dev/null
+++ b/modules/web-console/backend/routes/igfss.js
@@ -0,0 +1,65 @@
+/*
+ * 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/igfss',
+    inject: ['require(lodash)', 'require(express)', 'mongo', 'services/igfss']
+};
+
+module.exports.factory = function(_, express, mongo, igfssService) {
+    return new Promise((factoryResolve) => {
+        const router = new express.Router();
+
+        /**
+         * Save IGFS.
+         */
+        router.post('/save', (req, res) => {
+            const igfs = req.body;
+
+            igfssService.merge(igfs)
+                .then((savedIgfs) => res.api.ok(savedIgfs._id))
+                .catch(res.api.error);
+        });
+
+        /**
+         * Remove IGFS by ._id.
+         */
+        router.post('/remove', (req, res) => {
+            const igfsId = req.body._id;
+
+            igfssService.remove(igfsId)
+                .then(res.api.ok)
+                .catch(res.api.error);
+        });
+
+        /**
+         * Remove all IGFSs.
+         */
+        router.post('/remove/all', (req, res) => {
+            igfssService.removeAll(req.currentUserId(), req.header('IgniteDemoMode'))
+                .then(res.api.ok)
+                .catch(res.api.error);
+        });
+
+        factoryResolve(router);
+    });
+};
+

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/routes/notebooks.js
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/routes/notebooks.js b/modules/web-console/backend/routes/notebooks.js
new file mode 100644
index 0000000..c330809
--- /dev/null
+++ b/modules/web-console/backend/routes/notebooks.js
@@ -0,0 +1,80 @@
+/*
+ * 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/notebooks',
+    inject: ['require(lodash)', 'require(express)', 'mongo', 'services/spaces', 'services/notebooks']
+};
+
+module.exports.factory = (_, express, mongo, spacesService, notebooksService) => {
+    return new Promise((factoryResolve) => {
+        const router = new express.Router();
+
+        /**
+         * Get notebooks names accessed for user account.
+         *
+         * @param req Request.
+         * @param res Response.
+         */
+        router.get('/', (req, res) => {
+            return spacesService.spaces(req.currentUserId())
+                .then((spaces) => _.map(spaces, (space) => space._id))
+                .then((spaceIds) => notebooksService.listBySpaces(spaceIds))
+                .then(res.api.ok)
+                .catch(res.api.error);
+        });
+
+        /**
+         * Save notebook accessed for user account.
+         *
+         * @param req Request.
+         * @param res Response.
+         */
+        router.post('/save', (req, res) => {
+            const notebook = req.body;
+
+            spacesService.spaceIds(req.currentUserId())
+                .then((spaceIds) => {
+                    notebook.space = notebook.space || spaceIds[0];
+
+                    return notebooksService.merge(notebook);
+                })
+                .then(res.api.ok)
+                .catch(res.api.error);
+        });
+
+        /**
+         * Remove notebook by ._id.
+         *
+         * @param req Request.
+         * @param res Response.
+         */
+        router.post('/remove', (req, res) => {
+            const notebookId = req.body._id;
+
+            notebooksService.remove(notebookId)
+                .then(res.api.ok)
+                .catch(res.api.error);
+        });
+
+        factoryResolve(router);
+    });
+};

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/routes/profile.js
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/routes/profile.js b/modules/web-console/backend/routes/profile.js
new file mode 100644
index 0000000..4d01cda
--- /dev/null
+++ b/modules/web-console/backend/routes/profile.js
@@ -0,0 +1,73 @@
+/*
+ * 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/profiles',
+    inject: ['require(lodash)', 'require(express)', 'mongo', 'services/users']
+};
+
+/**
+ *
+ * @param _ Lodash module
+ * @param express Express module
+ * @param mongo
+ * @param {UsersService} usersService
+ * @returns {Promise}
+ */
+module.exports.factory = function(_, express, mongo, usersService) {
+    return new Promise((resolveFactory) => {
+        const router = new express.Router();
+
+        /**
+         * Save user profile.
+         */
+        router.post('/save', (req, res) => {
+            if (req.body.password && _.isEmpty(req.body.password))
+                return res.status(500).send('Wrong value for new password!');
+
+            usersService.save(req.body)
+                .then((user) => {
+                    const becomeUsed = req.session.viewedUser && user.admin;
+
+                    if (becomeUsed) {
+                        req.session.viewedUser = user;
+
+                        return user;
+                    }
+
+                    return new Promise((resolve, reject) => {
+                        req.logout();
+
+                        req.logIn(user, {}, (errLogIn) => {
+                            if (errLogIn)
+                                return reject(errLogIn);
+
+                            return resolve(user);
+                        });
+                    });
+                })
+                .then(res.api.ok)
+                .catch(res.api.error);
+        });
+
+        resolveFactory(router);
+    });
+};

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/routes/public.js
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/routes/public.js b/modules/web-console/backend/routes/public.js
new file mode 100644
index 0000000..5aad11a
--- /dev/null
+++ b/modules/web-console/backend/routes/public.js
@@ -0,0 +1,168 @@
+/*
+ * 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/public',
+    inject: ['require(express)', 'require(passport)', 'settings', 'mongo', 'services/mails', 'services/users']
+};
+
+/**
+ *
+ * @param express
+ * @param passport
+ * @param settings
+ * @param mongo
+ * @param mailsService
+ * @param {UsersService} usersService
+ * @returns {Promise}
+ */
+module.exports.factory = function(express, passport, settings, mongo, mailsService, usersService) {
+    return new Promise((factoryResolve) => {
+        const router = new 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', (req, res) => {
+            usersService.get(req.user, req.session.viewedUser)
+                .then(res.api.ok)
+                .catch(res.api.error);
+        });
+
+        /**
+         * Register new account.
+         */
+        router.post('/signup', (req, res) => {
+            usersService.create(req.origin(), req.body)
+                .then((user) => new Promise((resolve, reject) => {
+                    req.logIn(user, {}, (err) => {
+                        if (err)
+                            reject(err);
+
+                        resolve(user);
+                    });
+                }))
+                .then(res.api.ok)
+                .catch(res.api.error);
+        });
+
+        /**
+         * Sign in into exist account.
+         */
+        router.post('/signin', (req, res, next) => {
+            passport.authenticate('local', (errAuth, user) => {
+                if (errAuth)
+                    return res.status(401).send(errAuth.message);
+
+                if (!user)
+                    return res.status(401).send('Invalid email or password');
+
+                req.logIn(user, {}, (errLogIn) => {
+                    if (errLogIn)
+                        return res.status(401).send(errLogIn.message);
+
+                    return res.sendStatus(200);
+                });
+            })(req, res, next);
+        });
+
+        /**
+         * Logout.
+         */
+        router.post('/logout', (req, res) => {
+            req.logout();
+
+            res.sendStatus(200);
+        });
+
+        /**
+         * Send e-mail to user with reset token.
+         */
+        router.post('/password/forgot', (req, res) => {
+            mongo.Account.findOne({email: req.body.email}).exec()
+                .then((user) => {
+                    if (!user)
+                        throw new Error('Account with that email address does not exists!');
+
+                    user.resetPasswordToken = _randomString();
+
+                    return user.save();
+                })
+                .then((user) => mailsService.emailUserResetLink(req.origin(), user))
+                .then(() => res.status(200).send('An email has been sent with further instructions.'))
+                .catch((err) => {
+                    // TODO IGNITE-843 Send email to admin
+                    return res.status(401).send(err.message);
+                });
+        });
+
+        /**
+         * Change password with given token.
+         */
+        router.post('/password/reset', (req, res) => {
+            mongo.Account.findOne({resetPasswordToken: req.body.token}).exec()
+                .then((user) => {
+                    if (!user)
+                        throw new Error('Failed to find account with this token! Please check link from email.');
+
+                    return new Promise((resolve, reject) => {
+                        user.setPassword(req.body.password, (err, _user) => {
+                            if (err)
+                                return reject(new Error('Failed to reset password: ' + err.message));
+
+                            _user.resetPasswordToken = undefined; // eslint-disable-line no-undefined
+
+                            resolve(_user.save());
+                        });
+                    });
+                })
+                .then((user) => mailsService.emailPasswordChanged(req.origin(), user))
+                .then((user) => res.status(200).send(user.email))
+                .catch((err) => res.status(401).send(err.message));
+        });
+
+        /* GET reset password page. */
+        router.post('/password/validate/token', (req, res) => {
+            const token = req.body.token;
+
+            mongo.Account.findOne({resetPasswordToken: token}).exec()
+                .then((user) => {
+                    if (!user)
+                        throw new Error('Invalid token for password reset!');
+
+                    return res.json({token, email: user.email});
+                })
+                .catch((err) => res.status(401).send(err.message));
+        });
+
+        factoryResolve(router);
+    });
+};

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/services/agents.js
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/services/agents.js b/modules/web-console/backend/services/agents.js
new file mode 100644
index 0000000..8a65739
--- /dev/null
+++ b/modules/web-console/backend/services/agents.js
@@ -0,0 +1,82 @@
+/*
+ * 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: 'services/agents',
+    inject: ['require(lodash)', 'require(fs)', 'require(path)', 'require(jszip)', 'settings', 'agent-manager', 'errors']
+};
+
+/**
+ * @param _
+ * @param fs
+ * @param path
+ * @param JSZip
+ * @param settings
+ * @param agentMgr
+ * @param errors
+ * @returns {AgentsService}
+ */
+module.exports.factory = (_, fs, path, JSZip, settings, agentMgr, errors) => {
+    class AgentsService {
+        /**
+         * Get agent archive with user agent configuration.
+         * @returns {*} - readable stream for further piping. (http://stuk.github.io/jszip/documentation/api_jszip/generate_node_stream.html)
+         */
+        static getArchive(host, token) {
+            const latest = agentMgr.supportedAgents.latest;
+
+            if (_.isEmpty(latest))
+                throw new errors.MissingResourceException('Missing agent zip on server. Please ask webmaster to upload agent zip!');
+
+            const filePath = latest.filePath;
+            const fileName = latest.fileName;
+
+            const folder = path.basename(latest.fileName, '.zip');
+
+            // Read a zip file.
+            return new Promise((resolve, reject) => {
+                fs.readFile(filePath, (errFs, data) => {
+                    if (errFs)
+                        reject(new errors.ServerErrorException(errFs));
+
+                    JSZip.loadAsync(data)
+                        .then((zip) => {
+                            const prop = [];
+
+                            prop.push('tokens=' + token);
+                            prop.push('server-uri=' + (settings.agent.SSLOptions ? 'https' : 'http') + '://' + host + ':' + settings.agent.port);
+                            prop.push('#Uncomment following options if needed:');
+                            prop.push('#node-uri=http://localhost:8080');
+                            prop.push('#driver-folder=./jdbc-drivers');
+
+                            zip.file(folder + '/default.properties', prop.join('\n'));
+
+                            return zip.generateAsync({type: 'nodebuffer', platform: 'UNIX'})
+                                .then((buffer) => resolve({filePath, fileName, buffer}));
+                        })
+                        .catch(reject);
+                });
+            });
+        }
+    }
+
+    return AgentsService;
+};

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/services/auth.js
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/services/auth.js b/modules/web-console/backend/services/auth.js
new file mode 100644
index 0000000..9f7d77d
--- /dev/null
+++ b/modules/web-console/backend/services/auth.js
@@ -0,0 +1,47 @@
+/*
+ * 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: 'services/auth',
+    inject: ['require(lodash)', 'mongo', 'services/spaces', 'errors']
+};
+
+/**
+ * @param _
+ * @param mongo
+ * @param {SpacesService} spacesService
+ * @param errors
+ * @returns {AuthService}
+ */
+module.exports.factory = (_, mongo, spacesService, errors) => {
+    class AuthService {
+        // TODO IGNITE-3774: move implementation from public router.
+        static resetPassword() {
+
+        }
+
+        static validateResetToken() {
+
+        }
+    }
+
+    return AuthService;
+};

http://git-wip-us.apache.org/repos/asf/ignite/blob/6af6560a/modules/web-console/backend/services/caches.js
----------------------------------------------------------------------
diff --git a/modules/web-console/backend/services/caches.js b/modules/web-console/backend/services/caches.js
new file mode 100644
index 0000000..f8cc2ed
--- /dev/null
+++ b/modules/web-console/backend/services/caches.js
@@ -0,0 +1,144 @@
+/*
+ * 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: 'services/caches',
+    inject: ['require(lodash)', 'mongo', 'services/spaces', 'errors']
+};
+
+/**
+ * @param _
+ * @param mongo
+ * @param {SpacesService} spaceService
+ * @param errors
+ * @returns {CachesService}
+ */
+module.exports.factory = (_, mongo, spaceService, errors) => {
+    /**
+     * Convert remove status operation to own presentation.
+     * @param {RemoveResult} result - The results of remove operation.
+     */
+    const convertRemoveStatus = ({result}) => ({rowsAffected: result.n});
+
+    /**
+     * Update existing cache
+     * @param {Object} cache - The cache for updating
+     * @returns {Promise.<mongo.ObjectId>} that resolves cache id
+     */
+    const update = (cache) => {
+        const cacheId = cache._id;
+
+        return mongo.Cache.update({_id: cacheId}, cache, {upsert: true}).exec()
+            .then(() => mongo.Cluster.update({_id: {$in: cache.clusters}}, {$addToSet: {caches: cacheId}}, {multi: true}).exec())
+            .then(() => mongo.Cluster.update({_id: {$nin: cache.clusters}}, {$pull: {caches: cacheId}}, {multi: true}).exec())
+            .then(() => mongo.DomainModel.update({_id: {$in: cache.domains}}, {$addToSet: {caches: cacheId}}, {multi: true}).exec())
+            .then(() => mongo.DomainModel.update({_id: {$nin: cache.domains}}, {$pull: {caches: cacheId}}, {multi: true}).exec())
+            .then(() => cache)
+            .catch((err) => {
+                if (err.code === mongo.errCodes.DUPLICATE_KEY_UPDATE_ERROR || err.code === mongo.errCodes.DUPLICATE_KEY_ERROR)
+                    throw new errors.DuplicateKeyException('Cache with name: "' + cache.name + '" already exist.');
+            });
+    };
+
+    /**
+     * Create new cache.
+     * @param {Object} cache - The cache for creation.
+     * @returns {Promise.<mongo.ObjectId>} that resolves cache id.
+     */
+    const create = (cache) => {
+        return mongo.Cache.create(cache)
+            .then((savedCache) =>
+                mongo.Cluster.update({_id: {$in: savedCache.clusters}}, {$addToSet: {caches: savedCache._id}}, {multi: true}).exec()
+                    .then(() => mongo.DomainModel.update({_id: {$in: savedCache.domains}}, {$addToSet: {caches: savedCache._id}}, {multi: true}).exec())
+                    .then(() => savedCache)
+            )
+            .catch((err) => {
+                if (err.code === mongo.errCodes.DUPLICATE_KEY_ERROR)
+                    throw new errors.DuplicateKeyException('Cache with name: "' + cache.name + '" already exist.');
+            });
+    };
+
+    /**
+     * Remove all caches by space ids.
+     * @param {Number[]} spaceIds - The space ids for cache deletion.
+     * @returns {Promise.<RemoveResult>} - that resolves results of remove operation.
+     */
+    const removeAllBySpaces = (spaceIds) => {
+        return mongo.Cluster.update({space: {$in: spaceIds}}, {caches: []}, {multi: true}).exec()
+            .then(() => mongo.DomainModel.update({space: {$in: spaceIds}}, {caches: []}, {multi: true}).exec())
+            .then(() => mongo.Cache.remove({space: {$in: spaceIds}}).exec());
+    };
+
+    /**
+     * Service for manipulate Cache entities.
+     */
+    class CachesService {
+        /**
+         * Create or update cache.
+         * @param {Object} cache - The cache.
+         * @returns {Promise.<mongo.ObjectId>} that resolves cache id of merge operation.
+         */
+        static merge(cache) {
+            if (cache._id)
+                return update(cache);
+
+            return create(cache);
+        }
+
+        /**
+         * Get caches by spaces.
+         * @param {mongo.ObjectId|String} spaceIds - The spaces ids that own caches.
+         * @returns {Promise.<mongo.Cache[]>} - contains requested caches.
+         */
+        static listBySpaces(spaceIds) {
+            return mongo.Cache.find({space: {$in: spaceIds}}).sort('name').lean().exec();
+        }
+
+        /**
+         * Remove cache.
+         * @param {mongo.ObjectId|String} cacheId - The cache id for remove.
+         * @returns {Promise.<{rowsAffected}>} - The number of affected rows.
+         */
+        static remove(cacheId) {
+            if (_.isNil(cacheId))
+                return Promise.reject(new errors.IllegalArgumentException('Cache id can not be undefined or null'));
+
+            return mongo.Cluster.update({caches: {$in: [cacheId]}}, {$pull: {caches: cacheId}}, {multi: true}).exec()
+                .then(() => mongo.DomainModel.update({caches: {$in: [cacheId]}}, {$pull: {caches: cacheId}}, {multi: true}).exec())
+                .then(() => mongo.Cache.remove({_id: cacheId}).exec())
+                .then(convertRemoveStatus);
+        }
+
+        /**
+         * Remove all caches by user.
+         * @param {mongo.ObjectId|String} userId - The user id that own caches.
+         * @param {Boolean} demo - The flag indicates that need lookup in demo space.
+         * @returns {Promise.<{rowsAffected}>} - The number of affected rows.
+         */
+        static removeAll(userId, demo) {
+            return spaceService.spaceIds(userId, demo)
+                .then(removeAllBySpaces)
+                .then(convertRemoveStatus);
+        }
+    }
+
+    return CachesService;
+};


Mime
View raw message