Compare commits

...

13 Commits

Author SHA1 Message Date
Book Pauk
eedca4db9b Merge branch 'release/0.8.2-5' 2020-01-23 17:12:37 +07:00
Book Pauk
1d352a76ce Поправка опечаток 2020-01-23 17:00:17 +07:00
Book Pauk
17670aabf9 WebSocket: добавлен метод reader-storage, поправки багов 2020-01-23 16:59:08 +07:00
Book Pauk
3456b3d90e WebSocket: добавлен метод worker-get-state-finish, небольшой рефакторинг 2020-01-23 16:25:06 +07:00
Book Pauk
f3da5a9026 Поправил комментарий 2020-01-23 15:56:26 +07:00
Book Pauk
00cc63b7cd WebSocket: добавлен метод get-config 2020-01-23 15:54:46 +07:00
Book Pauk
8df80ce738 Мелкая поправка 2020-01-23 15:16:49 +07:00
Book Pauk
12e7a783b0 Небольшие изменения блокирования кнопок панели 2020-01-22 22:06:12 +07:00
Book Pauk
be86a15351 Добавил настройку proxy_read_timeout 2020-01-22 21:37:28 +07:00
Book Pauk
2c5022e7b4 Merge tag '0.8.2-4' into develop
0.8.2-4
2020-01-22 21:17:58 +07:00
Book Pauk
f4a996fcb9 Merge branch 'release/0.8.2-4' 2020-01-22 21:17:52 +07:00
Book Pauk
fdbf508bbf Используем протокол WSS при необходимости 2020-01-22 21:17:10 +07:00
Book Pauk
500fafa5b2 Merge tag '0.8.2-3' into develop
0.8.2-3
2020-01-22 21:05:36 +07:00
9 changed files with 145 additions and 79 deletions

View File

