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 <[email protected]>", + "auth": { + "user": "[email protected]", + "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; +};
