From 877066392e5bdc8930d4d5f314aa7c24fab894a7 Mon Sep 17 00:00:00 2001 From: Frank Celler Date: Mon, 25 Mar 2013 14:10:10 +0100 Subject: [PATCH 1/2] foxx manager cleanup --- html/admin/js/modules/org/arangodb-common.js | 57 ++++- js/actions/api-foxx.js | 13 +- js/client/modules/org/arangodb/deploy.js | 38 +-- js/client/modules/org/arangodb/foxx.js | 12 +- js/common/bootstrap/module-fs.js | 4 +- js/common/modules/org/arangodb-common.js | 61 ++++- js/server/modules/org/arangodb/actions.js | 12 +- .../modules/org/arangodb/foxx-manager.js | 230 ++++++++++++++++++ js/server/modules/org/arangodb/foxx.js | 60 +---- lib/V8/v8-utils.cpp | 4 +- 10 files changed, 378 insertions(+), 113 deletions(-) create mode 100644 js/server/modules/org/arangodb/foxx-manager.js diff --git a/html/admin/js/modules/org/arangodb-common.js b/html/admin/js/modules/org/arangodb-common.js index 591af4f2e5..00ad5f2254 100644 --- a/html/admin/js/modules/org/arangodb-common.js +++ b/html/admin/js/modules/org/arangodb-common.js @@ -35,6 +35,53 @@ var internal = require("internal"); // --SECTION-- module "arangodb" // ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// --SECTION-- public functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup ArangoShell +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief guesses the content type +//////////////////////////////////////////////////////////////////////////////// + +exports.guessContentType = function (filename) { + var re = /.*\.([^\.]*)$/; + var match = re.exec(filename); + var extension; + + if (match === null) { + return "text/plain; charset=utf-8"; + } + + extension = match[1]; + + if (extension === "html") { + return "text/html; charset=utf-8"; + } + + if (extension === "xml") { + return "application/xml; charset=utf-8"; + } + + if (extension === "json") { + return "application/json; charset=utf-8"; + } + + if (extension === "js") { + return "application/x-javascript; charset=utf-8"; + } + + return "text/plain; charset=utf-8"; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + // ----------------------------------------------------------------------------- // --SECTION-- MODULE EXPORTS // ----------------------------------------------------------------------------- @@ -44,11 +91,19 @@ var internal = require("internal"); /// @{ //////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// @brief function "normalizeURL" +//////////////////////////////////////////////////////////////////////////////// + +exports.normalizeURL = internal.normalizeURL; + //////////////////////////////////////////////////////////////////////////////// /// @brief function "output" //////////////////////////////////////////////////////////////////////////////// -exports.output = function () { internal.output.apply(internal.output, arguments); }; +exports.output = function () { + internal.output.apply(internal.output, arguments); +}; //////////////////////////////////////////////////////////////////////////////// /// @brief function "print" diff --git a/js/actions/api-foxx.js b/js/actions/api-foxx.js index 8ed15159d6..2184891e07 100644 --- a/js/actions/api-foxx.js +++ b/js/actions/api-foxx.js @@ -46,7 +46,7 @@ var foxx = require("org/arangodb/foxx"); //////////////////////////////////////////////////////////////////////////////// actions.defineHttp({ - url : "_admin/foxx/load", + url : "_admin/foxx/install", context : "admin", prefix : false, @@ -58,14 +58,11 @@ actions.defineHttp({ return; } - if (! body.hasOwnProperty("manifest")) { - actions.resultBad(req, - res, - arangodb.ERROR_HTTP_BAD_PARAMETER, - "body must specify a 'manifest'"); - } + var name = body.name; + var mount = body.mount; + var options = body.options || {}; - result = foxx.loadManifest(body.manifest); + result = foxx.installApp(name, mount, options); actions.resultOk(req, res, actions.HTTP_OK, result); } }); diff --git a/js/client/modules/org/arangodb/deploy.js b/js/client/modules/org/arangodb/deploy.js index a5c9c0463a..5e4686d054 100644 --- a/js/client/modules/org/arangodb/deploy.js +++ b/js/client/modules/org/arangodb/deploy.js @@ -32,6 +32,8 @@ var arangodb = require("org/arangodb"); var fs = require("fs"); var internal = require("internal"); +var guessContentType = arangodb.guessContentType; + // ----------------------------------------------------------------------------- // --SECTION-- ArangoApp // ----------------------------------------------------------------------------- @@ -68,40 +70,6 @@ function ArangoApp (routing, description) { /// @{ //////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// -/// @brief guesses the content type -//////////////////////////////////////////////////////////////////////////////// - -function guessContentType (filename, content) { - var re = /.*\.([^\.]*)$/; - var match = re.exec(filename); - var extension; - - if (match === null) { - return "text/plain; charset=utf-8"; - } - - extension = match[1]; - - if (extension === "html") { - return "text/html; charset=utf-8"; - } - - if (extension === "xml") { - return "application/xml; charset=utf-8"; - } - - if (extension === "json") { - return "application/json; charset=utf-8"; - } - - if (extension === "js") { - return "application/x-javascript; charset=utf-8"; - } - - return "text/plain; charset=utf-8"; -} - //////////////////////////////////////////////////////////////////////////////// /// @brief normalizes a path //////////////////////////////////////////////////////////////////////////////// @@ -431,7 +399,7 @@ ArangoApp.prototype.uploadStaticPages = function (prefix, path) { filename = files[i]; } - contentType = guessContentType(file, content); + contentType = guessContentType(file); collection.save({ application: this._name, diff --git a/js/client/modules/org/arangodb/foxx.js b/js/client/modules/org/arangodb/foxx.js index 02926be93c..cb83cbe14a 100644 --- a/js/client/modules/org/arangodb/foxx.js +++ b/js/client/modules/org/arangodb/foxx.js @@ -40,13 +40,17 @@ var internal = require("internal"); //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// -/// @brief loads a foxx manifest +/// @brief install a fox application //////////////////////////////////////////////////////////////////////////////// -exports.loadManifest = function (name) { - var result = { manifest: name }; +exports.installApp = function (name, mount, options) { + var req = { + name: name, + mount: mount, + options: options + }; - return internal.arango.POST("/_admin/foxx/load", JSON.stringify(result)); + return internal.arango.POST("/_admin/foxx/install", JSON.stringify(req)); }; //////////////////////////////////////////////////////////////////////////////// diff --git a/js/common/bootstrap/module-fs.js b/js/common/bootstrap/module-fs.js index 10c1687be3..3afb53faed 100644 --- a/js/common/bootstrap/module-fs.js +++ b/js/common/bootstrap/module-fs.js @@ -90,7 +90,7 @@ fs.isFile = internal.isFile; //////////////////////////////////////////////////////////////////////////////// -/// @brief joins two paths (dummy) +/// @brief joins two paths (dummy implementation) //////////////////////////////////////////////////////////////////////////////// fs.join = function () { @@ -105,7 +105,7 @@ }; //////////////////////////////////////////////////////////////////////////////// -/// @brief lists all files and directory under a given path +/// @brief lists all files and directories under a given path //////////////////////////////////////////////////////////////////////////////// fs.listTree = internal.listTree; diff --git a/js/common/modules/org/arangodb-common.js b/js/common/modules/org/arangodb-common.js index f68ce8cb06..da16117789 100644 --- a/js/common/modules/org/arangodb-common.js +++ b/js/common/modules/org/arangodb-common.js @@ -34,6 +34,57 @@ var internal = require("internal"); // --SECTION-- module "arangodb" // ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// --SECTION-- public functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup ArangoShell +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief guesses the content type +//////////////////////////////////////////////////////////////////////////////// + +exports.guessContentType = function (filename) { + var re = /.*\.([^\.]*)$/; + var match = re.exec(filename); + var extension; + + if (match === null) { + return "text/plain; charset=utf-8"; + } + + extension = match[1]; + + if (extension === "html") { + return "text/html; charset=utf-8"; + } + + if (extension === "xml") { + return "application/xml; charset=utf-8"; + } + + if (extension === "json") { + return "application/json; charset=utf-8"; + } + + if (extension === "js") { + return "application/x-javascript; charset=utf-8"; + } + + if (extension === "css") { + return "text/css; charset=utf-8"; + } + + return "text/plain; charset=utf-8"; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + // ----------------------------------------------------------------------------- // --SECTION-- MODULE EXPORTS // ----------------------------------------------------------------------------- @@ -43,11 +94,19 @@ var internal = require("internal"); /// @{ //////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// @brief function "normalizeURL" +//////////////////////////////////////////////////////////////////////////////// + +exports.normalizeURL = internal.normalizeURL; + //////////////////////////////////////////////////////////////////////////////// /// @brief function "output" //////////////////////////////////////////////////////////////////////////////// -exports.output = function () { internal.output.apply(internal.output, arguments); }; +exports.output = function () { + internal.output.apply(internal.output, arguments); +}; //////////////////////////////////////////////////////////////////////////////// /// @brief function "print" diff --git a/js/server/modules/org/arangodb/actions.js b/js/server/modules/org/arangodb/actions.js index c6b0455ff0..5abcfd7356 100644 --- a/js/server/modules/org/arangodb/actions.js +++ b/js/server/modules/org/arangodb/actions.js @@ -236,14 +236,22 @@ function lookupCallbackActionCallback (route, action) { me = modelModule._package._environment = {}; if (cp !== "") { - me.appCollection = function (name) { + me.appCollectionName = function (name) { return cp + "_" + name; }; + + me.appCollection = function (name) { + return internal.db._collection(cp + "_" + name); + }; } else { - me.appCollection = function (name) { + me.appCollectionName = function (name) { return name; }; + + me.appCollection = function (name) { + return internal.db._collection(name); + }; } me.requireModel = function (path) { diff --git a/js/server/modules/org/arangodb/foxx-manager.js b/js/server/modules/org/arangodb/foxx-manager.js new file mode 100644 index 0000000000..8662ce80be --- /dev/null +++ b/js/server/modules/org/arangodb/foxx-manager.js @@ -0,0 +1,230 @@ +/*jslint indent: 2, nomen: true, maxlen: 120, sloppy: true, vars: true */ +/*global module, require, exports */ + +//////////////////////////////////////////////////////////////////////////////// +/// @brief Foxx application +/// +/// @file +/// +/// DISCLAIMER +/// +/// Copyright 2013 triagens 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 triAGENS GmbH, Cologne, Germany +/// +/// @author Dr. Frank Celler +/// @author Copyright 2013, triAGENS GmbH, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + +var internal = require("internal"); + +var arangodb = require("org/arangodb"); +var fs = require("fs"); + +// ----------------------------------------------------------------------------- +// --SECTION-- public functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup Foxx +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief builds one assets of an app +//////////////////////////////////////////////////////////////////////////////// + +function buildAssetContent (app, assets) { + var files; + var i; + var j; + var m; + var match; + var content; + + var reSub = /(.*)\/\*\*$/; + var reAll = /(.*)\/\*$/; + var rootDir = app._appDescription.path; + + files = []; + + for (j = 0; j < assets.length; ++j) { + var asset = assets[j]; + + match = reSub.exec(asset); + + if (match !== null) { + var m = fs.listTree(fs.join(rootDir, match[1])); + + for (i = 0; i < m.length; ++i) { + var filename = fs.join(rootDir, match[1], m[i]); + + if (fs.isFile(filename)) { + files.push(filename); + } + } + } + else { + match = reAll.exec(asset); + + if (match !== null) { + throw "not implemented"; + } + else { + files.push(fs.join(rootDir, asset)); + } + } + } + + content = ""; + + for (i = 0; i < files.length; ++i) { + var c = fs.read(files[i]); + + content += c; + } + + return content; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief installs the assets of an app +//////////////////////////////////////////////////////////////////////////////// + +function installAssets (app, mount) { + var desc; + var path; + var routes; + + desc = app._appDescription.manifest; + + routes = { + urlPrefix: mount, + routes: [] + }; + + if (desc.hasOwnProperty('assets')) { + for (path in desc.assets) { + if (desc.assets.hasOwnProperty(path)) { + var asset = desc.assets[path]; + var content = buildAssetContent(app, asset); + var normalized = arangodb.normalizeURL("/" + path); + var type = arangodb.guessContentType(normalized); + + var route = { + url: { match: normalized }, + content: { contentType: type, body: content } + }; + + routes.routes.push(route); + } + } + } + + arangodb.db._collection("_routing").save(routes); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// --SECTION-- public functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup Foxx +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief installs a FOXX application +//////////////////////////////////////////////////////////////////////////////// + +exports.installApp = function (name, mount, options) { + 'use strict'; + + var apps; + var description; + var i; + + var version = options && options.version; // TODO currently ignored + var prefix = options && options.collectionPrefix; + var context = {}; + var root = module.appRootModule(name); // TODO use version + + if (root === null) { + if (version === undefined) { + throw "cannot find application '" + name + "'"; + } + else { + throw "cannot find application '" + name + "' in version '" + version + "'"; + } + } + + description = root._appDescription.manifest; + + if (mount === "") { + mount = "/"; + } + else { + mount = internal.normalizeURL(mount); + } + + if (mount[0] !== "/") { + throw "mount point must be absolute"; + } + + if (prefix === undefined) { + context.collectionPrefix = mount.substr(1).replace(/\//g, "_"); + } + else { + context.collectionPrefix = prefix; + } + + context.name = description.name; + context.version = description.version; + context.mount = mount; + + apps = description.apps; + + for (i in apps) { + if (apps.hasOwnProperty(i)) { + var file = apps[i]; + + context.appMount = i; + context.prefix = internal.normalizeURL(mount + "/" + i); + + root.loadAppScript(root, file, context); + } + } + + installAssets(root, mount); + + return true; +}; + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +/// ----------------------------------------------------------------------------- +/// --SECTION-- END-OF-FILE +/// ----------------------------------------------------------------------------- + +/// Local Variables: +/// mode: outline-minor +/// outline-regexp: "/// @brief\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}\\|/\\*jslint" +/// End: diff --git a/js/server/modules/org/arangodb/foxx.js b/js/server/modules/org/arangodb/foxx.js index c845068934..dbdc7870de 100644 --- a/js/server/modules/org/arangodb/foxx.js +++ b/js/server/modules/org/arangodb/foxx.js @@ -705,65 +705,7 @@ FormatMiddleware = function (allowedFormats, defaultFormat) { /// We finish off with exporting FoxxApplication and the middlewares. /// Everything else will remain our secret. -//////////////////////////////////////////////////////////////////////////////// -/// @brief loads a manifest file -//////////////////////////////////////////////////////////////////////////////// - -exports.installApp = function (name, mount, options) { - 'use strict'; - var version = options && options.version, // TODO currently ignored - prefix = options && options.collectionPrefix, - context = {}, - apps, - description, - i, - root = module.appRootModule(name); // TODO use version - - if (root === null) { - if (version === undefined) { - throw "cannot find application '" + name + "'"; - } else { - throw "cannot find application '" + name + "' in version '" + version + "'"; - } - } - - description = root._appDescription; - - if (mount === "") { - mount = "/"; - } - else { - mount = INTERNAL.normalizeURL(mount); - } - - if (mount[0] !== "/") { - throw "mount point must be absolute"; - } - - if (prefix === undefined) { - context.collectionPrefix = mount.substr(1).replace(/\//g, "_"); - } else { - context.collectionPrefix = prefix; - } - - context.name = description.manifest.name; - context.version = description.manifest.version; - context.mount = mount; - - apps = root._appDescription.manifest.apps; - - for (i in apps) { - if (apps.hasOwnProperty(i)) { - var file = apps[i]; - - context.appMount = i; - context.prefix = INTERNAL.normalizeURL(mount + "/" + i); - - root.loadAppScript(root, file, context); - } - } -}; - +exports.installApp = require("org/arangodb/foxx-manager").installApp; exports.FoxxApplication = FoxxApplication; exports.BaseMiddleware = BaseMiddleware; exports.FormatMiddleware = FormatMiddleware; diff --git a/lib/V8/v8-utils.cpp b/lib/V8/v8-utils.cpp index d6dc8732ac..1b2253898f 100644 --- a/lib/V8/v8-utils.cpp +++ b/lib/V8/v8-utils.cpp @@ -812,7 +812,9 @@ static v8::Handle JS_IsFile (v8::Arguments const& argv) { } // return result - return scope.Close(TRI_ExistsFile(*name) ? v8::True() : v8::False()); + bool isFile = TRI_ExistsFile(*name) && ! TRI_IsDirectory(*name); + + return scope.Close(isFile ? v8::True() : v8::False()); } //////////////////////////////////////////////////////////////////////////////// From 557184631d8eb1fdfb813a1c118725c044c5d5ab Mon Sep 17 00:00:00 2001 From: Frank Celler Date: Tue, 26 Mar 2013 13:58:00 +0100 Subject: [PATCH 2/2] more fox-manager, added files section --- Documentation/UserManual/Actions.md | 10 +++--- arangod/V8Server/v8-actions.cpp | 36 +++++++++++++++++-- .../js/client/bootstrap/module-internal.js | 6 +++- html/admin/js/modules/org/arangodb-common.js | 12 +++++++ js/actions/api-system.js | 2 +- js/common/modules/org/arangodb-common.js | 12 +++++++ js/server/modules/org/arangodb/actions.js | 30 ++++++++++++++-- .../modules/org/arangodb/foxx-manager.js | 21 +++++++++++ lib/Rest/HttpResponse.cpp | 8 +++++ lib/Rest/HttpResponse.h | 6 ++++ lib/V8/v8-globals.h | 7 ++++ 11 files changed, 137 insertions(+), 13 deletions(-) diff --git a/Documentation/UserManual/Actions.md b/Documentation/UserManual/Actions.md index 0be7371dc2..fe862ad796 100644 --- a/Documentation/UserManual/Actions.md +++ b/Documentation/UserManual/Actions.md @@ -454,7 +454,7 @@ Install it as arangosh> db._routing.save({ ........> url: "/echo", - ........> action: { controller: "org/arangodb/actions", do: "echoRequest" } }); + ........> action: { do: "org/arangodb/actions/echoRequest" } }); Reload the routing and check @@ -489,8 +489,7 @@ You may also pass options to the called function: arangosh> db._routing.save({ ........> url: "/echo", ........> action: { - ........> controller: "org/arangodb/actions", - ........> do: "echoRequest", + ........> do: "org/arangodb/actions/echoRequest", ........> options: { "Hello": "World" } } }); You should now see the options in the result. @@ -651,8 +650,7 @@ Use the following for a permanent redirect: arangosh> db._routing.save({ ........> url: "/", ........> action: { - ........> controller: "org/arangodb/actions", - ........> do: "redirectRequest", + ........> do: "org/arangodb/actions/redirectRequest", ........> options: { ........> permanently: true, ........> destination: "http://somewhere.else/" } } }); @@ -707,7 +705,7 @@ should win in this case: arangosh> db._routing.save({ ........> middleware: [ - ........> { url: { match: "/*" }, action: { controller: "org/arangodb/actions", do: "logRequest" } } + ........> { url: { match: "/*" }, action: { do: "org/arangodb/actions/logRequest" } } ........> ] ........> }); diff --git a/arangod/V8Server/v8-actions.cpp b/arangod/V8Server/v8-actions.cpp index bc62d38cfd..453133ff1f 100644 --- a/arangod/V8Server/v8-actions.cpp +++ b/arangod/V8Server/v8-actions.cpp @@ -32,6 +32,8 @@ #include "Basics/StringUtils.h" #include "Basics/WriteLocker.h" #include "BasicsC/conversions.h" +#include "BasicsC/files.h" +#include "BasicsC/strings.h" #include "Logger/Logger.h" #include "Rest/HttpRequest.h" #include "Rest/HttpResponse.h" @@ -39,7 +41,6 @@ #include "V8/v8-utils.h" #include "V8Server/ApplicationV8.h" #include "V8Server/v8-vocbase.h" -#include "3rdParty/V8/include/v8.h" using namespace std; using namespace triagens::basics; @@ -517,6 +518,10 @@ static HttpResponse* ExecuteActionVocbase (TRI_vocbase_t* vocbase, response->setContentType(TRI_ObjectToString(res->Get(v8g->ContentTypeKey))); } + // ............................................................................. + // body + // ............................................................................. + if (res->Has(v8g->BodyKey)) { // check if we should apply result transformations // transformations turn the result from one type into another @@ -524,10 +529,11 @@ static HttpResponse* ExecuteActionVocbase (TRI_vocbase_t* vocbase, // putting a list of transformations into the res.transformations // array, e.g. res.transformations = [ "base64encode" ] v8::Handle val = res->Get(v8g->TransformationsKey); + if (val->IsArray()) { string out(TRI_ObjectToString(res->Get(v8g->BodyKey))); - v8::Handle transformations = val.As(); + for (uint32_t i = 0; i < transformations->Length(); i++) { v8::Handle transformator = transformations->Get(v8::Integer::New(i)); string name = TRI_ObjectToString(transformator); @@ -554,6 +560,31 @@ static HttpResponse* ExecuteActionVocbase (TRI_vocbase_t* vocbase, } } + // ............................................................................. + // body from file + // ............................................................................. + + else if (res->Has(v8g->BodyFromFileKey)) { + TRI_Utf8ValueNFC filename(TRI_UNKNOWN_MEM_ZONE, res->Get(v8g->BodyFromFileKey)); + size_t length; + char* content = TRI_SlurpFile(TRI_UNKNOWN_MEM_ZONE, *filename, &length); + + if (content != 0) { + response->body().appendText(content, length); + TRI_FreeString(TRI_UNKNOWN_MEM_ZONE, content); + } + else { + string msg = string("cannot read file '") + *filename + "': " + TRI_last_error(); + + response->body().appendText(msg.c_str()); + response->setResponseCode(HttpResponse::SERVER_ERROR); + } + } + + // ............................................................................. + // headers + // ............................................................................. + if (res->Has(v8g->HeadersKey)) { v8::Handle val = res->Get(v8g->HeadersKey); v8::Handle v8Headers = val.As(); @@ -740,6 +771,7 @@ void TRI_InitV8Actions (v8::Handle context, ApplicationV8* applicat // ............................................................................. v8g->BodyKey = v8::Persistent::New(TRI_V8_SYMBOL("body")); + v8g->BodyFromFileKey = v8::Persistent::New(TRI_V8_SYMBOL("bodyFromFile")); v8g->ContentTypeKey = v8::Persistent::New(TRI_V8_SYMBOL("contentType")); v8g->HeadersKey = v8::Persistent::New(TRI_V8_SYMBOL("headers")); v8g->ParametersKey = v8::Persistent::New(TRI_V8_SYMBOL("parameters")); diff --git a/html/admin/js/client/bootstrap/module-internal.js b/html/admin/js/client/bootstrap/module-internal.js index 30ad9a367d..db3445f968 100644 --- a/html/admin/js/client/bootstrap/module-internal.js +++ b/html/admin/js/client/bootstrap/module-internal.js @@ -149,7 +149,7 @@ //////////////////////////////////////////////////////////////////////////////// internal.appendCurlRequest = function (appender) { - return function (method, url, body) { + return function (method, url, body, headers) { var response; var curl; @@ -183,6 +183,10 @@ curl += "-X " + method + " "; } + if (headers !== undefined && headers !== "") { + curl += "--header \'" + headers + "\' "; + } + if (body !== undefined && body !== "") { curl += "--data @- "; } diff --git a/html/admin/js/modules/org/arangodb-common.js b/html/admin/js/modules/org/arangodb-common.js index e1991e7cce..0822f0a15f 100644 --- a/html/admin/js/modules/org/arangodb-common.js +++ b/html/admin/js/modules/org/arangodb-common.js @@ -79,6 +79,18 @@ exports.guessContentType = function (filename) { return "text/css; charset=utf-8"; } + if (extension === "png") { + return "image/png"; + } + + if (extension === "gif") { + return "image/gif"; + } + + if (extension === "jpg") { + return "image/jpg; + } + return "text/plain; charset=utf-8"; } diff --git a/js/actions/api-system.js b/js/actions/api-system.js index 5bc13190ac..f2c07758f8 100644 --- a/js/actions/api-system.js +++ b/js/actions/api-system.js @@ -59,7 +59,7 @@ function routing (req, res) { execute = function () { if (action.route === undefined) { - actions.resultNotImplemented(req, res, "unknown path '" + path + "'"); + actions.resultNotFound(req, res, arangodb.ERROR_HTTP_NOT_FOUND, "unknown path '" + path + "'"); return; } diff --git a/js/common/modules/org/arangodb-common.js b/js/common/modules/org/arangodb-common.js index da16117789..959b051366 100644 --- a/js/common/modules/org/arangodb-common.js +++ b/js/common/modules/org/arangodb-common.js @@ -78,6 +78,18 @@ exports.guessContentType = function (filename) { return "text/css; charset=utf-8"; } + if (extension === "png") { + return "image/png"; + } + + if (extension === "gif") { + return "image/gif"; + } + + if (extension === "jpg") { + return "image/jpg"; + } + return "text/plain; charset=utf-8"; } diff --git a/js/server/modules/org/arangodb/actions.js b/js/server/modules/org/arangodb/actions.js index 5abcfd7356..27fa52a191 100644 --- a/js/server/modules/org/arangodb/actions.js +++ b/js/server/modules/org/arangodb/actions.js @@ -30,6 +30,7 @@ var arangodb = require("org/arangodb"); var internal = require("internal"); +var fs = require("fs"); var console = require("console"); var moduleExists = function(name) { return module.exists; }; @@ -241,7 +242,7 @@ function lookupCallbackActionCallback (route, action) { }; me.appCollection = function (name) { - return internal.db._collection(cp + "_" + name); + return arangodb.db._collection(cp + "_" + name); }; } else { @@ -250,7 +251,7 @@ function lookupCallbackActionCallback (route, action) { }; me.appCollection = function (name) { - return internal.db._collection(name); + return arangodb.db._collection(name); }; } @@ -1108,7 +1109,7 @@ function reloadRouting () { // lookup all routes // ............................................................................. - routing = internal.db._collection("_routing"); + routing = arangodb.db._collection("_routing"); routes = routing.all(); // ............................................................................. @@ -1739,6 +1740,28 @@ function redirectRequest (req, res, options, next) { } } +//////////////////////////////////////////////////////////////////////////////// +/// @brief redirects a request +//////////////////////////////////////////////////////////////////////////////// + +function pathHandler (req, res, options, next) { + var filename; + var result; + + filename = fs.join(options.path, fs.join.apply(fs.join, req.suffix)); + + if (fs.exists(filename)) { + res.responseCode = exports.HTTP_OK; + res.contentType = arangodb.guessContentType(filename); + res.bodyFromFile = filename; + } + else { + res.responseCode = exports.HTTP_NOT_FOUND; + res.contentType = "text/plain"; + res.body = "cannot find file '" + filename + "'"; + } +} + //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// @@ -1785,6 +1808,7 @@ exports.resultException = resultException; exports.echoRequest = echoRequest; exports.logRequest = logRequest; exports.redirectRequest = redirectRequest; +exports.pathHandler = pathHandler; // some useful constants exports.COLLECTION = "collection"; diff --git a/js/server/modules/org/arangodb/foxx-manager.js b/js/server/modules/org/arangodb/foxx-manager.js index 6baef7141f..bacd5b31f7 100644 --- a/js/server/modules/org/arangodb/foxx-manager.js +++ b/js/server/modules/org/arangodb/foxx-manager.js @@ -133,6 +133,27 @@ function installAssets (app, mount) { } } + if (desc.hasOwnProperty('files')) { + for (path in desc.files) { + if (desc.files.hasOwnProperty(path)) { + var directory = desc.files[path]; + var normalized = arangodb.normalizeURL("/" + path); + + var route = { + url: { match: normalized + "/*" }, + action: { + "do": "org/arangodb/actions/pathHandler", + "options": { + path: fs.join(app._appDescription.path, directory) + } + } + }; + + routes.routes.push(route); + } + } + } + arangodb.db._collection("_routing").save(routes); } diff --git a/lib/Rest/HttpResponse.cpp b/lib/Rest/HttpResponse.cpp index 227a0d3c64..151b43c79b 100644 --- a/lib/Rest/HttpResponse.cpp +++ b/lib/Rest/HttpResponse.cpp @@ -215,6 +215,14 @@ HttpResponse::HttpResponseCode HttpResponse::responseCode () const { return _code; } +//////////////////////////////////////////////////////////////////////////////// +/// @brief sets the response code +//////////////////////////////////////////////////////////////////////////////// + +void HttpResponse::setResponseCode (HttpResponseCode code) { + _code = code; +} + //////////////////////////////////////////////////////////////////////////////// /// @brief returns the content length //////////////////////////////////////////////////////////////////////////////// diff --git a/lib/Rest/HttpResponse.h b/lib/Rest/HttpResponse.h index fa43807feb..5ae69070e4 100644 --- a/lib/Rest/HttpResponse.h +++ b/lib/Rest/HttpResponse.h @@ -205,6 +205,12 @@ namespace triagens { HttpResponseCode responseCode () const; +//////////////////////////////////////////////////////////////////////////////// +/// @brief sets the response code +//////////////////////////////////////////////////////////////////////////////// + + void setResponseCode (HttpResponseCode); + //////////////////////////////////////////////////////////////////////////////// /// @brief returns the content length //////////////////////////////////////////////////////////////////////////////// diff --git a/lib/V8/v8-globals.h b/lib/V8/v8-globals.h index 3fe8b6558e..015898d44f 100644 --- a/lib/V8/v8-globals.h +++ b/lib/V8/v8-globals.h @@ -89,6 +89,7 @@ typedef struct TRI_v8_global_s { RevKey(), ToKey(), BodyKey(), + BodyFromFileKey(), ContentTypeKey(), IsSystemKey(), IsVolatileKey(), @@ -294,6 +295,12 @@ typedef struct TRI_v8_global_s { v8::Persistent BodyKey; +//////////////////////////////////////////////////////////////////////////////// +/// @brief "bodyFromFile" key name +//////////////////////////////////////////////////////////////////////////////// + + v8::Persistent BodyFromFileKey; + //////////////////////////////////////////////////////////////////////////////// /// @brief "contentType" key name ////////////////////////////////////////////////////////////////////////////////