@@ -1,4 +1,5 @@
import axios from 'axios'; import axios from 'axios';
import wsc from './webSocketConnection';
const api = axios.create({ const api = axios.create({
baseURL: '/api' baseURL: '/api'
@@ -6,9 +7,20 @@ const api = axios.create({
class Misc { class Misc {
async loadConfig() { async loadConfig() {
const response = await api.post('/config', {params: [
const query = {params: [
'name', 'version', 'mode', 'maxUploadFileSize', 'useExternalBookConverter', 'branch', 'name', 'version', 'mode', 'maxUploadFileSize', 'useExternalBookConverter', 'branch',
]}); ]};
try {
await wsc.open();
return await wsc.message(wsc.send(Object.assign({action: 'get-config'}, query)));
} catch (e) {
console.error(e);
}
//если с WebSocket проблема, работаем по http
const response = await api.post('/config', query);
return response.data; return response.data;
} }
} }

View File

@@ -1,6 +1,6 @@
import axios from 'axios'; import axios from 'axios';
import * as utils from '../share/utils'; import * as utils from '../share/utils';
import WebSocketConnection from './WebSocketConnection'; import wsc from './webSocketConnection';
const api = axios.create({ const api = axios.create({
baseURL: '/api/reader' baseURL: '/api/reader'
@@ -12,16 +12,14 @@ const workerApi = axios.create({
class Reader { class Reader {
constructor() { constructor() {
this.wsc = new WebSocketConnection();
} }
async getStateFinish(workerId, callback) { async getWorkerStateFinish(workerId, callback) {
if (!callback) callback = () => {}; if (!callback) callback = () => {};
let response = {}; let response = {};
try { try {
const wsc = this.wsc;
await wsc.open(); await wsc.open();
const requestId = wsc.send({action: 'worker-get-state-finish', workerId}); const requestId = wsc.send({action: 'worker-get-state-finish', workerId});
@@ -35,11 +33,10 @@ class Reader {
} }
return response; return response;
} catch (e) { } catch (e) {
//
console.error(e); console.error(e);
} }
//с WebSocket проблема, проверяем по http //если с WebSocket проблема, работаем по http
const refreshPause = 500; const refreshPause = 500;
let i = 0; let i = 0;
response = {}; response = {};
@@ -80,12 +77,12 @@ class Reader {
callback({totalSteps: 4}); callback({totalSteps: 4});
callback(response.data); callback(response.data);
response = await this.getStateFinish(workerId, callback); response = await this.getWorkerStateFinish(workerId, callback);
if (response) { if (response) {
if (response.state == 'finish') {//воркер закончил работу, можно скачивать кешированный на сервере файл if (response.state == 'finish') {//воркер закончил работу, можно скачивать кешированный на сервере файл
callback({step: 4}); callback({step: 4});
const book = await this.loadCachedBook(response.path, callback, false, (response.size ? response.size : -1)); const book = await this.loadCachedBook(response.path, callback, response.size);
return Object.assign({}, response, {data: book.data}); return Object.assign({}, response, {data: book.data});
} }
@@ -103,75 +100,58 @@ class Reader {
} }
} }
async checkUrl(url) { async checkCachedBook(url) {
let fileExists = false; let estSize = -1;
try { try {
await axios.head(url, {headers: {'Cache-Control': 'no-cache'}}); const response = await axios.head(url, {headers: {'Cache-Control': 'no-cache'}});
fileExists = true;
if (response.headers['content-length']) {
estSize = response.headers['content-length'];
}
} catch (e) { } catch (e) {
// //восстановим при необходимости файл на сервере из удаленного облака
} let response = null
//восстановим при необходимости файл на сервере из удаленного облака
if (!fileExists) {
let response = await api.post('/restore-cached-file', {path: url});
const workerId = response.data.workerId;
if (!workerId)
throw new Error('Неверный ответ api');
response = await this.getStateFinish(workerId);
if (response.state == 'error') {
throw new Error(response.error);
}
}
return true;
}
async loadCachedBook(url, callback, restore = true, estSize = -1) {
if (!callback) callback = () => {};
let response = null;
callback({state: 'loading', progress: 0});
//получение размера файла
let fileExists = false;
if (estSize < 0) {
try { try {
response = await axios.head(url, {headers: {'Cache-Control': 'no-cache'}}); await wsc.open();
response = await wsc.message(wsc.send({action: 'reader-restore-cached-file', path: url}));
if (response.headers['content-length']) {
estSize = response.headers['content-length'];
}
fileExists = true;
} catch (e) { } catch (e) {
// console.error(e);
//если с WebSocket проблема, работаем по http
response = await api.post('/restore-cached-file', {path: url});
response = response.data;
} }
}
//восстановим при необходимости файл на сервере из удаленного облака const workerId = response.workerId;
if (restore && !fileExists) {
response = await api.post('/restore-cached-file', {path: url});
const workerId = response.data.workerId;
if (!workerId) if (!workerId)
throw new Error('Неверный ответ api'); throw new Error('Неверный ответ api');
response = await this.getStateFinish(workerId); response = await this.getWorkerStateFinish(workerId);
if (response.state == 'error') { if (response.state == 'error') {
throw new Error(response.error); throw new Error(response.error);
} }
if (response.size && estSize < 0) { if (response.size && estSize < 0) {
estSize = response.size; estSize = response.size;
} }
} }
return estSize;
}
async loadCachedBook(url, callback, estSize = -1) {
if (!callback) callback = () => {};
callback({state: 'loading', progress: 0});
//получение размера файла
if (estSize && estSize < 0) {
estSize = await this.checkCachedBook(url);
}
//получение файла //получение файла
estSize = (estSize > 0 ? estSize : 1000000); estSize = (estSize > 0 ? estSize : 1000000);
const options = { const options = {
onDownloadProgress: progress => { onDownloadProgress: (progress) => {
while (progress.loaded > estSize) estSize *= 1.5; while (progress.loaded > estSize) estSize *= 1.5;
if (callback) if (callback)
@@ -215,13 +195,22 @@ class Reader {
} }
async storage(request) { async storage(request) {
let response = await api.post('/storage', request); let response = null;
try {
await wsc.open();
response = await wsc.message(wsc.send({action: 'reader-storage', body: request}));
} catch (e) {
console.error(e);
//если с WebSocket проблема, работаем по http
response = await api.post('/storage', request);
response = response.data;
}
const state = response.data.state; const state = response.state;
if (!state) if (!state)
throw new Error('Неверный ответ api'); throw new Error('Неверный ответ api');
return response.data; return response;
} }
} }

View File

@@ -57,7 +57,12 @@ class WebSocketConnection {
if (this.ws && this.ws.readyState == WebSocket.OPEN) { if (this.ws && this.ws.readyState == WebSocket.OPEN) {
resolve(this.ws); resolve(this.ws);
} else { } else {
url = url || `ws://${window.location.host}/ws`; let protocol = 'ws:';
if (window.location.protocol == 'https:') {
protocol = 'wss:'
}
url = url || `${protocol}//${window.location.host}/ws`;
this.ws = new WebSocket(url); this.ws = new WebSocket(url);
@@ -106,7 +111,11 @@ class WebSocketConnection {
requestId, requestId,
timeout, timeout,
onMessage: (mes) => { onMessage: (mes) => {
resolve(mes); if (mes.error) {
reject(mes.error);
} else {
resolve(mes);
}
}, },
onError: (e) => { onError: (e) => {
reject(e); reject(e);
@@ -164,4 +173,4 @@ class WebSocketConnection {
} }
} }
export default WebSocketConnection; export default new WebSocketConnection();

View File

@@ -719,15 +719,16 @@ class Reader extends Vue {
case 'scrolling': case 'scrolling':
case 'search': case 'search':
case 'copyText': case 'copyText':
case 'recentBooks': case 'refresh':
case 'offlineMode': case 'offlineMode':
case 'recentBooks':
case 'settings': case 'settings':
if (this[`${button}Active`]) if (this.progressActive) {
classResult = classDisabled;
} else if (this[`${button}Active`]) {
classResult = classActive; classResult = classActive;
}
break; break;
}
switch (button) {
case 'undoAction': case 'undoAction':
if (this.actionCur <= 0) if (this.actionCur <= 0)
classResult = classDisabled; classResult = classDisabled;

View File

@@ -272,7 +272,7 @@ class RecentBooksPage extends Vue {
async downloadBook(fb2path) { async downloadBook(fb2path) {
try { try {
await readerApi.checkUrl(fb2path); await readerApi.checkCachedBook(fb2path);
const d = this.$refs.download; const d = this.$refs.download;
d.href = fb2path; d.href = fb2path;

View File

@@ -8,6 +8,7 @@ server {
server_name omnireader.ru; server_name omnireader.ru;
client_max_body_size 50m; client_max_body_size 50m;
proxy_read_timeout 1h;
gzip on; gzip on;
gzip_min_length 1024; gzip_min_length 1024;

View File

@@ -3,6 +3,7 @@ server {
server_name omnireader.ru; server_name omnireader.ru;
client_max_body_size 50m; client_max_body_size 50m;
proxy_read_timeout 1h;
gzip on; gzip on;
gzip_min_length 1024; gzip_min_length 1024;

View File

@@ -1,5 +1,10 @@
const WebSocket = require ('ws'); const WebSocket = require ('ws');
const _ = require('lodash');
const ReaderWorker = require('../core/Reader/ReaderWorker');//singleton
const ReaderStorage = require('../core/Reader/ReaderStorage');//singleton
const WorkerState = require('../core/WorkerState');//singleton const WorkerState = require('../core/WorkerState');//singleton
const log = new (require('../core/AppLogger'))().log;//singleton
const utils = require('../core/utils'); const utils = require('../core/utils');
const cleanPeriod = 1*60*1000;//1 минута const cleanPeriod = 1*60*1000;//1 минута
@@ -8,6 +13,10 @@ const closeSocketOnIdle = 5*60*1000;//5 минут
class WebSocketController { class WebSocketController {
constructor(wss, config) { constructor(wss, config) {
this.config = config; this.config = config;
this.isDevelopment = (config.branch == 'development');
this.readerStorage = new ReaderStorage();
this.readerWorker = new ReaderWorker(config);
this.workerState = new WorkerState(); this.workerState = new WorkerState();
this.wss = wss; this.wss = wss;
@@ -37,15 +46,25 @@ class WebSocketController {
async onMessage(ws, message) { async onMessage(ws, message) {
let req = {}; let req = {};
try { try {
if (this.isDevelopment) {
log(`WebSocket-IN: ${message.substr(0, 4000)}`);
}
ws.lastActivity = Date.now(); ws.lastActivity = Date.now();
req = JSON.parse(message); req = JSON.parse(message);
switch (req.action) { switch (req.action) {
case 'test': case 'test':
this.test(req, ws); break; await this.test(req, ws); break;
case 'get-config':
await this.getConfig(req, ws); break;
case 'worker-get-state': case 'worker-get-state':
this.workerGetState(req, ws); break; await this.workerGetState(req, ws); break;
case 'worker-get-state-finish': case 'worker-get-state-finish':
this.workerGetStateFinish(req, ws); break; await this.workerGetStateFinish(req, ws); break;
case 'reader-restore-cached-file':
await this.readerRestoreCachedFile(req, ws); break;
case 'reader-storage':
await this.readerStorageDo(req, ws); break;
default: default:
throw new Error(`Action not found: ${req.action}`); throw new Error(`Action not found: ${req.action}`);
@@ -58,10 +77,17 @@ class WebSocketController {
send(res, req, ws) { send(res, req, ws) {
if (ws.readyState == WebSocket.OPEN) { if (ws.readyState == WebSocket.OPEN) {
ws.lastActivity = Date.now(); ws.lastActivity = Date.now();
let r = Object.assign({}, res); let r = res;
if (req.requestId) if (req.requestId)
r.requestId = req.requestId; r = Object.assign({requestId: req.requestId}, r);
ws.send(JSON.stringify(r));
const message = JSON.stringify(r);
ws.send(message);
if (this.isDevelopment) {
log(`WebSocket-OUT: ${message.substr(0, 4000)}`);
}
} }
} }
@@ -70,6 +96,14 @@ class WebSocketController {
this.send({message: 'Liberama project is awesome'}, req, ws); this.send({message: 'Liberama project is awesome'}, req, ws);
} }
async getConfig(req, ws) {
if (Array.isArray(req.params)) {
this.send(_.pick(this.config, req.params), req, ws);
} else {
throw new Error('params is not an array');
}
}
async workerGetState(req, ws) { async workerGetState(req, ws) {
if (!req.workerId) if (!req.workerId)
throw new Error(`key 'workerId' is wrong`); throw new Error(`key 'workerId' is wrong`);
@@ -106,6 +140,25 @@ class WebSocketController {
} }
} }
async readerRestoreCachedFile(req, ws) {
if (!req.path)
throw new Error(`key 'path' is empty`);
const workerId = this.readerWorker.restoreCachedFile(req.path);
const state = this.workerState.getState(workerId);
this.send((state ? state : {}), req, ws);
}
async readerStorageDo(req, ws) {
if (!req.body)
throw new Error(`key 'body' is empty`);
if (!req.body.action)
throw new Error(`key 'action' is empty`);
if (!req.body.items || Array.isArray(req.body.data))
throw new Error(`key 'items' is empty`);
this.send(await this.readerStorage.doAction(req.body), req, ws);
}
} }
module.exports = WebSocketController; module.exports = WebSocketController;