index.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. const fs = require('fs-extra');
  2. const path = require('path');
  3. const express = require('express');
  4. const compression = require('compression');
  5. const http = require('http');
  6. const WebSocket = require ('ws');
  7. const ayncExit = new (require('./core/AsyncExit'))();
  8. const utils = require('./core/utils');
  9. const maxPayloadSize = 50;//in MB
  10. let log;
  11. let config;
  12. let argv;
  13. let branch = '';
  14. const argvStrings = ['lib-dir', 'inpx'];
  15. function showHelp() {
  16. console.log(utils.versionText(config));
  17. console.log(
  18. `Usage: ${config.name} [options]
  19. Options:
  20. --help Print ${config.name} command line options
  21. --lib-dir=<dirpath> Set library directory, default: the same as ${config.name} executable's
  22. --inpx=<filepath> Set INPX collection file, default: the one that found in library dir
  23. --recreate Force recreation of the search database on start
  24. `
  25. );
  26. }
  27. async function init() {
  28. argv = require('minimist')(process.argv.slice(2), {string: argvStrings});
  29. //config
  30. const configManager = new (require('./config'))();//singleton
  31. await configManager.init();
  32. //configManager.userConfigFile = argv.config;
  33. await configManager.load();
  34. config = configManager.config;
  35. branch = config.branch;
  36. //logger
  37. const appLogger = new (require('./core/AppLogger'))();//singleton
  38. await appLogger.init(config);
  39. log = appLogger.log;
  40. //dirs
  41. await fs.ensureDir(config.dataDir);
  42. await fs.ensureDir(config.tempDir);
  43. await fs.emptyDir(config.tempDir);
  44. //web app
  45. if (branch !== 'development') {
  46. const createWebApp = require('./createWebApp');
  47. await createWebApp(config);
  48. }
  49. //cli
  50. if (argv.help) {
  51. showHelp();
  52. ayncExit.exit(0);
  53. } else {
  54. log(utils.versionText(config));
  55. log('Initializing');
  56. }
  57. const libDir = argv['lib-dir'];
  58. if (libDir) {
  59. if (await fs.pathExists(libDir)) {
  60. config.libDir = libDir;
  61. } else {
  62. throw new Error(`Directory "${libDir}" not exists`);
  63. }
  64. } else {
  65. config.libDir = config.execDir;
  66. }
  67. if (argv.inpx) {
  68. if (await fs.pathExists(argv.inpx)) {
  69. config.inpxFile = argv.inpx;
  70. } else {
  71. throw new Error(`File "${argv.inpx}" not found`);
  72. }
  73. } else {
  74. const inpxFiles = [];
  75. await utils.findFiles((file) => {
  76. if (path.extname(file) == '.inpx')
  77. inpxFiles.push(file);
  78. }, config.libDir, false);
  79. if (inpxFiles.length) {
  80. if (inpxFiles.length == 1) {
  81. config.inpxFile = inpxFiles[0];
  82. } else {
  83. throw new Error(`Found more than one .inpx files: \n${inpxFiles.join('\n')}`);
  84. }
  85. } else {
  86. throw new Error(`No .inpx files found here: ${config.libDir}`);
  87. }
  88. }
  89. config.recreateDb = argv.recreate || false;
  90. //app
  91. const appDir = `${config.publicDir}/app`;
  92. const appNewDir = `${config.publicDir}/app_new`;
  93. if (await fs.pathExists(appNewDir)) {
  94. await fs.remove(appDir);
  95. await fs.move(appNewDir, appDir);
  96. }
  97. }
  98. async function main() {
  99. const log = new (require('./core/AppLogger'))().log;//singleton
  100. //server
  101. const app = express();
  102. const server = http.createServer(app);
  103. const wss = new WebSocket.Server({ server, maxPayload: maxPayloadSize*1024*1024 });
  104. const serverConfig = Object.assign({}, config, config.server);
  105. let devModule = undefined;
  106. if (serverConfig.branch == 'development') {
  107. const devFileName = './dev.js'; //require ignored by pkg -50Mb executable size
  108. devModule = require(devFileName);
  109. devModule.webpackDevMiddleware(app);
  110. }
  111. app.use(compression({ level: 1 }));
  112. //app.use(express.json({limit: `${maxPayloadSize}mb`}));
  113. if (devModule)
  114. devModule.logQueries(app);
  115. initStatic(app, config);
  116. const { WebSocketController } = require('./controllers');
  117. new WebSocketController(wss, config);
  118. if (devModule) {
  119. devModule.logErrors(app);
  120. } else {
  121. app.use(function(err, req, res, next) {// eslint-disable-line no-unused-vars
  122. log(LM_ERR, err.stack);
  123. res.sendStatus(500);
  124. });
  125. }
  126. server.listen(serverConfig.port, serverConfig.ip, () => {
  127. log(`Server is ready on http://${serverConfig.ip}:${serverConfig.port}`);
  128. });
  129. }
  130. function initStatic(app, config) {
  131. const WebWorker = require('./core/WebWorker');//singleton
  132. const webWorker = new WebWorker(config);
  133. //загрузка или восстановление файлов в /files, при необходимости
  134. app.use(async(req, res, next) => {
  135. if ((req.method !== 'GET' && req.method !== 'HEAD') ||
  136. !(req.path.indexOf('/files/') === 0)
  137. ) {
  138. return next();
  139. }
  140. const publicPath = `${config.publicDir}${req.path}`;
  141. let downFileName = '';
  142. //восстановим
  143. try {
  144. if (!await fs.pathExists(publicPath)) {
  145. downFileName = await webWorker.restoreBookFile(publicPath);
  146. } else {
  147. downFileName = await webWorker.getDownFileName(publicPath);
  148. }
  149. } catch(e) {
  150. //quiet
  151. }
  152. if (downFileName)
  153. res.downFileName = downFileName;
  154. return next();
  155. });
  156. //заголовки при отдаче
  157. const filesDir = `${config.publicDir}/files`;
  158. app.use(express.static(config.publicDir, {
  159. maxAge: '30d',
  160. setHeaders: (res, filePath) => {
  161. if (path.dirname(filePath) == filesDir) {
  162. res.set('Content-Encoding', 'gzip');
  163. if (res.downFileName)
  164. res.set('Content-Disposition', `inline; filename*=UTF-8''${encodeURIComponent(res.downFileName)}`);
  165. }
  166. },
  167. }));
  168. }
  169. (async() => {
  170. try {
  171. await init();
  172. await main();
  173. } catch (e) {
  174. if (log)
  175. log(LM_FATAL, (branch == 'development' ? e.stack : e.message));
  176. else
  177. console.error(branch == 'development' ? e.stack : e.message);
  178. ayncExit.exit(1);
  179. }
  180. })();