Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
894349618c | ||
|
|
2c74afd19b | ||
|
|
9f665eaeac | ||
|
|
96e0df783d | ||
|
|
54f82d73f3 | ||
|
|
d6ee223414 | ||
|
|
319d8e7fdf | ||
|
|
6bdfdebeaf | ||
|
|
58b1ed5638 | ||
|
|
420c0f2464 | ||
|
|
7e86621fcd | ||
|
|
4ddabd1ca9 | ||
|
|
061f50b714 | ||
|
|
8aab918ac5 | ||
|
|
9aa9261b6a | ||
|
|
0894a38978 | ||
|
|
f519bf3f67 | ||
|
|
ecf0aada37 | ||
|
|
eb9bf89854 | ||
|
|
e1418678e0 | ||
|
|
ecc7e2b2c3 | ||
|
|
7799bba9f2 |
266
README.md
266
README.md
@@ -1,3 +1,265 @@
|
||||
# inpx-web
|
||||
inpx-web
|
||||
========
|
||||
|
||||
Веб-сервер для поиска по inpx-коллекции.
|
||||
Веб-сервер для поиска по .inpx-коллекции.
|
||||
Выглядит это так: https://lib.omnireader.ru
|
||||
|
||||
.inpx - индексный файл для импорта\экспорта информации из базы данных сетевых библиотек
|
||||
в базу каталогизатора [MyHomeLib](https://alex80.github.io/mhl/)
|
||||
или [freeLib](http://sourceforge.net/projects/freelibdesign)
|
||||
или [LightLib](https://lightlib.azurewebsites.net)
|
||||
|
||||
Просто поместите приложение `inpx-web` в папку с .inpx-файлом и файлами библиотеки (zip-архивами) и запустите.
|
||||
Сервер будет доступен по адресу http://127.0.0.1:12380
|
||||
|
||||
После открытия веб-приложения в бразуере, для быстрого понимания того, как работает поиск, воспользуйтесь памяткой (кнопка со знаком вопроса).
|
||||
|
||||
##
|
||||
* [Возможности программы](#capabilities)
|
||||
* [Использование](#usage)
|
||||
* [Параметры командной строки](#cli)
|
||||
* [Конфигурация](#config)
|
||||
* [Удаленная библиотека](#remotelib)
|
||||
* [Фильтр по аторам и книгам](#filter)
|
||||
* [Настройка https с помощью nginx](#https)
|
||||
* [Сборка проекта](#build)
|
||||
* [Разработка](#development)
|
||||
|
||||
<a id="capabilities" />
|
||||
|
||||
## Возможности программы
|
||||
- поиск по автору, серии, названию и пр.
|
||||
- скачивание книги, копирование ссылки или открытие в читалке
|
||||
- возможность указать рабочий каталог при запуске, а также расположение .inpx и файлов библиотеки
|
||||
- ограничение доступа по паролю
|
||||
- работа в режиме "удаленная библиотека"
|
||||
- фильтр авторов и книг при создании поисковой БД для создания своей коллекции "на лету"
|
||||
- подхват изменений .inpx-файла (периодическая проверка), автоматическое пересоздание поисковой БД
|
||||
- мощная оптимизация, хорошая скорость поиска
|
||||
- релизы под Linux и Windows
|
||||
|
||||
<a id="usage" />
|
||||
|
||||
## Использование
|
||||
Поместите приложение `inpx-web` в папку с .inpx-файлом и файлами библиотеки и запустите.
|
||||
По умолчанию сервер будет доступен по адресу http://127.0.0.1:12380
|
||||
|
||||
<a id="cli" />
|
||||
|
||||
### Параметры командной строки
|
||||
Запустите `inpx-web --help`, чтобы увидеть список опций:
|
||||
```console
|
||||
Usage: inpx-web [options]
|
||||
|
||||
Options:
|
||||
--help Показать опции командной строки
|
||||
--host=<ip> Задать имя хоста для веб сервера, по умолчанию: 0.0.0.0
|
||||
--port=<port> Задать порт для веб сервера, по умолчанию: 12380
|
||||
--app-dir=<dirpath> Задать рабочую директорию, по умолчанию: <execDir>/.inpx-web
|
||||
--lib-dir=<dirpath> Задать директорию библиотеки (с zip-архивами), по умолчанию: там же, где лежит файл приложения
|
||||
--inpx=<filepath> Задать путь к файлу .inpx, по умолчанию: тот, что найдется в директории библиотеки
|
||||
--recreate Принудительно пересоздать поисковую БД при запуске приложения
|
||||
```
|
||||
|
||||
<a id="config" />
|
||||
|
||||
### Конфигурация
|
||||
При первом запуске в рабочей директории будет создан конфигурационный файл `config.json`:
|
||||
```js
|
||||
{
|
||||
// пароль для ограничения доступа к веб-интерфейсу сервера
|
||||
"accessPassword": "",
|
||||
|
||||
// содержимое кнопки-ссылки (читать), если не задано - кнопка (читать) не показывается
|
||||
// пример: "https://omnireader.ru/#/reader?url=${DOWNLOAD_LINK}"
|
||||
// на место ${DOWNLOAD_LINK} будет подставлена ссылка на скачивание файла книги
|
||||
"bookReadLink": "",
|
||||
|
||||
// включить(true)/выключить(false) журналирование
|
||||
"loggingEnabled": true,
|
||||
|
||||
// максимальный размер в байтах директории закешированных файлов в <раб.дир>/public/files
|
||||
// чистка каждый час
|
||||
"maxFilesDirSize": 1073741824,
|
||||
|
||||
// включить(true)/выключить(false) кеширование запросов на сервере
|
||||
"queryCacheEnabled": true,
|
||||
|
||||
// периодичность чистки кеша запросов на сервере, в минутах
|
||||
"cacheCleanInterval": 60,
|
||||
|
||||
// периодичность проверки изменений .inpx-файла
|
||||
// если файл изменился, поисковая БД будет автоматически пересоздана
|
||||
"inpxCheckInterval": 60,
|
||||
|
||||
// включить(true)/выключить(false) режим работы с малым количеством физической памяти на машине
|
||||
// при включении этого режима, количество требуемой для создания БД памяти снижается примерно в 1.5-2 раза
|
||||
// во столько же раз увеличивается время создания
|
||||
"lowMemoryMode": false,
|
||||
|
||||
// включить(true)/выключить(false) режим "Удаленная библиотека" (сервер)
|
||||
"allowRemoteLib": false,
|
||||
|
||||
// включить(Object)/выключить(false) режим "Удаленная библиотека" (клиент)
|
||||
// подробнее см. раздел "Удаленная библиотека" ниже
|
||||
"remoteLib": false,
|
||||
|
||||
// настройки веб-сервера
|
||||
"server": {
|
||||
"host": "0.0.0.0",
|
||||
"port": "12380"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
При необходимости, можно настроить нужный параметр в этом файле вручную. Параметры командной
|
||||
строки имеют больший приоритет, чем настройки из `config.json`.
|
||||
|
||||
<a id="remotelib" />
|
||||
|
||||
### Удаленная библиотека
|
||||
|
||||
В случае, когда необходимо физически разнести веб-интерфейс и библиотеку файлов на разные машины,
|
||||
приложение может работать в режиме клиент-сервер: веб-интерфейс, поисковый движок и поисковая БД на одной машине (клиент),
|
||||
а библиотека книг и .inpx-файл на другой (сервер).
|
||||
|
||||
Для этого необходимо развернуть два приложения, первое из которых будет клиентом для второго.
|
||||
|
||||
На сервере правим `config.json`:
|
||||
```
|
||||
"accessPassword": "123456",
|
||||
"allowRemoteLib": true,
|
||||
```
|
||||
|
||||
На клиенте:
|
||||
```
|
||||
"remoteLib": {
|
||||
"accessPassword": "123456",
|
||||
"url": "ws://server.host:12380"
|
||||
},
|
||||
```
|
||||
|
||||
Если сервер работает по протоколу `http://`, то указываем протокол `ws://`, а для `https://` соответственно `wss://`.
|
||||
Пароль не обязателен, но необходим в случае, если сервер тоже "смотрит" в интернет, для ограничения доступа к его веб-интерфесу.
|
||||
При указании `"remoteLib": {...}` настройки командной строки --inpx и --lib-dir игнорируются,
|
||||
т.к. файлы .inpx-индекса и библиотеки используются удаленно.
|
||||
|
||||
<a id="filter" />
|
||||
|
||||
### Фильтр по аторам и книгам
|
||||
|
||||
При создании поисковой БД во время загрузки и парсинга .inpx-файла, имеется возможность
|
||||
отфильтровать авторов и книги, задав определенные критерии. Для этого небходимо создать
|
||||
в рабочей директории (там же, где `config.json`) файл `filter.json` следующего вида:
|
||||
```json
|
||||
{
|
||||
"info": {
|
||||
"collection": "Новое название коллекции",
|
||||
"structure": "",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"filter": "(r) => r.del == 0",
|
||||
"includeAuthors": ["Имя автора 1", "Имя автора 2"],
|
||||
"excludeAuthors": ["Имя автора"]
|
||||
}
|
||||
```
|
||||
При создании поисковой БД, авторы и книги из `includeAuthors` будут добавлены, а из `excludeAuthors` исключены.
|
||||
Использование совместно `includeAuthors` и `excludeAuthors` имеет мало смысла, поэтому для включения
|
||||
определенных авторов можно использовать только `includeAuthors`:
|
||||
```json
|
||||
{
|
||||
"info": {
|
||||
"collection": "Новое название коллекции"
|
||||
},
|
||||
"includeAuthors": ["Имя автора 1", "Имя автора 2"]
|
||||
}
|
||||
```
|
||||
Для исключения:
|
||||
```json
|
||||
{
|
||||
"info": {
|
||||
"collection": "Новое название коллекции"
|
||||
},
|
||||
"excludeAuthors": ["Имя автора 1", "Имя автора 2"]
|
||||
}
|
||||
```
|
||||
|
||||
Параметр `filter` используется для более гибкой фильтрации по атрибутам записей из .inpx.
|
||||
Уберем все записи, помеченные как удаленные и исключим "Имя автора 1":
|
||||
```json
|
||||
{
|
||||
"info": {
|
||||
"collection": "Новое название коллекции"
|
||||
},
|
||||
"filter": "(inpxRec) => inpxRec.del == 0",
|
||||
"excludeAuthors": ["Имя автора 1"]
|
||||
}
|
||||
```
|
||||
Использование `filter` небезопасно, т.к. позволяет выполнить произвольный js-код внутри программы,
|
||||
поэтому запуск приложения в этом случае должен сопровождаться дополнительным параметром командной строки `--unsafe-filter`.
|
||||
Названия атрибутов inpxRec соответствуют названиям в нижнем регистре из структуры structure.info в .inpx-файле.
|
||||
<a id="https" />
|
||||
|
||||
### Настройка https с помощью nginx
|
||||
Проще всего настроить https с помощью certbot и проксирования в nginx (пример для debian-based linux):
|
||||
|
||||
```sh
|
||||
#ставим nginx
|
||||
sudo apt install nginx
|
||||
```
|
||||
```
|
||||
#правим конфиг nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name <имя сервера>;
|
||||
set $inpx_web http://127.0.0.1:12380;
|
||||
|
||||
client_max_body_size 512m;
|
||||
proxy_read_timeout 1h;
|
||||
|
||||
location / {
|
||||
proxy_pass $inpx_web;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
}
|
||||
```
|
||||
```sh
|
||||
#загружаем новый конфиг
|
||||
sudo service nginx reload
|
||||
```
|
||||
Далее следовать инструкции установки https://certbot.eff.org/instructions?ws=nginx&os=debianbuster
|
||||
|
||||
<a id="build" />
|
||||
|
||||
### Сборка проекта
|
||||
Сборка только в среде Linux.
|
||||
Необходима версия node.js не ниже 16.
|
||||
|
||||
```sh
|
||||
git clone https://github.com/bookpauk/inpx-web
|
||||
cd inpx-web
|
||||
npm i
|
||||
```
|
||||
|
||||
#### Для платформы Windows
|
||||
```sh
|
||||
npm run build:win
|
||||
```
|
||||
|
||||
#### Для платформы Linux
|
||||
```sh
|
||||
npm run build:linux
|
||||
```
|
||||
|
||||
Результат сборки будет доступен в каталоге `dist/linux|win` в виде исполнимого (standalone) файла.
|
||||
|
||||
<a id="development" />
|
||||
|
||||
### Разработка
|
||||
```sh
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Связаться с автором проекта: [bookpauk@gmail.com](mailto:bookpauk@gmail.com)
|
||||
31
build/release.js
Normal file
31
build/release.js
Normal file
@@ -0,0 +1,31 @@
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
const pckg = require('../package.json');
|
||||
|
||||
const distDir = path.resolve(__dirname, '../dist');
|
||||
const outDir = `${distDir}/release`;
|
||||
|
||||
async function makeRelease(target) {
|
||||
const srcDir = `${distDir}/${target}`;
|
||||
|
||||
if (await fs.pathExists(srcDir)) {
|
||||
const zipFile = `${outDir}/${pckg.name}-${pckg.version}-${target}.zip`;
|
||||
|
||||
execSync(`zip -r ${zipFile} .`, {cwd: srcDir, stdio: 'inherit'});
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
await fs.emptyDir(outDir);
|
||||
await makeRelease('win');
|
||||
await makeRelease('linux');
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -533,7 +533,7 @@ class Search {
|
||||
}
|
||||
|
||||
openReleasePage() {
|
||||
window.open('https://github.com/bookpauk/inpx-web', '_blank');
|
||||
window.open('https://github.com/bookpauk/inpx-web/releases', '_blank');
|
||||
}
|
||||
|
||||
makeProjectName() {
|
||||
@@ -842,10 +842,16 @@ class Search {
|
||||
d.click();
|
||||
} else if (action == 'copyLink') {
|
||||
//копирование ссылки
|
||||
if (utils.copyTextToClipboard(href))
|
||||
if (await utils.copyTextToClipboard(href))
|
||||
this.$root.notify.success('Ссылка успешно скопирована');
|
||||
else
|
||||
this.$root.notify.error('Копирование ссылки не удалось');
|
||||
this.$root.stdDialog.alert(
|
||||
`Копирование ссылки не удалось. Пожалуйста, попробуйте еще раз.
|
||||
<br><br>
|
||||
<b>Пояснение</b>: вероятно, браузер запретил копирование, т.к. прошло<br>
|
||||
слишком много времени с момента нажатия на кнопку (инициация<br>
|
||||
пользовательского события). Сейчас ссылка уже закеширована,<br>
|
||||
поэтому повторная попытка должна быть успешной.`, 'Ошибка');
|
||||
} else if (action == 'readBook') {
|
||||
//читать
|
||||
if (this.liberamaReady) {
|
||||
|
||||
@@ -48,7 +48,29 @@ export function wordEnding(num, type = 0) {
|
||||
}
|
||||
}
|
||||
|
||||
export function fallbackCopyTextToClipboard(text) {
|
||||
let textArea = document.createElement('textarea');
|
||||
textArea.value = text;
|
||||
document.body.appendChild(textArea);
|
||||
textArea.focus();
|
||||
textArea.select();
|
||||
|
||||
let result = false;
|
||||
try {
|
||||
result = document.execCommand('copy');
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
document.body.removeChild(textArea);
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function copyTextToClipboard(text) {
|
||||
if (!navigator.clipboard) {
|
||||
return fallbackCopyTextToClipboard(text);
|
||||
}
|
||||
|
||||
let result = false;
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "inpx-web",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.3",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "inpx-web",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.3",
|
||||
"hasInstallScript": true,
|
||||
"license": "CC0-1.0",
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "inpx-web",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.3",
|
||||
"author": "Book Pauk <bookpauk@gmail.com>",
|
||||
"license": "CC0-1.0",
|
||||
"repository": "bookpauk/inpx-web",
|
||||
@@ -13,6 +13,7 @@
|
||||
"build:linux": "npm run build:client && node build/prepkg.js linux && pkg -t node16-linux-x64 -C GZip --options max-old-space-size=4096,expose-gc -o dist/linux/inpx-web .",
|
||||
"build:win": "npm run build:client && node build/prepkg.js win && pkg -t node16-win-x64 -C GZip --options max-old-space-size=4096,expose-gc -o dist/win/inpx-web .",
|
||||
"build:client-dev": "webpack --config build/webpack.dev.config.js",
|
||||
"release": "npm run build:linux && npm run build:client && node build/release.js",
|
||||
"postinstall": "npm run build:client-dev"
|
||||
},
|
||||
"bin": "server/index.js",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
const fs = require('fs-extra');
|
||||
const _ = require('lodash');
|
||||
const WebSocket = require ('ws');
|
||||
|
||||
@@ -182,9 +181,9 @@ class WebSocketController {
|
||||
if (!this.config.allowRemoteLib)
|
||||
throw new Error('Remote lib access disabled');
|
||||
|
||||
const data = await fs.readFile(this.config.inpxFile, 'base64');
|
||||
const result = await this.webWorker.getInpxFile(req);
|
||||
|
||||
this.send({data}, req, ws);
|
||||
this.send(result, req, ws);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,10 +9,10 @@ class FileDownloader {
|
||||
this.limitDownloadSize = limitDownloadSize;
|
||||
}
|
||||
|
||||
async load(url, callback, abort) {
|
||||
async load(url, opts, callback, abort) {
|
||||
let errMes = '';
|
||||
|
||||
const options = {
|
||||
let options = {
|
||||
headers: {
|
||||
'user-agent': userAgent,
|
||||
timeout: 300*1000,
|
||||
@@ -22,6 +22,8 @@ class FileDownloader {
|
||||
}),
|
||||
responseType: 'stream',
|
||||
};
|
||||
if (opts)
|
||||
options = Object.assign({}, opts, options);
|
||||
|
||||
try {
|
||||
const res = await axios.get(url, options);
|
||||
|
||||
@@ -23,6 +23,14 @@ class InpxHashCreator {
|
||||
|
||||
return utils.getBufHash(joinedHash, 'sha256', 'hex');
|
||||
}
|
||||
|
||||
async getInpxFileHash() {
|
||||
return (
|
||||
await fs.pathExists(this.config.inpxFile) ?
|
||||
await utils.getFileHash(this.config.inpxFile, 'sha256', 'hex') :
|
||||
''
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = InpxHashCreator;
|
||||
@@ -4,6 +4,7 @@ const utils = require('./utils');
|
||||
|
||||
const FileDownloader = require('./FileDownloader');
|
||||
const WebSocketConnection = require('./WebSocketConnection');
|
||||
const InpxHashCreator = require('./InpxHashCreator');
|
||||
const log = new (require('./AppLogger'))().log;//singleton
|
||||
|
||||
//singleton
|
||||
@@ -20,10 +21,9 @@ class RemoteLib {
|
||||
|
||||
this.remoteHost = config.remoteLib.url.replace(/^ws:\/\//, 'http://').replace(/^wss:\/\//, 'https://');
|
||||
|
||||
this.inpxFile = `${config.tempDir}/${utils.randomHexString(20)}`;
|
||||
this.lastUpdateTime = 0;
|
||||
|
||||
this.down = new FileDownloader(config.maxPayloadSize*1024*1024);
|
||||
this.inpxHashCreator = new InpxHashCreator(config);
|
||||
this.inpxFileHash = '';
|
||||
|
||||
instance = this;
|
||||
}
|
||||
@@ -46,17 +46,16 @@ class RemoteLib {
|
||||
return response;
|
||||
}
|
||||
|
||||
async downloadInpxFile(getPeriod = 0) {
|
||||
if (getPeriod && Date.now() - this.lastUpdateTime < getPeriod)
|
||||
return this.inpxFile;
|
||||
async downloadInpxFile() {
|
||||
if (!this.inpxFileHash)
|
||||
this.inpxFileHash = await this.inpxHashCreator.getInpxFileHash();
|
||||
|
||||
const response = await this.wsRequest({action: 'get-inpx-file'});
|
||||
const response = await this.wsRequest({action: 'get-inpx-file', inpxFileHash: this.inpxFileHash});
|
||||
|
||||
await fs.writeFile(this.inpxFile, response.data, 'base64');
|
||||
|
||||
this.lastUpdateTime = Date.now();
|
||||
|
||||
return this.inpxFile;
|
||||
if (response.data) {
|
||||
await fs.writeFile(this.config.inpxFile, response.data, 'base64');
|
||||
this.inpxFileHash = '';
|
||||
}
|
||||
}
|
||||
|
||||
async downloadBook(bookPath, downFileName) {
|
||||
@@ -64,9 +63,10 @@ class RemoteLib {
|
||||
const response = await await this.wsRequest({action: 'get-book-link', bookPath, downFileName});
|
||||
const link = response.link;
|
||||
|
||||
const buf = await this.down.load(`${this.remoteHost}${link}`);
|
||||
const buf = await this.down.load(`${this.remoteHost}${link}`, {decompress: false});
|
||||
|
||||
const publicPath = `${this.config.publicDir}${link}`;
|
||||
|
||||
await fs.writeFile(publicPath, buf);
|
||||
|
||||
return path.basename(link);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const fs = require('fs-extra');
|
||||
const zlib = require('zlib');
|
||||
const _ = require('lodash');
|
||||
|
||||
const ZipReader = require('./ZipReader');
|
||||
@@ -44,6 +43,9 @@ class WebWorker {
|
||||
this.remoteLib = new RemoteLib(config);
|
||||
}
|
||||
|
||||
this.inpxHashCreator = new InpxHashCreator(config);
|
||||
this.inpxFileHash = '';
|
||||
|
||||
this.wState = this.workerState.getControl('server_state');
|
||||
this.myState = '';
|
||||
this.db = null;
|
||||
@@ -137,12 +139,20 @@ class WebWorker {
|
||||
const config = this.config;
|
||||
const dbPath = `${config.dataDir}/db`;
|
||||
|
||||
this.inpxFileHash = await this.inpxHashCreator.getInpxFileHash();
|
||||
|
||||
//пересоздаем БД из INPX если нужно
|
||||
if (config.recreateDb || recreate)
|
||||
await fs.remove(dbPath);
|
||||
|
||||
if (!await fs.pathExists(dbPath)) {
|
||||
await this.createDb(dbPath);
|
||||
try {
|
||||
await this.createDb(dbPath);
|
||||
} catch (e) {
|
||||
//при ошибке создания БД удалим ее, чтобы не работать с поломанной базой при следующем запуске
|
||||
await fs.remove(dbPath);
|
||||
throw e;
|
||||
}
|
||||
utils.freeMemory();
|
||||
}
|
||||
|
||||
@@ -310,20 +320,6 @@ class WebWorker {
|
||||
}
|
||||
}
|
||||
|
||||
//async
|
||||
gzipFile(inputFile, outputFile, level = 1) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const gzip = zlib.createGzip({level});
|
||||
const input = fs.createReadStream(inputFile);
|
||||
const output = fs.createWriteStream(outputFile);
|
||||
|
||||
input.pipe(gzip).pipe(output).on('finish', (err) => {
|
||||
if (err) reject(err);
|
||||
else resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async restoreBook(bookPath, downFileName) {
|
||||
const db = this.db;
|
||||
|
||||
@@ -344,7 +340,7 @@ class WebWorker {
|
||||
await fs.ensureDir(path.dirname(publicPath));
|
||||
|
||||
const tmpFile = `${this.config.tempDir}/${utils.randomHexString(30)}`;
|
||||
await this.gzipFile(extractedFile, tmpFile, 4);
|
||||
await utils.gzipFile(extractedFile, tmpFile, 4);
|
||||
await fs.remove(extractedFile);
|
||||
await fs.move(tmpFile, publicPath, {overwrite: true});
|
||||
} else {
|
||||
@@ -442,6 +438,18 @@ class WebWorker {
|
||||
}
|
||||
}
|
||||
|
||||
async getInpxFile(params) {
|
||||
let data = null;
|
||||
if (params.inpxFileHash && this.inpxFileHash && params.inpxFileHash === this.inpxFileHash) {
|
||||
data = false;
|
||||
}
|
||||
|
||||
if (data === null)
|
||||
data = await fs.readFile(this.config.inpxFile, 'base64');
|
||||
|
||||
return {data};
|
||||
}
|
||||
|
||||
logServerStats() {
|
||||
try {
|
||||
const memUsage = process.memoryUsage().rss/(1024*1024);//Mb
|
||||
@@ -451,7 +459,7 @@ class WebWorker {
|
||||
log(`Server info [ memUsage: ${memUsage.toFixed(2)}MB, loadAvg: (${loadAvg.join(', ')}) ]`);
|
||||
|
||||
if (this.config.server.ready)
|
||||
log(`Server accessible on http://127.0.0.1:${this.config.server.port} (listening on ${this.config.server.host}:${this.config.server.port})`);
|
||||
log(`Server accessible at http://127.0.0.1:${this.config.server.port} (listening on ${this.config.server.host}:${this.config.server.port})`);
|
||||
} catch (e) {
|
||||
log(LM_ERR, e.message);
|
||||
}
|
||||
@@ -531,18 +539,16 @@ class WebWorker {
|
||||
if (!inpxCheckInterval)
|
||||
return;
|
||||
|
||||
const inpxHashCreator = new InpxHashCreator(this.config);
|
||||
|
||||
while (1) {// eslint-disable-line no-constant-condition
|
||||
try {
|
||||
while (this.myState != ssNormal)
|
||||
await utils.sleep(1000);
|
||||
|
||||
if (this.remoteLib) {
|
||||
await this.remoteLib.downloadInpxFile(60*1000);
|
||||
await this.remoteLib.downloadInpxFile();
|
||||
}
|
||||
|
||||
const newInpxHash = await inpxHashCreator.getHash();
|
||||
const newInpxHash = await this.inpxHashCreator.getHash();
|
||||
|
||||
const dbConfig = await this.dbConfig();
|
||||
const currentInpxHash = (dbConfig.inpxHash ? dbConfig.inpxHash : '');
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const zlib = require('zlib');
|
||||
const crypto = require('crypto');
|
||||
|
||||
function sleep(ms) {
|
||||
@@ -93,6 +94,24 @@ function randomHexString(len) {
|
||||
return crypto.randomBytes(len).toString('hex')
|
||||
}
|
||||
|
||||
//async
|
||||
function gzipFile(inputFile, outputFile, level = 1) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const gzip = zlib.createGzip({level});
|
||||
const input = fs.createReadStream(inputFile);
|
||||
const output = fs.createWriteStream(outputFile);
|
||||
|
||||
input.pipe(gzip).pipe(output).on('finish', (err) => {
|
||||
if (err) reject(err);
|
||||
else resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function toUnixPath(dir) {
|
||||
return dir.replace(/\\/g, '/');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
sleep,
|
||||
versionText,
|
||||
@@ -104,4 +123,6 @@ module.exports = {
|
||||
getBufHash,
|
||||
intersectSet,
|
||||
randomHexString,
|
||||
gzipFile,
|
||||
toUnixPath,
|
||||
};
|
||||
@@ -114,9 +114,10 @@ async function init() {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
config.inpxFile = `${config.tempDir}/${utils.randomHexString(20)}`;
|
||||
const RemoteLib = require('./core/RemoteLib');//singleton
|
||||
const remoteLib = new RemoteLib(config);
|
||||
config.inpxFile = await remoteLib.downloadInpxFile();
|
||||
await remoteLib.downloadInpxFile();
|
||||
}
|
||||
|
||||
config.recreateDb = argv.recreate || false;
|
||||
@@ -204,13 +205,13 @@ function initStatic(app, config) {
|
||||
});
|
||||
|
||||
//заголовки при отдаче
|
||||
const filesDir = `${config.publicDir}/files`;
|
||||
const filesDir = utils.toUnixPath(`${config.publicDir}/files`);
|
||||
app.use(express.static(config.publicDir, {
|
||||
setHeaders: (res, filePath) => {
|
||||
res.set('Cache-Control', 'no-cache');
|
||||
res.set('Expires', '-1');
|
||||
//res.set('Cache-Control', 'no-cache');
|
||||
//res.set('Expires', '-1');
|
||||
|
||||
if (path.dirname(filePath) == filesDir) {
|
||||
if (utils.toUnixPath(path.dirname(filePath)) == filesDir) {
|
||||
res.set('Content-Encoding', 'gzip');
|
||||
|
||||
if (res.downFileName)
|
||||
|
||||
Reference in New Issue
Block a user