index.js 7.7 KB

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