routerUtils.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. const mime = require('mime-types');
  2. const path = require('path');
  3. const fs = require('fs');
  4. const Security = require('./security.js');
  5. const CONFIG = require('./config.js');
  6. function RouterUtils(app) {
  7. this.app = app;
  8. }
  9. RouterUtils.prototype.httpResponse = function(res, code, response) {
  10. res.writeHead(code);
  11. res.end(response);
  12. return true;
  13. }
  14. RouterUtils.prototype.requireLogin =function(req, res) {
  15. if (Security.isLoggedUser(req.cookies))
  16. {
  17. req.loggedUser = Security.getLoggedUser(req.cookies);
  18. req.loggedSession = Security.getSessionId(req.cookies);
  19. return false;
  20. }
  21. this.redirect(res, '/login?page='+encodeURIComponent(req.url));
  22. return true;
  23. };
  24. RouterUtils.prototype.onApiRequest = function(req, res) {
  25. this.onRequest(req);
  26. req.sessionObj = Security.getSessionObj(req.cookies);
  27. if (req.sessionObj === null) {
  28. const log = Security.createSession(req);
  29. res.setHeader("Set-Cookie", Security.SESSION_COOKIE +'='+log.key);
  30. req.sessionObj = log.data;
  31. }
  32. };
  33. RouterUtils.prototype.apiRequireLogin =function(req, res, validTokens) {
  34. if (Security.isLoggedUser(req.cookies))
  35. {
  36. req.loggedUser = Security.getLoggedUser(req.cookies);
  37. req.loggedSession = Security.getSessionId(req.cookies);
  38. return false;
  39. }
  40. if (validTokens && req.body?.apiKey && validTokens.indexOf(req.body?.apiKey) >= 0)
  41. {
  42. req.loggedUser = req.body.apiKey;
  43. req.loggedSession = "";
  44. return false;
  45. }
  46. return this.httpResponse(res, 403, "Unauthorized Access");
  47. };
  48. RouterUtils.prototype.redirect = function(res, url) {
  49. res.writeHead(302, { Location: url });
  50. res.end();
  51. }
  52. RouterUtils.prototype.prepareCookie = function(req) {
  53. req.cookies = {};
  54. let arr = ((req.headers?.cookie || "").split(';').map(i => i.split('=', 2))).forEach(i => { req.cookies[i[0].trim()] = decodeURIComponent(i[1]).trim();});
  55. }
  56. RouterUtils.prototype.onRequest = function(req) {
  57. this.prepareCookie(req);
  58. }
  59. RouterUtils.prototype.readPostBody = function(req, res) {
  60. const now = Math.floor(Date.now() / 1000);
  61. return new Promise((ok, ko) => {
  62. if (req.headers['content-type'] !== 'application/json') {
  63. console.error("Unexpected input from query: wrong Content-Type");
  64. ko();
  65. return;
  66. }
  67. let data = null;
  68. try {
  69. data = JSON.parse(req.body.data);
  70. } catch (e) {
  71. console.error("Unexpected input from query: invalid JSON");
  72. ko();
  73. return;
  74. }
  75. if (!data.time || Math.abs(now - data.time) > 3) {
  76. console.error("Unexpected input from query: Invalid time");
  77. ko();
  78. return;
  79. }
  80. if (!data.hostname) {
  81. console.error("Unexpected input from query: missing hostname");
  82. ko();
  83. return;
  84. }
  85. req.data = data;
  86. ok();
  87. });
  88. }
  89. RouterUtils.prototype.apiError = function(res) {
  90. res.writeHead(400, { "Content-Type": "application/json"});
  91. res.end();
  92. }
  93. RouterUtils.prototype.jsonResponse = function(res, data) {
  94. res.writeHead(200, { "Content-Type": "application/json"});
  95. if (typeof data !== 'string')
  96. data = JSON.stringify(data);
  97. res.end(data);
  98. }
  99. RouterUtils.prototype.onPageNotFound = function(res) {
  100. return this.httpResponse(res, 404, "Page not found...");
  101. }
  102. RouterUtils.prototype.staticServe = async function(res, filePath) {
  103. return new Promise((ok, ko) => {
  104. try {
  105. const stream = fs.createReadStream(filePath);
  106. let onError = false;
  107. stream.once('error', err => {
  108. ko(err);
  109. onError = true;
  110. });
  111. const fileSize = fs.statSync(filePath)?.size || undefined;
  112. if (!stream || !fileSize || onError) {
  113. console.error("RouterUtils::staticGet", filePath, err);
  114. this.httpResponse(res, 500, "Internal Server Error");
  115. return ko(err);
  116. }
  117. res.writeHead(200, {
  118. "Content-Type": mime.contentType(path.basename(filePath)),
  119. "Content-Length": fileSize
  120. });
  121. stream.pipe(res);
  122. stream.once('end', () => ok());
  123. } catch (err) {
  124. ko(err);
  125. }
  126. });
  127. }
  128. RouterUtils.prototype.staticGet = function(app, url, staticResources) {
  129. app.router.get(url, (req, res) => {
  130. app.routerUtils.staticServe(res, staticResources).catch(err => {
  131. app.routerUtils.onPageNotFound(res);
  132. });
  133. });
  134. }
  135. RouterUtils.encodeUrlComponent = function(input) {
  136. return btoa(input).replaceAll('=', '-').replaceAll('+', '_');
  137. }
  138. RouterUtils.prototype.encodeUrlComponent = function(input) {
  139. return RouterUtils.encodeUrlComponent(input);
  140. }
  141. RouterUtils.decodeUrlComponent = function(input) {
  142. return atob(input.replaceAll('-', '=').replaceAll('_', '+'));
  143. }
  144. RouterUtils.prototype.decodeUrlComponent = function(input) {
  145. return RouterUtils.decodeUrlComponent(input);
  146. }
  147. RouterUtils.prototype.commonRenderInfos = function() {
  148. return {
  149. };
  150. }
  151. module.exports = { RouterUtils: RouterUtils, encodeUrlComponent: RouterUtils.encodeUrlComponent, decodeUrlComponent: RouterUtils.decodeUrlComponent };