index.js 7.0 KB

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