1
0
Fork 0
arangodb/js/apps/system/_admin/aardvark/APP/aardvark.js

327 lines
8.9 KiB
JavaScript

'use strict';
////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2010-2013 triAGENS GmbH, Cologne, Germany
/// Copyright 2016 ArangoDB GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Michael Hackstein
/// @author Alan Plum
////////////////////////////////////////////////////////////////////////////////
const joi = require('joi');
const Netmask = require('netmask').Netmask;
const dd = require('dedent');
const internal = require('internal');
const db = require('@arangodb').db;
const errors = require('@arangodb').errors;
const joinPath = require('path').posix.join;
const notifications = require('@arangodb/configuration').notifications;
const examples = require('@arangodb/graph-examples/example-graph');
const createRouter = require('@arangodb/foxx/router');
const users = require('@arangodb/users');
const cluster = require('@arangodb/cluster');
const ERROR_USER_NOT_FOUND = errors.ERROR_USER_NOT_FOUND.code;
const API_DOCS = require(module.context.fileName('api-docs.json'));
API_DOCS.basePath = `/_db/${encodeURIComponent(db._name())}`;
const router = createRouter();
module.exports = router;
let trustedProxies = TRUSTED_PROXIES();
let trustedProxyBlocks;
if (Array.isArray(trustedProxies)) {
trustedProxyBlocks = [];
trustedProxies.forEach(trustedProxy => {
try {
trustedProxyBlocks.push(new Netmask(trustedProxy));
} catch (e) {
console.warn("Error parsing trusted proxy " + trustedProxy, e);
}
});
} else {
trustedProxyBlocks = null;
}
let isTrustedProxy = function(proxyAddress) {
if (trustedProxies === null) {
return true;
}
return trustedProxyBlocks.some(block => {
return block.contains(proxyAddress);
});
}
router.get('/config.js', function(req, res) {
let basePath = '';
if (req.headers.hasOwnProperty('x-forwarded-for')
&& req.headers.hasOwnProperty('x-script-name')
&& isTrustedProxy(req.remoteAddress)) {
basePath = req.headers['x-script-name'];
}
res.set('content-type', 'text/javascript');
res.send("var frontendConfig = " + JSON.stringify({
"basePath": basePath,
"db": req.database,
"authenticationEnabled": global.AUTHENTICATION_ENABLED(),
"isCluster": cluster.isCluster()
}));
});
router.get('/whoAmI', function(req, res) {
res.json({user: req.user || null});
})
.summary('Return the current user')
.description(dd`
Returns the current user or "null" if the user is not authenticated.
Returns "false" if authentication is disabled.
`);
const authRouter = createRouter();
router.use(authRouter);
authRouter.use((req, res, next) => {
if (global.AUTHENTICATION_ENABLED()) {
if (!req.user) {
res.throw('unauthorized');
}
}
next();
});
router.get('/api/*', module.context.apiDocumentation({
swaggerJson(req, res) {
res.json(API_DOCS);
}
}))
.summary('System API documentation')
.description(dd`
Mounts the system API documentation.
`);
authRouter.get('shouldCheckVersion', function(req, res) {
const versions = notifications.versions();
res.json(Boolean(versions && versions.enableVersionNotification));
})
.summary('Is version check allowed')
.description(dd`
Check if version check is allowed.
`);
authRouter.post('disableVersionCheck', function(req, res) {
notifications.setVersions({enableVersionNotification: false});
res.json('ok');
})
.summary('Disable version check')
.description(dd`
Disable the version check in web interface
`);
authRouter.post('/query/explain', function(req, res) {
const bindVars = req.body.bindVars;
const query = req.body.query;
const id = req.body.id;
const batchSize = req.body.batchSize;
let msg = null;
try {
if (bindVars) {
msg = require('@arangodb/aql/explainer').explain({
query: query,
bindVars: bindVars,
batchSize: batchSize,
id: id
}, {colors: false}, false, bindVars);
} else {
msg = require('@arangodb/aql/explainer').explain(query, {colors: false}, false);
}
} catch (e) {
res.throw('bad request', e.message, {cause: e});
}
res.json({msg});
})
.body(joi.object({
query: joi.string().required(),
bindVars: joi.object().optional(),
batchSize: joi.number().optional(),
id: joi.string().optional()
}).required(), 'Query and bindVars to explain.')
.summary('Explains a query')
.description(dd`
Explains a query in a more user-friendly way than the query_api/explain
`);
authRouter.post('/query/upload/:user', function(req, res) {
let user = req.pathParams.user;
try {
user = users.document(user);
} catch (e) {
if (!e.isArangoError || e.errorNum !== ERROR_USER_NOT_FOUND) {
throw e;
}
res.throw('not found');
}
if (!user.extra.queries) {
user.extra.queries = [];
}
const existingQueries = user.extra.queries
.map(query => query.name);
for (const query of req.body) {
if (existingQueries.indexOf(query.name) === -1) {
existingQueries.push(query.name);
user.extra.queries.push(query);
}
}
users.update(user.user, undefined, undefined, user.extra);
res.json(user.extra.queries);
})
.pathParam('user', joi.string().required(), 'Username. Ignored if authentication is enabled.')
.body(joi.array().items(joi.object({
name: joi.string().required(),
parameter: joi.any().optional(),
value: joi.any().optional()
}).required()).required(), 'User query array to import.')
.error('not found', 'User does not exist.')
.summary('Upload user queries')
.description(dd`
This function uploads all given user queries.
`);
authRouter.get('/query/download/:user', function(req, res) {
let user = req.pathParams.user;
try {
user = users.document(user);
} catch (e) {
if (!e.isArangoError || e.errorNum !== ERROR_USER_NOT_FOUND) {
throw e;
}
res.throw('not found');
}
res.attachment(`queries-${db._name()}-${user.user}.json`);
res.json(user.extra.queries || []);
})
.pathParam('user', joi.string().required(), 'Username. Ignored if authentication is enabled.')
.error('not found', 'User does not exist.')
.summary('Download stored queries')
.description(dd`
Download and export all queries from the given username.
`);
authRouter.get('/query/result/download/:query', function(req, res) {
let query;
try {
query = internal.base64Decode(req.pathParams.query);
query = JSON.parse(query);
}
catch (e) {
res.throw('bad request', e.message, {cause: e});
}
const result = db._query(query.query, query.bindVars).toArray();
res.attachment(`results-${db._name()}.json`);
res.json(result);
})
.pathParam('query', joi.string().required(), 'Base64 encoded query.')
.error('bad request', 'The query is invalid or malformed.')
.summary('Download the result of a query')
.description(dd`
This function downloads the result of a user query.
`);
authRouter.post('/graph-examples/create/:name', function(req, res) {
const name = req.pathParams.name;
if (['knows_graph', 'social', 'routeplanner'].indexOf(name) === -1) {
res.throw('not found');
}
const g = examples.loadGraph(name);
res.json({error: !g});
})
.pathParam('name', joi.string().required(), 'Name of the example graph.')
.summary('Create example graphs')
.description(dd`
Create one of the given example graphs.
`);
authRouter.post('/job', function(req, res) {
db._frontend.save(Object.assign(req.body, {model: 'job'}));
res.json(true);
})
.body(joi.object({
id: joi.any().required(),
collection: joi.any().required(),
type: joi.any().required(),
desc: joi.any().required()
}).required())
.summary('Store job id of a running job')
.description(dd`
Create a new job id entry in a specific system database with a given id.
`);
authRouter.delete('/job', function(req, res) {
db._frontend.removeByExample({model: 'job'}, false);
res.json(true);
})
.summary('Delete all jobs')
.description(dd`
Delete all jobs in a specific system database with a given id.
`);
authRouter.delete('/job/:id', function(req, res) {
db._frontend.removeByExample({id: req.pathParams.id}, false);
res.json(true);
})
.summary('Delete a job id')
.description(dd`
Delete an existing job id entry in a specific system database with a given id.
`);
authRouter.get('/job', function(req, res) {
const result = db._frontend.all().toArray();
res.json(result);
})
.summary('Return all job ids.')
.description(dd`
This function returns the job ids of all currently running jobs.
`);