index.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. const fs = require('fs-extra');
  2. const path = require('path');
  3. const os = require('os');
  4. const express = require('express');
  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', 'config', 'data-dir', 'app-dir', 'lib-dir', 'inpx'];
  14. function showHelp(defaultConfig) {
  15. console.log(utils.versionText(defaultConfig));
  16. console.log(
  17. `Usage: ${defaultConfig.name} [options]
  18. Options:
  19. --help Print ${defaultConfig.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. --config=<filepath> Set config filename, default: <dataDir>/config.json
  23. --data-dir=<dirpath> (or --app-dir) Set application working directory, default: <execDir>/.${defaultConfig.name}
  24. --lib-dir=<dirpath> Set library directory, default: the same as ${defaultConfig.name} executable's
  25. --inpx=<filepath> Set INPX collection file, default: the one that found in library dir
  26. --recreate Force recreation of the search database on start
  27. --unsafe-filter Use filter config at your own risk
  28. `
  29. );
  30. }
  31. async function init() {
  32. argv = require('minimist')(process.argv.slice(2), {string: argvStrings});
  33. const argvDataDir = argv['data-dir'] || argv['app-dir'];
  34. const configFile = argv['config'];
  35. //config
  36. const configManager = new (require('./config'))();//singleton
  37. await configManager.init(argvDataDir, configFile);
  38. const defaultConfig = configManager.config;
  39. await configManager.load();
  40. config = configManager.config;
  41. branch = config.branch;
  42. //dirs
  43. config.dataDir = config.dataDir || argvDataDir || `${config.execDir}/.${config.name}`;
  44. config.tempDir = config.tempDir || `${config.dataDir}/tmp`;
  45. if (config.tempDir === '${OS}')
  46. config.tempDir = `${os.tmpdir()}/${config.name}`
  47. config.logDir = config.logDir || `${config.dataDir}/log`;
  48. config.publicDir = `${config.dataDir}/public`;
  49. config.publicFilesDir = `${config.dataDir}/public-files`;
  50. config.rootPathStatic = config.server.root || '';
  51. config.bookPathStatic = `${config.rootPathStatic}/book`;
  52. config.bookDir = `${config.publicFilesDir}/book`;
  53. configManager.config = config;
  54. await fs.ensureDir(config.dataDir);
  55. await fs.ensureDir(config.bookDir);
  56. await fs.ensureDir(config.tempDir);
  57. await fs.emptyDir(config.tempDir);
  58. //logger
  59. const appLogger = new (require('./core/AppLogger'))();//singleton
  60. await appLogger.init(config);
  61. log = appLogger.log;
  62. //cli
  63. if (argv.help) {
  64. showHelp(defaultConfig);
  65. ayncExit.exit(0);
  66. } else {
  67. log(utils.versionText(config));
  68. log('Initializing');
  69. }
  70. if (argv.host) {
  71. config.server.host = argv.host;
  72. }
  73. if (argv.port) {
  74. config.server.port = argv.port;
  75. }
  76. if (!config.remoteLib) {
  77. const libDir = argv['lib-dir'] || config.libDir;
  78. if (libDir) {
  79. if (await fs.pathExists(libDir)) {
  80. config.libDir = libDir;
  81. } else {
  82. throw new Error(`Directory "${libDir}" not exists`);
  83. }
  84. } else {
  85. config.libDir = config.execDir;
  86. }
  87. const inpxFile = argv.inpx || config.inpx;
  88. if (inpxFile) {
  89. if (await fs.pathExists(inpxFile)) {
  90. config.inpxFile = inpxFile;
  91. } else {
  92. throw new Error(`File "${inpxFile}" not found`);
  93. }
  94. } else {
  95. const inpxFiles = [];
  96. await utils.findFiles((file) => {
  97. if (path.extname(file) == '.inpx')
  98. inpxFiles.push(file);
  99. }, config.libDir, false);
  100. if (inpxFiles.length) {
  101. if (inpxFiles.length == 1) {
  102. config.inpxFile = inpxFiles[0];
  103. } else {
  104. throw new Error(`Found more than one .inpx files: \n${inpxFiles.join('\n')}`);
  105. }
  106. } else {
  107. throw new Error(`No .inpx files found here: ${config.libDir}`);
  108. }
  109. }
  110. } else {
  111. config.inpxFile = `${config.dataDir}/remote.inpx`;
  112. const RemoteLib = require('./core/RemoteLib');//singleton
  113. const remoteLib = new RemoteLib(config);
  114. await remoteLib.downloadInpxFile();
  115. }
  116. config.recreateDb = argv.recreate || false;
  117. config.inpxFilterFile = config.inpxFilterFile || `${path.dirname(config.configFile)}/filter.json`;
  118. config.allowUnsafeFilter = argv['unsafe-filter'] || config.allowUnsafeFilter || false;
  119. //web app
  120. if (branch !== 'development') {
  121. const createWebApp = require('./createWebApp');
  122. await createWebApp(config);
  123. }
  124. //log dirs
  125. for (const prop of ['configFile', 'dataDir', 'tempDir', 'logDir']) {
  126. log(`${prop}: ${config[prop]}`);
  127. }
  128. if (await fs.pathExists(config.inpxFilterFile))
  129. log(`inpxFilterFile: ${config.inpxFilterFile}`)
  130. }
  131. function logQueries(app) {
  132. app.use(function(req, res, next) {
  133. const start = Date.now();
  134. log(`${req.method} ${req.originalUrl} ${utils.cutString(req.body)}`);
  135. //log(`${JSON.stringify(req.headers, null, 2)}`)
  136. res.once('finish', () => {
  137. log(`${Date.now() - start}ms`);
  138. });
  139. next();
  140. });
  141. }
  142. async function main() {
  143. const log = new (require('./core/AppLogger'))().log;//singleton
  144. //server
  145. const app = express();
  146. const server = http.createServer(app);
  147. const wss = new WebSocket.Server({ server, maxPayload: config.maxPayloadSize*1024*1024 });
  148. let devModule = undefined;
  149. if (branch == 'development') {
  150. const devFileName = './dev.js'; //require ignored by pkg -50Mb executable size
  151. devModule = require(devFileName);
  152. devModule.webpackDevMiddleware(app);
  153. }
  154. if (devModule)
  155. devModule.logQueries(app);
  156. const opds = require('./core/opds');
  157. opds(app, config);
  158. const initStatic = require('./static');
  159. initStatic(app, config);
  160. const webAccess = new (require('./core/WebAccess'))(config);
  161. await webAccess.init();
  162. const { WebSocketController } = require('./controllers');
  163. new WebSocketController(wss, webAccess, config);
  164. if (config.logQueries) {
  165. logQueries(app);
  166. }
  167. if (devModule) {
  168. devModule.logErrors(app);
  169. } else {
  170. app.use(function(err, req, res, next) {// eslint-disable-line no-unused-vars
  171. log(LM_ERR, err.stack);
  172. res.sendStatus(500);
  173. });
  174. }
  175. server.listen(config.server.port, config.server.host, () => {
  176. config.server.ready = true;
  177. log(`Server accessible at http://127.0.0.1:${config.server.port} (listening on ${config.server.host}:${config.server.port})`);
  178. });
  179. }
  180. (async() => {
  181. try {
  182. await init();
  183. await main();
  184. } catch (e) {
  185. const mes = (branch == 'development' ? e.stack : e.message);
  186. if (log)
  187. log(LM_FATAL, mes);
  188. else
  189. console.error(mes);
  190. ayncExit.exit(1);
  191. }
  192. })();