'use strict';
// //////////////////////////////////////////////////////////////////////////////
// / DISCLAIMER
// /
// / Copyright 2013-2014 triAGENS GmbH, Cologne, Germany
// / Copyright 2015-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 Dr. Frank Celler
// / @author Alan Plum
// //////////////////////////////////////////////////////////////////////////////
const _ = require('lodash');
const accepts = require('accepts');
const fs = require('fs');
const ansiHtml = require('ansi-html');
const dd = require('@arangodb/util').dedent;
const arangodb = require('@arangodb');
const ArangoError = arangodb.ArangoError;
const errors = arangodb.errors;
const actions = require('@arangodb/actions');
const routeLegacyService = require('@arangodb/foxx/legacy/routing').routeService;
const codeFrame = require('@arangodb/util').codeFrame;
function solarize (ansiText) {
try {
ansiHtml.setColors({
reset: ['657b83', 'fdf6e3'],
black: 'eee8d5',
red: 'dc322f',
green: '859900',
yellow: 'b58900',
blue: '268bd2',
magenta: 'd33682',
cyan: '2aa198',
lightgrey: '586e75',
darkgrey: '93a1a1'
});
const html = ansiHtml(ansiText);
return html;
} finally {
ansiHtml.reset();
}
}
function escapeHtml (raw) {
return String(raw)
.replace(/&/g, '&')
.replace(/
${escapeHtml(title || 'Service Unavailable')}
${body}
`;
}
}
}],
middleware: [],
context: {},
models: {},
foxx: true
};
}
// //////////////////////////////////////////////////////////////////////////////
// / @brief routes a default Configuration Required service
// //////////////////////////////////////////////////////////////////////////////
function createServiceNeedsConfigurationRoute (service) {
return createErrorRoute(service, new ArangoError({
errorNum: errors.ERROR_SERVICE_NEEDS_CONFIGURATION.code,
errorMessage: errors.ERROR_SERVICE_NEEDS_CONFIGURATION.message
}), dd`
Service needs to be configured
This service requires configuration
and has either not yet been fully configured
or is missing some of its dependencies.
`);
}
// //////////////////////////////////////////////////////////////////////////////
// / @brief routes this service if the original is broken service
// //////////////////////////////////////////////////////////////////////////////
function createBrokenServiceRoute (service, err) {
if (service.isDevelopment) {
const title = String(err).replace(/\n/g, ' ');
const frame = codeFrame(err.cause || err, service.basePath, true);
let stacktrace = err.stack;
while (err.cause) {
err = err.cause;
stacktrace += `\nvia ${err.stack}`;
}
const body = [
dd`
Failed to mount service at "${
escapeHtml(service.mount)
}"
An error occurred while mounting this service.
This usually indicates a problem with the service itself.
`,
frame ? dd`
${solarize(escapeHtml(frame))}
` : '',
dd`
Stacktrace
${escapeHtml(stacktrace)}
`
].filter(Boolean).join('\n');
return createErrorRoute(service, err, body, title);
}
return createErrorRoute(service, err, dd`
Failed to mount service
An error occured while mounting the service.
Please check the log file for errors.
`);
}
function checkAndCreateDefaultDocumentRoute (service) {
const defaultDocument = service.manifest.defaultDocument;
if (defaultDocument) {
service.routes.routes.push({
url: {match: '/'},
action: {
do: '@arangodb/actions/redirectRequest',
options: {
permanently: false,
destination: defaultDocument,
relative: true
}
}
});
}
}
// -----------------------------------------------------------------------------
// --SECTION-- public functions
// -----------------------------------------------------------------------------
// //////////////////////////////////////////////////////////////////////////////
// / @brief computes the routes and exports of an service
// //////////////////////////////////////////////////////////////////////////////
exports.routeService = function (service, throwOnErrors) {
if (service.main.loaded && !service.isDevelopment) {
return {
exports: service.main.exports,
routes: service.routes,
docs: service.legacy ? null : service.docs
};
}
if (service.needsConfiguration()) {
return {
get exports () {
if (!throwOnErrors) {
return service.main.exports;
}
throw new ArangoError({
errorNum: errors.ERROR_SERVICE_NEEDS_CONFIGURATION.code,
errorMessage: errors.ERROR_SERVICE_NEEDS_CONFIGURATION.message
});
},
routes: createServiceNeedsConfigurationRoute(service),
docs: null
};
}
service._reset();
let error = null;
if (service.legacy) {
error = routeLegacyService(service, throwOnErrors);
checkAndCreateDefaultDocumentRoute(service);
} else {
checkAndCreateDefaultDocumentRoute(service);
if (service.manifest.main) {
try {
service.main.exports = service.run(service.manifest.main);
} catch (e) {
e.codeFrame = codeFrame(e.cause || e, service.basePath);
console.errorStack(
e,
`Service "${service.mount}" encountered an error while being mounted`
);
if (throwOnErrors) {
throw e;
}
error = e;
}
}
service.buildRoutes();
}
if (service.manifest.files) {
const files = service.manifest.files;
_.each(files, function (file, path) {
const directory = file.path || file;
const normalized = arangodb.normalizeURL(`/${path}`);
const route = {
url: {match: `${normalized}/*`},
action: {
do: '@arangodb/actions/pathHandler',
options: {
root: service.root,
path: fs.join(service.path, directory),
type: file.type,
gzip: Boolean(file.gzip)
}
}
};
service.routes.routes.push(route);
});
}
service.main.loaded = true;
return {
exports: service.main.exports,
routes: error ? createBrokenServiceRoute(service, error) : service.routes,
docs: service.legacy ? null : service.docs
};
};