From afef0ed04c9c5816007df11ba1fe7c15c4950b6b Mon Sep 17 00:00:00 2001 From: Book Pauk Date: Mon, 26 Sep 2022 16:04:26 +0700 Subject: [PATCH] =?UTF-8?q?=D0=A3=D0=BB=D1=83=D1=87=D1=88=D0=B5=D0=BD?= =?UTF-8?q?=D0=BE=20=D1=81=D0=BA=D0=B0=D1=87=D0=B8=D0=B2=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D1=84=D0=B0=D0=B9=D0=BB=D0=BE=D0=B2=20-=20=D0=B4?= =?UTF-8?q?=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=BE=20=D1=87=D0=B8?= =?UTF-8?q?=D1=82=D0=B0=D0=B1=D0=B5=D0=BB=D1=8C=D0=BD=D0=BE=D0=B5=20=D0=B8?= =?UTF-8?q?=D0=BC=D1=8F=20=D1=84=D0=B0=D0=B9=D0=BB=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/components/Api/Api.vue | 8 ++--- client/components/Search/Search.vue | 39 +++++++++++++++++++---- client/share/utils.js | 13 ++++++++ server/controllers/WebSocketController.js | 4 ++- server/core/WebWorker.js | 33 ++++++++++++++----- server/index.js | 11 ++++++- 6 files changed, 87 insertions(+), 21 deletions(-) diff --git a/client/components/Api/Api.vue b/client/components/Api/Api.vue index 014c0c1..d95c3a5 100644 --- a/client/components/Api/Api.vue +++ b/client/components/Api/Api.vue @@ -124,9 +124,9 @@ class Api { } } - async request(params) { + async request(params, timeoutSecs = 10) { while (1) {// eslint-disable-line - const response = await wsc.message(await wsc.send(params)); + const response = await wsc.message(await wsc.send(params), timeoutSecs); if (response && response.error == 'server_busy') { await this.showBusyDialog(); @@ -166,8 +166,8 @@ class Api { return response; } - async getBookLink(bookPath) { - const response = await this.request({action: 'get-book-link', bookPath}); + async getBookLink(params) { + const response = await this.request(Object.assign({action: 'get-book-link'}, params), 120); if (response.error) { throw new Error(response.error); diff --git a/client/components/Search/Search.vue b/client/components/Search/Search.vue index f37308f..4f7c96e 100644 --- a/client/components/Search/Search.vue +++ b/client/components/Search/Search.vue @@ -555,26 +555,51 @@ class Search { } async download(book, copy = false) { - let downloadFlag = true; + if (this.downloadFlag) + return; + + this.downloadFlag = true; (async() => { await utils.sleep(200); - if (downloadFlag) + if (this.downloadFlag) this.loadingMessage2 = 'Подготовка файла...'; })(); - try { - const bookPath = `${book.folder}/${book.file}.${book.ext}`; + try { + const makeValidFilenameOrEmpty = (s) => { + try { + return utils.makeValidFilename(s); + } catch(e) { + return ''; + } + }; - const response = await this.api.getBookLink(bookPath); + //имя файла + let downFileName = 'default-name'; + const author = book.author.split(','); + const at = [author[0], book.title]; + downFileName = makeValidFilenameOrEmpty(at.filter(r => r).join(' - ')) + || makeValidFilenameOrEmpty(at[0]) + || makeValidFilenameOrEmpty(at[1]) + || downFileName; + downFileName = `${downFileName.substring(0, 100)}.${book.ext}`; + + const bookPath = `${book.folder}/${book.file}.${book.ext}`; + //подготовка + const response = await this.api.getBookLink({bookPath, downFileName}); - const href = `${window.location.origin}${response.link}`; + const link = response.link; + const href = `${window.location.origin}${link}`; if (!copy) { + //скачивание const d = this.$refs.download; d.href = href; + d.download = downFileName; d.click(); } else { + //копирование ссылки if (utils.copyTextToClipboard(href)) this.$root.notify.success('Ссылка успешно скопирована'); else @@ -583,7 +608,7 @@ class Search { } catch(e) { this.$root.stdDialog.alert(e.message, 'Ошибка'); } finally { - downloadFlag = false; + this.downloadFlag = false; this.loadingMessage2 = ''; } } diff --git a/client/share/utils.js b/client/share/utils.js index f653588..332a337 100644 --- a/client/share/utils.js +++ b/client/share/utils.js @@ -54,3 +54,16 @@ export async function copyTextToClipboard(text) { return result; } + +export function makeValidFilename(filename, repl = '_') { + let f = filename.replace(/[\x00\\/:*"<>|]/g, repl); // eslint-disable-line no-control-regex + f = f.trim(); + while (f.length && (f[f.length - 1] == '.' || f[f.length - 1] == '_')) { + f = f.substring(0, f.length - 1); + } + + if (f) + return f; + else + throw new Error('Invalid filename'); +} diff --git a/server/controllers/WebSocketController.js b/server/controllers/WebSocketController.js index 939270f..4c813cd 100644 --- a/server/controllers/WebSocketController.js +++ b/server/controllers/WebSocketController.js @@ -147,8 +147,10 @@ class WebSocketController { async getBookLink(req, ws) { if (!utils.hasProp(req, 'bookPath')) throw new Error(`bookPath is empty`); + if (!utils.hasProp(req, 'downFileName')) + throw new Error(`downFileName is empty`); - const result = await this.webWorker.getBookLink(req.bookPath); + const result = await this.webWorker.getBookLink({bookPath: req.bookPath, downFileName: req.downFileName}); this.send(result, req, ws); } diff --git a/server/core/WebWorker.js b/server/core/WebWorker.js index 2be9673..65c1c96 100644 --- a/server/core/WebWorker.js +++ b/server/core/WebWorker.js @@ -307,7 +307,7 @@ class WebWorker { }); } - async restoreBook(bookPath) { + async restoreBook(bookPath, downFileName) { const db = this.db; const extractedFile = await this.extractBook(bookPath); @@ -333,16 +333,18 @@ class WebWorker { replace: true, rows: [ {id: bookPath, hash}, - {id: hash, bookPath} + {id: hash, bookPath, downFileName} ] }); return link; } - async getBookLink(bookPath) { + async getBookLink(params) { this.checkMyState(); + const {bookPath, downFileName} = params; + try { const db = this.db; let link = ''; @@ -360,7 +362,7 @@ class WebWorker { } if (!link) { - link = await this.restoreBook(bookPath) + link = await this.restoreBook(bookPath, downFileName) } if (!link) @@ -380,11 +382,13 @@ class WebWorker { const db = this.db; const hash = path.basename(publicPath); - //найдем bookPath + //найдем bookPath и downFileName const rows = await db.select({table: 'file_hash', where: `@@id(${db.esc(hash)})`}); - if (rows.length) {//bookPath найден по хешу - const bookPath = rows[0].bookPath; - await this.restoreBook(bookPath); + if (rows.length) {//нашли по хешу + const rec = rows[0]; + await this.restoreBook(rec.bookPath, rec.downFileName); + + return rec.downFileName; } else {//bookPath не найден throw new Error('404 Файл не найден'); } @@ -396,6 +400,19 @@ class WebWorker { } } + async getDownFileName(publicPath) { + const db = this.db; + const hash = path.basename(publicPath); + + //найдем downFileName + const rows = await db.select({table: 'file_hash', where: `@@id(${db.esc(hash)})`}); + if (rows.length) {//downFileName найден по хешу + return rows[0].downFileName; + } else {//bookPath не найден + throw new Error('404 Файл не найден'); + } + } + async logServerStats() { while (1) {// eslint-disable-line try { diff --git a/server/index.js b/server/index.js index e68f80f..afbb163 100644 --- a/server/index.js +++ b/server/index.js @@ -163,15 +163,21 @@ function initStatic(app, config) { const publicPath = `${config.publicDir}${req.path}`; + let downFileName = ''; //восстановим try { if (!await fs.pathExists(publicPath)) { - await webWorker.restoreBookFile(publicPath); + downFileName = await webWorker.restoreBookFile(publicPath); + } else { + downFileName = await webWorker.getDownFileName(publicPath); } } catch(e) { //quiet } + if (downFileName) + res.downFileName = downFileName; + return next(); }); @@ -183,6 +189,9 @@ function initStatic(app, config) { setHeaders: (res, filePath) => { if (path.dirname(filePath) == filesDir) { res.set('Content-Encoding', 'gzip'); + + if (res.downFileName) + res.set('Content-Disposition', `inline; filename*=UTF-8''${encodeURIComponent(res.downFileName)}`); } }, }));