@angstone/monostone
Version:
monolitic event-sourced framework
223 lines • 7.42 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const ast = require("@angstone/node-util");
const error_1 = require("../error");
const bodyParser = require("body-parser");
const express = require("express");
const http = require("http");
const _1 = require("./");
const tools_1 = require("../tools");
const AuthTools = require("../features/auth/tools");
const messages_1 = require("../features/auth/messages");
const net = require("net");
const state = {
apiPort: 0,
expressApp: express().use(bodyParser.json()),
httpServer: null,
};
const config = () => {
state.apiPort = +(process.env.API_PORT || 3002);
state.expressApp = express().use(bodyParser.json());
state.httpServer = null;
};
const loadFeatures = (features) => {
ast.log("creating routes");
routeSystem();
routeFeatures(features);
};
const routeSystem = () => {
ast.log("adding system routes");
const router = express.Router();
router.get("/ping", (req, res) => {
res.send("pong");
});
state.expressApp.use("/", router);
ast.log("system routes added");
};
/**
* Route each feature
* @param features loaded features
* @param eventReduced$ reduced events stream
* @param viewData stored data from views
*/
const routeFeatures = (features) => {
ast.log("adding the feature routes");
features.forEach((feature) => {
ast.log("found feature: " + feature.featureName);
const featureRouter = express.Router();
featureRouter.use(authMiddleware);
if (feature.commands) {
feature.commands.forEach((command) => {
ast.log(" found command: " + command.commandName);
featureRouter.post("/" + command.commandName, commandRequest(command));
ast.log(" routed command " + command.commandName);
});
}
if (feature.views) {
feature.views.forEach((view) => {
ast.log(" found view: " + view.viewName);
featureRouter.get("/" + view.viewName, viewRequest(view));
ast.log(" routed view " + view.viewName);
});
}
state.expressApp.use("/" + feature.featureName, featureRouter);
ast.log("routed feature " + feature.featureName);
});
};
/**
* Middleware to handle authentication
* @param req the request
* @param res the response
* @param next called at end
*/
const authMiddleware = (req, res, next) => {
const tokenHeader = req.get("token");
if (tokenHeader) {
AuthTools.decodeToken(tokenHeader).then((decoded) => {
req.token = decoded;
next();
}).catch((err) => {
res.status(401).send(err.message || err.msg || "unknown");
});
}
else {
next();
}
};
/**
* Generate the request for each command
* @param command [description]
* @param eventReduced$ stream of reduced events
* @return express function midlleware
*/
const commandRequest = (command) => {
return (req, res) => {
req.body.token = req.token;
tools_1.CommandTools.execute(command, req.body, _1.ReducerModule.getEventReducedStream())
.then((ans) => res.status(200).send(ans))
.catch((err) => {
ast.dev(err);
res.status(400).send(err);
});
};
};
/**
* Generate the request for each view
* @param view [description]
* @param viewTag [description]
* @param viewData [description]
* @return express function middleware
*/
const viewRequest = (view) => {
if (view.renderPrivate && !view.renderPublic) {
return (req, res) => {
if (req.token && view.renderPrivate) {
view.renderPrivate(_1.ViewModule.getViewData(view), req.token)
.then((ans) => res.status(200).send(ans))
.catch((err) => res.status(400).send(err));
}
else {
res.status(401).send(messages_1.messages.NO_TOKEN_PROVIDED);
}
};
}
else if (view.renderPrivate && view.renderPublic) {
return (req, res) => {
if (req.token && view.renderPrivate) {
view.renderPrivate(_1.ViewModule.getViewData(view), req.token)
.then((ans) => res.status(200).send(ans))
.catch((err) => res.status(400).send(err));
}
else if (view.renderPublic) {
view.renderPublic(_1.ViewModule.getViewData(view))
.then((ans) => res.status(200).send(ans))
.catch((err) => res.status(400).send(err));
}
};
}
else {
if (view.renderPublic) {
return (req, res) => {
if (view.renderPublic) {
view.renderPublic(_1.ViewModule.getViewData(view))
.then((ans) => res.status(200).send(ans))
.catch((err) => res.status(400).send(err));
}
};
}
else {
return (req, res) => res.status(200).send({});
}
}
};
const start = () => {
return new Promise((resolve, reject) => {
ast.log("testing http port use");
isPortTaken(state.apiPort).then((isTaken) => {
if (isTaken) {
reject("The port '" + state.apiPort + "' is already being used!'");
}
else {
state.httpServer = http.createServer(state.expressApp);
state.httpServer.once('listening', () => {
ast.log("Express Server listening on port " + state.apiPort);
resolve();
});
state.httpServer.on('error', (e) => {
error_1.error.fatal(e);
reject();
});
state.httpServer.listen.apply(state.httpServer, [{
host: 'localhost',
port: state.apiPort,
}]);
}
}).catch((err) => {
reject("failure when testing http server api port");
});
});
};
const isPortTaken = (port) => {
return new Promise((resolve) => {
const tester = net.createServer()
.once('error', (err) => {
if (err.code == 'EADDRINUSE')
resolve(true);
else
resolve(false);
})
.once('listening', () => {
tester.once('close', () => resolve(false))
.close();
})
.listen(port);
resolve(false);
});
};
const stop = () => {
return new Promise((resolve, reject) => {
if (state.httpServer) {
state.httpServer.close((error) => {
if (error)
reject(error);
else {
ast.info("http server closed");
resolve();
}
});
}
else {
ast.info("http server never started");
resolve();
}
});
};
const getExpressApp = () => state.expressApp;
const getApiPort = () => state.apiPort;
exports.PortalModule = Object.assign({}, _1.BasicModule, { config,
loadFeatures,
start,
stop,
getExpressApp,
getApiPort });
//# sourceMappingURL=portal.module.js.map