diff --git a/server/config/base.js b/server/config/base.js index d36123c..59a4744 100644 --- a/server/config/base.js +++ b/server/config/base.js @@ -2,7 +2,6 @@ const path = require('path'); const pckg = require('../../package.json'); const execDir = path.resolve(__dirname, '..'); -const dataDir = `${execDir}/.${pckg.name}`; module.exports = { branch: 'unknown', @@ -10,10 +9,6 @@ module.exports = { name: pckg.name, execDir, - dataDir, - tempDir: `${dataDir}/tmp`, - logDir: `${dataDir}/log`, - publicDir: `${dataDir}/public`, accessPassword: '', bookReadLink: '', @@ -27,6 +22,16 @@ module.exports = { webConfigParams: ['name', 'version', 'branch', 'bookReadLink'], + allowRemoteLib: false, + remoteLib: false, + /* + allowRemoteLib: true, // на сервере + remoteLib: { // на клиенте + accessPassword: '', + url: 'wss://remoteInpxWeb.ru', + }, + */ + server: { ip: '0.0.0.0', port: '22380', diff --git a/server/config/index.js b/server/config/index.js index 1422c31..857496f 100644 --- a/server/config/index.js +++ b/server/config/index.js @@ -1,4 +1,5 @@ const _ = require('lodash'); +const path = require('path'); const fs = require('fs-extra'); const branchFilename = __dirname + '/application_env'; @@ -12,6 +13,8 @@ const propsToSave = [ 'cacheCleanInterval', 'inpxCheckInterval', 'lowMemoryMode', + 'allowRemoteLib', + 'remoteLib', 'server', ]; @@ -29,7 +32,7 @@ class ConfigManager { return instance; } - async init() { + async init(dataDir) { if (this.inited) throw new Error('already inited'); @@ -44,10 +47,17 @@ class ConfigManager { process.env.NODE_ENV = this.branch; this.branchConfigFile = __dirname + `/${this.branch}.js`; - this._config = require(this.branchConfigFile); + const config = require(this.branchConfigFile); - await fs.ensureDir(this._config.dataDir); - this._userConfigFile = `${this._config.dataDir}/config.json`; + if (dataDir) { + config.dataDir = path.resolve(dataDir); + } else { + config.dataDir = `${config.execDir}/.${config.name}`; + } + + await fs.ensureDir(config.dataDir); + this._userConfigFile = `${config.dataDir}/config.json`; + this._config = config; this.inited = true; } diff --git a/server/config/production.js b/server/config/production.js index 69a9720..26a3eda 100644 --- a/server/config/production.js +++ b/server/config/production.js @@ -1,18 +1,12 @@ const path = require('path'); -const pckg = require('../../package.json'); const base = require('./base'); const execDir = path.dirname(process.execPath); -const dataDir = `${execDir}/.${pckg.name}`; module.exports = Object.assign({}, base, { branch: 'production', execDir, - dataDir, - tempDir: `${dataDir}/tmp`, - logDir: `${dataDir}/log`, - publicDir: `${dataDir}/public`, server: { ip: '0.0.0.0', diff --git a/server/controllers/WebSocketController.js b/server/controllers/WebSocketController.js index f274efe..e42879e 100644 --- a/server/controllers/WebSocketController.js +++ b/server/controllers/WebSocketController.js @@ -1,5 +1,6 @@ -const WebSocket = require ('ws'); +const fs = require('fs-extra'); const _ = require('lodash'); +const WebSocket = require ('ws'); const WorkerState = require('../core/WorkerState');//singleton const WebWorker = require('../core/WebWorker');//singleton @@ -83,6 +84,9 @@ class WebSocketController { case 'get-book-link': await this.getBookLink(req, ws); break; + case 'get-inpx-file': + await this.getInpxFile(req, ws); break; + default: throw new Error(`Action not found: ${req.action}`); } @@ -162,6 +166,16 @@ class WebSocketController { this.send(result, req, ws); } + + async getInpxFile(req, ws) { + if (!this.config.allowRemoteLib) + throw new Error('Remote lib access disabled'); + + const data = await fs.readFile(this.config.inpxFile, 'base64'); + + this.send({data}, req, ws); + } + } module.exports = WebSocketController; diff --git a/server/core/RemoteLib.js b/server/core/RemoteLib.js new file mode 100644 index 0000000..6675b45 --- /dev/null +++ b/server/core/RemoteLib.js @@ -0,0 +1,56 @@ +const fs = require('fs-extra'); +const utils = require('./utils'); + +const WebSocketConnection = require('./WebSocketConnection'); + +//singleton +let instance = null; + +class RemoteLib { + constructor(config) { + if (!instance) { + this.config = config; + + this.wsc = new WebSocketConnection(config.remoteLib.url, 10, 30, {rejectUnauthorized: false}); + if (config.remoteLib.accessPassword) + this.accessToken = utils.getBufHash(config.remoteLib.accessPassword, 'sha256', 'hex'); + + this.inpxFile = `${config.tempDir}/${utils.randomHexString(20)}`; + this.lastUpdateTime = 0; + + instance = this; + } + + return instance; + } + + async wsRequest(query) { + if (this.accessToken) + query.accessToken = this.accessToken; + + const response = await this.wsc.message( + await this.wsc.send(query, 60), + 60 + ); + + if (response.error) + throw new Error(response.error); + + return response; + } + + async getInpxFile(getPeriod = 0) { + if (getPeriod && Date.now() - this.lastUpdateTime < getPeriod) + return this.inpxFile; + + const response = await this.wsRequest({action: 'get-inpx-file'}); + + await fs.writeFile(this.inpxFile, response.data, 'base64'); + + this.lastUpdateTime = Date.now(); + + return this.inpxFile; + } +} + +module.exports = RemoteLib; \ No newline at end of file diff --git a/server/core/WebWorker.js b/server/core/WebWorker.js index f8ab542..3ece346 100644 --- a/server/core/WebWorker.js +++ b/server/core/WebWorker.js @@ -5,11 +5,12 @@ const zlib = require('zlib'); const _ = require('lodash'); const ZipReader = require('./ZipReader'); -const WorkerState = require('./WorkerState'); +const WorkerState = require('./WorkerState');//singleton const { JembaDbThread } = require('jembadb'); const DbCreator = require('./DbCreator'); const DbSearcher = require('./DbSearcher'); const InpxHashCreator = require('./InpxHashCreator'); +const RemoteLib = require('./RemoteLib');//singleton const ayncExit = new (require('./AsyncExit'))(); const log = new (require('./AppLogger'))().log;//singleton @@ -505,6 +506,11 @@ class WebWorker { while (this.myState != ssNormal) await utils.sleep(1000); + if (this.config.remoteLib) { + const remoteLib = new RemoteLib(this.config); + await remoteLib.getInpxFile(60*1000); + } + const newInpxHash = await inpxHashCreator.getHash(); const dbConfig = await this.dbConfig(); diff --git a/server/index.js b/server/index.js index 53d1ba1..5f03468 100644 --- a/server/index.js +++ b/server/index.js @@ -6,16 +6,18 @@ const compression = require('compression'); const http = require('http'); const WebSocket = require ('ws'); -const ayncExit = new (require('./core/AsyncExit'))(); +const RemoteLib = require('./core/RemoteLib');//singleton const utils = require('./core/utils'); +const ayncExit = new (require('./core/AsyncExit'))(); + const maxPayloadSize = 50;//in MB let log; let config; let argv; let branch = ''; -const argvStrings = ['lib-dir', 'inpx']; +const argvStrings = ['lib-dir', 'app-dir', 'inpx']; function showHelp() { console.log(utils.versionText(config)); @@ -24,6 +26,7 @@ function showHelp() { Options: --help Print ${config.name} command line options + --app-dir= Set application working directory, default: "/.${config.name}" --lib-dir= Set library directory, default: the same as ${config.name} executable's --inpx= Set INPX collection file, default: the one that found in library dir --recreate Force recreation of the search database on start @@ -33,25 +36,30 @@ Options: async function init() { argv = require('minimist')(process.argv.slice(2), {string: argvStrings}); + const dataDir = argv['app-dir']; //config const configManager = new (require('./config'))();//singleton - await configManager.init(); - //configManager.userConfigFile = argv.config; + await configManager.init(dataDir); await configManager.load(); config = configManager.config; branch = config.branch; + //dirs + config.tempDir = `${config.dataDir}/tmp`; + config.logDir = `${config.dataDir}/log`; + config.publicDir = `${config.dataDir}/public`; + configManager.config = config; + + await fs.ensureDir(config.dataDir); + await fs.ensureDir(config.tempDir); + await fs.emptyDir(config.tempDir); + //logger const appLogger = new (require('./core/AppLogger'))();//singleton await appLogger.init(config); log = appLogger.log; - //dirs - await fs.ensureDir(config.dataDir); - await fs.ensureDir(config.tempDir); - await fs.emptyDir(config.tempDir); - //web app if (branch !== 'development') { const createWebApp = require('./createWebApp'); @@ -67,52 +75,49 @@ async function init() { log('Initializing'); } - const libDir = argv['lib-dir']; - if (libDir) { - if (await fs.pathExists(libDir)) { - config.libDir = libDir; - } else { - throw new Error(`Directory "${libDir}" not exists`); - } - } else { - config.libDir = config.execDir; - } - - if (argv.inpx) { - if (await fs.pathExists(argv.inpx)) { - config.inpxFile = argv.inpx; - } else { - throw new Error(`File "${argv.inpx}" not found`); - } - } else { - const inpxFiles = []; - await utils.findFiles((file) => { - if (path.extname(file) == '.inpx') - inpxFiles.push(file); - }, config.libDir, false); - - if (inpxFiles.length) { - if (inpxFiles.length == 1) { - config.inpxFile = inpxFiles[0]; + if (!config.remoteLib) { + const libDir = argv['lib-dir']; + if (libDir) { + if (await fs.pathExists(libDir)) { + config.libDir = libDir; } else { - throw new Error(`Found more than one .inpx files: \n${inpxFiles.join('\n')}`); + throw new Error(`Directory "${libDir}" not exists`); } } else { - throw new Error(`No .inpx files found here: ${config.libDir}`); + config.libDir = config.execDir; } + + if (argv.inpx) { + if (await fs.pathExists(argv.inpx)) { + config.inpxFile = argv.inpx; + } else { + throw new Error(`File "${argv.inpx}" not found`); + } + } else { + const inpxFiles = []; + await utils.findFiles((file) => { + if (path.extname(file) == '.inpx') + inpxFiles.push(file); + }, config.libDir, false); + + if (inpxFiles.length) { + if (inpxFiles.length == 1) { + config.inpxFile = inpxFiles[0]; + } else { + throw new Error(`Found more than one .inpx files: \n${inpxFiles.join('\n')}`); + } + } else { + throw new Error(`No .inpx files found here: ${config.libDir}`); + } + } + } else { + const remoteLib = new RemoteLib(config); + config.inpxFile = await remoteLib.getInpxFile(); } config.recreateDb = argv.recreate || false; config.inpxFilterFile = `${config.execDir}/inpx-web-filter.json`; config.allowUnsafeFilter = argv['unsafe-filter'] || false; - - //app - const appDir = `${config.publicDir}/app`; - const appNewDir = `${config.publicDir}/app_new`; - if (await fs.pathExists(appNewDir)) { - await fs.remove(appDir); - await fs.move(appNewDir, appDir); - } } async function main() { @@ -124,10 +129,8 @@ async function main() { const server = http.createServer(app); const wss = new WebSocket.Server({ server, maxPayload: maxPayloadSize*1024*1024 }); - const serverConfig = Object.assign({}, config, config.server); - let devModule = undefined; - if (serverConfig.branch == 'development') { + if (branch == 'development') { const devFileName = './dev.js'; //require ignored by pkg -50Mb executable size devModule = require(devFileName); devModule.webpackDevMiddleware(app); @@ -152,6 +155,7 @@ async function main() { }); } + const serverConfig = config.server; server.listen(serverConfig.port, serverConfig.ip, () => { log(`Server is ready on http://${serverConfig.ip}:${serverConfig.port}`); });