mirror of https://gitee.com/bigwinds/arangodb
327 lines
8.9 KiB
JavaScript
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.
|
|
`);
|