Работа над api reader, worker

This commit is contained in:
Book Pauk
2019-01-11 22:20:25 +07:00
parent 33caf94988
commit 75bed20f0c
8 changed files with 181 additions and 4 deletions

View File

@@ -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 {

View 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;

View 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;

View File

@@ -1,3 +1,5 @@
module.exports = {
MiscController: require('./MiscController'),
ReaderController: require('./ReaderController'),
WorkerController: require('./WorkerController'),
}

View 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;

View File

@@ -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
};

View 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;

View File

@@ -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