浏览代码

Merge branch 'release/0.8.2-5'

Book Pauk 5 年之前
父节点
当前提交
eedca4db9b

+ 14 - 2
client/api/misc.js

@@ -1,4 +1,5 @@
 import axios from 'axios';
+import wsc from './webSocketConnection';
 
 const api = axios.create({
   baseURL: '/api'
@@ -6,9 +7,20 @@ const api = axios.create({
 
 class Misc {
     async loadConfig() {
-        const response = await api.post('/config', {params: [
+
+        const query = {params: [
             '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;
     }
 }

+ 46 - 57
client/api/reader.js

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

+ 6 - 2
client/api/WebSocketConnection.js → client/api/webSocketConnection.js

@@ -111,7 +111,11 @@ class WebSocketConnection {
                 requestId,
                 timeout,
                 onMessage: (mes) => {
-                    resolve(mes);
+                    if (mes.error) {
+                        reject(mes.error);
+                    } else {
+                        resolve(mes);
+                    }
                 },
                 onError: (e) => {
                     reject(e);
@@ -169,4 +173,4 @@ class WebSocketConnection {
     }
 }
 
-export default WebSocketConnection;
+export default new WebSocketConnection();

+ 6 - 5
client/components/Reader/Reader.vue

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

+ 1 - 1
client/components/Reader/RecentBooksPage/RecentBooksPage.vue

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

+ 1 - 0
docs/omnireader/omnireader

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

+ 1 - 0
docs/omnireader/omnireader_http

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

+ 2 - 2
server/controllers/ReaderController.js

@@ -35,9 +35,9 @@ class ReaderController extends BaseController {
         const request = req.body;
         let error = '';
         try {
-            if (!request.action) 
+            if (!request.action)
                 throw new Error(`key 'action' is empty`);
-            if (!request.items || Array.isArray(request.data)) 
+            if (!request.items || Array.isArray(request.data))
                 throw new Error(`key 'items' is empty`);
 
             return await this.readerStorage.doAction(request);

+ 59 - 6
server/controllers/WebSocketController.js

@@ -1,5 +1,10 @@
 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 log = new (require('../core/AppLogger'))().log;//singleton
 const utils = require('../core/utils');
 
 const cleanPeriod = 1*60*1000;//1 минута
@@ -8,6 +13,10 @@ const closeSocketOnIdle = 5*60*1000;//5 минут
 class WebSocketController {
     constructor(wss, config) {
         this.config = config;
+        this.isDevelopment = (config.branch == 'development');
+
+        this.readerStorage = new ReaderStorage();
+        this.readerWorker = new ReaderWorker(config);
         this.workerState = new WorkerState();
 
         this.wss = wss;
@@ -37,15 +46,25 @@ class WebSocketController {
     async onMessage(ws, message) {
         let req = {};
         try {
+            if (this.isDevelopment) {
+                log(`WebSocket-IN:  ${message.substr(0, 4000)}`);
+            }
+
             ws.lastActivity = Date.now();
             req = JSON.parse(message);
             switch (req.action) {
                 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':
-                    this.workerGetState(req, ws); break;
+                    await this.workerGetState(req, ws); break;
                 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:
                     throw new Error(`Action not found: ${req.action}`);
@@ -58,10 +77,17 @@ class WebSocketController {
     send(res, req, ws) {
         if (ws.readyState == WebSocket.OPEN) {
             ws.lastActivity = Date.now();
-            let r = Object.assign({}, res);
+            let r = res;
             if (req.requestId)
-                r.requestId = req.requestId;
-            ws.send(JSON.stringify(r));
+                r = Object.assign({requestId: req.requestId}, 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);
     }
 
+    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) {
         if (!req.workerId)
             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;