Работа над api reader, worker
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
const log = require('../core/getLogger').getLog();
|
||||
const BaseController = require('./BaseController');
|
||||
const log = require('../core/getLogger').getLog();
|
||||
const _ = require('lodash');
|
||||
|
||||
class MiscController extends BaseController {
|
||||
|
||||
39
server/controllers/ReaderController.js
Normal file
39
server/controllers/ReaderController.js
Normal file
@@ -0,0 +1,39 @@
|
||||
const BaseController = require('./BaseController');
|
||||
const ReaderWorker = require('../core/ReaderWorker');
|
||||
const workerState = require('../core/workerState');
|
||||
//const log = require('../core/getLogger').getLog();
|
||||
//const _ = require('lodash');
|
||||
|
||||
class ReaderController extends BaseController {
|
||||
constructor(connPool, config) {
|
||||
super(connPool, config);
|
||||
this.readerWorker = new ReaderWorker(config);
|
||||
}
|
||||
|
||||
async loadBook(req, res) {
|
||||
const request = req.body;
|
||||
let error = '';
|
||||
try {
|
||||
if (!request.type || !(request.type == 'url' || request.type == 'file'))
|
||||
throw new Error(`key 'type' is wrong`);
|
||||
|
||||
if (request.type == 'file')
|
||||
throw new Error(`file loading is not supported yet`);
|
||||
|
||||
if (request.type == 'url') {
|
||||
if (!request.url)
|
||||
throw new Error(`key 'url' is empty`);
|
||||
const workerId = this.readerWorker.loadBookUrl(request.url);
|
||||
const state = workerState.getState(workerId);
|
||||
return (state ? state : {});
|
||||
}
|
||||
} catch (e) {
|
||||
error = e.message;
|
||||
}
|
||||
//bad request
|
||||
res.status(400).send({error});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ReaderController;
|
||||
23
server/controllers/WorkerController.js
Normal file
23
server/controllers/WorkerController.js
Normal file
@@ -0,0 +1,23 @@
|
||||
const BaseController = require('./BaseController');
|
||||
const workerState = require('../core/workerState');
|
||||
|
||||
class WorkerController extends BaseController {
|
||||
async getState(req, res) {
|
||||
const request = req.body;
|
||||
let error = '';
|
||||
try {
|
||||
if (!request.workerId)
|
||||
throw new Error(`key 'workerId' is wrong`);
|
||||
|
||||
const state = workerState.getState(request.workerId);
|
||||
return (state ? state : {});
|
||||
} catch (e) {
|
||||
error = e.message;
|
||||
}
|
||||
//bad request
|
||||
res.status(400).send({error});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = WorkerController;
|
||||
@@ -1,3 +1,5 @@
|
||||
module.exports = {
|
||||
MiscController: require('./MiscController'),
|
||||
ReaderController: require('./ReaderController'),
|
||||
WorkerController: require('./WorkerController'),
|
||||
}
|
||||
46
server/core/ReaderWorker.js
Normal file
46
server/core/ReaderWorker.js
Normal file
@@ -0,0 +1,46 @@
|
||||
const workerState = require('./workerState');
|
||||
const utils = require('./utils');
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const util = require('util');
|
||||
const stream = require('stream');
|
||||
const pipeline = util.promisify(stream.pipeline);
|
||||
const download = require('download');
|
||||
|
||||
class ReaderWorker {
|
||||
constructor(config) {
|
||||
this.config = config;
|
||||
this.tempDownloadDir = `${config.tempDir}/download`;
|
||||
fs.ensureDirSync(this.tempDownloadDir);
|
||||
}
|
||||
|
||||
async loadBook(wState, url) {
|
||||
try {
|
||||
wState.set({state: 'download', step: 1, totalSteps: 3, url});
|
||||
|
||||
const tempFilename = utils.randomHexString(30);
|
||||
const d = download(url);
|
||||
d.on('downloadProgress', progress => {
|
||||
wState.set({progress: Math.round(progress.percent*100)});
|
||||
})
|
||||
|
||||
await pipeline(d, fs.createWriteStream(`${this.tempDownloadDir}/${tempFilename}`));
|
||||
|
||||
wState.finish({step: 3, file: tempFilename});
|
||||
} catch (e) {
|
||||
wState.set({state: 'error', error: e.message});
|
||||
}
|
||||
}
|
||||
|
||||
loadBookUrl(url) {
|
||||
const workerId = workerState.generateWorkerId();
|
||||
const wState = workerState.getControl(workerId);
|
||||
wState.set({state: 'start'});
|
||||
|
||||
this.loadBook(wState, url);
|
||||
|
||||
return workerId;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ReaderWorker;
|
||||
@@ -1,7 +1,14 @@
|
||||
const crypto = require('crypto');
|
||||
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
function randomHexString(len) {
|
||||
return crypto.randomBytes(len).toString('hex')
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
sleep
|
||||
sleep,
|
||||
randomHexString
|
||||
};
|
||||
56
server/core/workerState.js
Normal file
56
server/core/workerState.js
Normal file
@@ -0,0 +1,56 @@
|
||||
const utils = require('./utils');
|
||||
|
||||
const cleanInterval = 3600; //sec
|
||||
const cleanAfterLastModified = cleanInterval - 60; //sec
|
||||
|
||||
class WorkerState {
|
||||
constructor() {
|
||||
this.states = {};
|
||||
setTimeout(this.cleanStates.bind(this), cleanInterval*1000);
|
||||
}
|
||||
|
||||
generateWorkerId() {
|
||||
return utils.randomHexString(20);
|
||||
}
|
||||
|
||||
getControl(workerId) {
|
||||
return {
|
||||
set: state => this.setState(workerId, state),
|
||||
finish: state => this.finishState(workerId, state),
|
||||
get: workerId => this.getState(workerId),
|
||||
};
|
||||
}
|
||||
|
||||
setState(workerId, state) {
|
||||
this.states[workerId] = Object.assign({}, this.states[workerId], state, {
|
||||
workerId,
|
||||
lastModified: Date.now()
|
||||
});
|
||||
}
|
||||
|
||||
finishState(workerId, state) {
|
||||
this.states[workerId] = Object.assign({}, this.states[workerId], state, {
|
||||
workerId,
|
||||
state: 'finish',
|
||||
lastModified: Date.now()
|
||||
});
|
||||
}
|
||||
|
||||
getState(workerId) {
|
||||
return this.states[workerId];
|
||||
}
|
||||
|
||||
cleanStates() {
|
||||
const now = Date.now();
|
||||
for (let workerID in this.states) {
|
||||
if ((now - this.states[workerID].lastModified) >= cleanAfterLastModified*1000) {
|
||||
delete this.states[workerID];
|
||||
}
|
||||
}
|
||||
setTimeout(this.cleanStates.bind(this), cleanInterval*1000);
|
||||
}
|
||||
}
|
||||
|
||||
const workerState = new WorkerState();
|
||||
|
||||
module.exports = workerState;
|
||||
@@ -2,14 +2,18 @@ const c = require('./controllers');
|
||||
|
||||
function initRoutes(app, connPool, config) {
|
||||
const misc = new c.MiscController(connPool, config);
|
||||
const reader = new c.ReaderController(connPool, config);
|
||||
const worker = new c.WorkerController(connPool, config);
|
||||
|
||||
//access
|
||||
const [all, normal, site, reader, omnireader] = // eslint-disable-line no-unused-vars
|
||||
const [aAll, aNormal, aSite, aReader, aOmnireader] = // eslint-disable-line no-unused-vars
|
||||
[config.mode, 'normal', 'site', 'reader', 'omnireader'];
|
||||
|
||||
//routes
|
||||
const routes = [
|
||||
['POST', '/api/config', misc.getConfig.bind(misc), [all], {}],
|
||||
['POST', '/api/config', misc.getConfig.bind(misc), [aAll], {}],
|
||||
['POST', '/api/reader/load-book', reader.loadBook.bind(reader), [aAll], {}],
|
||||
['POST', '/api/worker/get-state', worker.getState.bind(worker), [aAll], {}],
|
||||
];
|
||||
|
||||
//to app
|
||||
|
||||
Reference in New Issue
Block a user