actionhero
Version:
actionhero.js is a multi-transport API Server with integrated cluster capabilities and delayed tasks
168 lines (167 loc) • 7.67 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Routes = void 0;
const path = require("path");
const index_1 = require("../index");
/**
* Contains routing options for web clients. Can associate routes with actions or files.
*/
class Routes extends index_1.Initializer {
constructor() {
super();
this.name = "routes";
this.loadPriority = 500;
}
async initialize(config) {
index_1.api.routes = {
routes: {},
verbs: ["head", "get", "post", "put", "patch", "delete"],
};
index_1.api.routes.processRoute = (connection, pathParts) => {
if (connection.params.action === undefined ||
index_1.api.actions.actions[connection.params.action] === undefined) {
let method = connection.rawConnection.method.toLowerCase();
if (method === "head" && !index_1.api.routes.routes.head) {
method = "get";
}
for (const i in index_1.api.routes.routes[method]) {
const route = index_1.api.routes.routes[method][i];
const match = index_1.api.routes.matchURL(pathParts, route.path, route.matchTrailingPathParts);
if (match.match === true) {
if (route.apiVersion) {
connection.params.apiVersion =
connection.params.apiVersion || route.apiVersion;
}
for (const param in match.params) {
try {
const decodedName = decodeURIComponent(param.replace(/\+/g, " "));
const decodedValue = decodeURIComponent(match.params[param].replace(/\+/g, " "));
connection.params[decodedName] = decodedValue;
}
catch (e) {
// malformed URL
}
}
connection.matchedRoute = route;
if (route.dir) {
const requestedFile = connection.rawConnection.parsedURL.pathname.substring(route.path.length, connection.rawConnection.parsedURL.pathname.length);
connection.params.file = path.normalize(route.dir + "/" + requestedFile);
}
else {
connection.params.action = route.action;
}
break;
}
}
}
};
index_1.api.routes.matchURL = (pathParts, match, matchTrailingPathParts) => {
const response = { match: false, params: {} };
const matchParts = match.split("/");
let regexp = "";
let variable = "";
if (matchParts[0] === "") {
matchParts.splice(0, 1);
}
if (matchParts[matchParts.length - 1] === "") {
matchParts.pop();
}
if (matchParts.length !== pathParts.length &&
matchTrailingPathParts !== true) {
return response;
}
for (const i in matchParts) {
const matchPart = matchParts[i];
let pathPart = pathParts[i];
if (matchTrailingPathParts === true &&
parseInt(i) === matchParts.length - 1) {
for (const j in pathParts) {
if (j > i) {
pathPart = pathPart + "/" + pathParts[j];
}
}
}
if (!pathPart) {
return response;
}
if (matchPart.indexOf(":") >= 0) {
const trimmedMatchParts = matchPart.split(":");
const trimmedMatchPart = trimmedMatchParts[trimmedMatchParts.length - 1];
const replacement = trimmedMatchParts[trimmedMatchParts.length - 2];
if (replacement) {
pathPart = pathPart.replace(replacement, "");
}
if (trimmedMatchPart.indexOf("(") < 0) {
variable = trimmedMatchPart;
response.params[variable] = pathPart;
}
else {
variable = trimmedMatchPart.replace(":", "").split("(")[0];
regexp = trimmedMatchPart.substring(trimmedMatchPart.indexOf("(") + 1, trimmedMatchPart.length - 1);
const matches = pathPart.match(new RegExp(regexp, "g"));
if (matches) {
response.params[variable] = pathPart;
}
else {
return response;
}
}
}
else {
if (pathPart === null ||
pathPart === undefined ||
pathParts[i].toLowerCase() !== matchPart.toLowerCase()) {
return response;
}
}
}
response.match = true;
return response;
};
// load in the routes file
index_1.api.routes.loadRoutes = (rawRoutes) => {
let counter = 0;
index_1.api.routes.verbs.forEach((verb) => {
index_1.api.routes.routes[verb] = index_1.api.routes.routes[verb] || [];
});
if (!rawRoutes) {
if (config.routes) {
rawRoutes = config.routes;
}
}
let v;
let verb;
for (const i in rawRoutes) {
const method = i.toLowerCase();
for (const j in rawRoutes[i]) {
const thisRoute = rawRoutes[i][j];
if (method === "all") {
for (v in index_1.api.routes.verbs) {
verb = index_1.api.routes.verbs[v];
index_1.route.registerRoute(verb, thisRoute.path, thisRoute.action, thisRoute.apiVersion, thisRoute.matchTrailingPathParts, thisRoute.dir);
}
}
else {
index_1.route.registerRoute(method, thisRoute.path, thisRoute.action, thisRoute.apiVersion, thisRoute.matchTrailingPathParts, thisRoute.dir);
}
counter++;
}
}
index_1.api.params.postVariables = index_1.utils.arrayUnique(index_1.api.params.postVariables);
if (config.servers.web && config.servers.web.simpleRouting === true) {
const simplePaths = [];
for (const action in index_1.api.actions.actions) {
simplePaths.push("/" + action);
for (v in index_1.api.routes.verbs) {
verb = index_1.api.routes.verbs[v];
index_1.route.registerRoute(verb, "/" + action, action, null);
}
}
index_1.log(`${simplePaths.length} simple routes loaded from action names`, "debug");
index_1.log("routes:", "debug", index_1.api.routes.routes);
}
};
index_1.api.routes.loadRoutes();
}
}
exports.Routes = Routes;