Merge branch 'release/1.5.7'

This commit is contained in:
Book Pauk
2024-04-04 18:11:08 +07:00
10 changed files with 151 additions and 66 deletions

View File

@@ -1,9 +1,14 @@
1.5.7 / 2024-04-04
- В параметр bookReadLink конфига добавлен вариант замены DOWNLOAD_URI на uri из ссылки для скачивания книги (#29)
- В параметры командной строки добавлена возможность задавать путь к файлу конфигурации, а все остальные настройки приложения можно указать в нем самом (#30)
1.5.6 / 2024-03-25
- Добавлен вывод version.info в статистику по коллекции (#27)
- Убрано (по умолчанию) ежеминутное журналирование статистики сервера. В конфиг добавлен параметр logServerStats (#26)
- В конфиг добавлен параметр logQueries для журналирования запросов и времени их выполнения
- Добавлена расшифровка имен жанров в информации о книге(#24)
- Добавлена расшифровка имен жанров в информации о книге (#24)
1.5.5 / 2023-04-25

View File

@@ -66,29 +66,63 @@ OPDS-сервер доступен по адресу [http://127.0.0.1:12380/opd
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 Принудительно пересоздать поисковую БД при запуске приложения
--help Показать опции командной строки
--host=<ip> Задать имя хоста для веб сервера, по умолчанию: 0.0.0.0
--port=<port> Задать порт для веб сервера, по умолчанию: 12380
--config=<filepath> Задать файл конфигурации, по умолчанию: <dataDir>/config.json
--data-dir=<dirpath> (или --app-dir) Задать рабочую директорию, по умолчанию: <execDir>/.inpx-web
--lib-dir=<dirpath> Задать директорию библиотеки (с zip-архивами), по умолчанию: там же, где лежит файл приложения
--inpx=<filepath> Задать путь к файлу .inpx, по умолчанию: тот, что найдется в директории библиотеки
--recreate Принудительно пересоздать поисковую БД при запуске приложения
--unsafe-filter Использовать небезопасный фильтр на свой страх и риск
```
<a id="config" />
### Конфигурация
При первом запуске в рабочей директории будет создан конфигурационный файл `config.json`:
По умолчанию, при первом запуске в рабочей директории будет создан конфигурационный файл `config.json`.
При необходимости, можно настроить нужный параметр в этом файле вручную. Параметры командной
строки имеют больший приоритет, чем настройки из `config.json`.
```js
{
// рабочая директория приложения, аналог параметра командной строки --data-dir (или --app-dir)
// пустая строка: использовать значение по умолчанию - <execDir>/.inpx-web
// где execDir - директория файла приложения
"dataDir": "",
// директория для хранения временных файлов
// пустая строка: использовать значение по умолчанию - <dataDir>/tmp
// специальное значение "${OS}" указывается для использования системного каталога:
// "${OS}" => "<os_temporary_dir>/inpx-web"
"tempDir": "",
// директория для хранения логов
// пустая строка: использовать значение по умолчанию - <dataDir>/logs
"logDir": "",
// директория библиотеки (с zip-архивами), аналог параметра командной строки --lib-dir
// пустая строка: использовать значение по умолчанию - директорию файла приложения
// пустая строка: использовать значение по умолчанию - директорию файла приложения (execDir)
"libDir": "",
// путь к файлу .inpx, аналог параметра командной строки --inpx
// пустая строка: использовать значение по умолчанию - inpx-файл, что найдется в директории библиотеки
"inpx": "",
// конфигурационный файл для фильра по авторам и книгам (см. ниже)
// пустая строка: использовать значение по умолчанию - файл filter.json в директории файла конфигурации
"inpxFilterFile": "",
// разрешить(true)/запретить(false) перезаписывать файл конфигурации, если появились новые параметры для настройки
// файл перезаписывается с сохранением всех предыдущих настроек и с новыми по умолчанию
// бывает полезно при выходе новых версий приложения
"allowConfigRewrite": false,
// разрешить(true)/запретить(false) использовать небезопасный фильтр (см. ниже)
// аналог параметра командной строки --unsafe-filter
"allowUnsafeFilter": false,
// пароль для ограничения доступа к веб-интерфейсу сервера
// пустое значение - доступ без ограничений
"accessPassword": "",
@@ -106,6 +140,8 @@ Options:
// содержимое кнопки-ссылки "(читать)", если не задано - кнопка "(читать)" не показывается
// пример: "https://omnireader.ru/#/reader?url=${DOWNLOAD_LINK}"
// на место ${DOWNLOAD_LINK} будет подставлена ссылка на скачивание файла книги
// пример: "https://mydomain.ru/#/reader?url=http://127.0.0.1:8086${DOWNLOAD_URI}"
// на место ${DOWNLOAD_URI} будут подставлены параметры (без имени хоста) из ссылки на скачивание файла книги
"bookReadLink": "",
// включить(true)/выключить(false) журналирование
@@ -208,9 +244,6 @@ Options:
}
```
При необходимости, можно настроить нужный параметр в этом файле вручную. Параметры командной
строки имеют больший приоритет, чем настройки из `config.json`.
<a id="remotelib" />
### Удаленная библиотека
@@ -245,8 +278,8 @@ Options:
### Фильтр по авторам и книгам
При создании поисковой БД, во время загрузки и парсинга .inpx-файла, имеется возможность
отфильтровать авторов и книги, задав определенные критерии. Для этого небходимо создать
в рабочей директории (там же, где `config.json`) файл `filter.json` следующего вида:
отфильтровать авторов и книги, задав определенные критерии. По умолчанию, для этого небходимо создать
в директории конфигурационного файла (там же, где `config.json`) файл `filter.json` следующего вида:
```json
{
"info": {
@@ -291,8 +324,10 @@ Options:
}
```
Использование `filter` небезопасно, т.к. позволяет выполнить произвольный js-код внутри программы,
поэтому запуск приложения в этом случае должен сопровождаться дополнительным параметром командной строки `--unsafe-filter`.
поэтому запуск приложения в этом случае должен сопровождаться дополнительным параметром командной строки `--unsafe-filter`
или разрешением в конфиге `allowUnsafeFilter`.
Названия атрибутов inpxRec соответствуют названиям в нижнем регистре из структуры structure.info в .inpx-файле.
Файл `filter.json` можно расположить где угодно, что задается параметром `inpxFilterFile` в конфиге.
<a id="https" />
### Настройка https с помощью nginx

View File

@@ -3,6 +3,7 @@ const path = require('path');
const { execSync } = require('child_process');
const pckg = require('../package.json');
const platform = process.argv[2];
const distDir = path.resolve(__dirname, '../dist');
const outDir = `${distDir}/release`;
@@ -20,10 +21,14 @@ async function makeRelease(target) {
async function main() {
try {
await fs.emptyDir(outDir);
await makeRelease('win');
await makeRelease('linux');
await makeRelease('linux-arm64');
await makeRelease('macos');
if (platform) {
await makeRelease(platform);
} else {
await makeRelease('win');
await makeRelease('linux');
await makeRelease('linux-arm64');
await makeRelease('macos');
}
} catch(e) {
console.error(e);
process.exit(1);

View File

@@ -178,7 +178,18 @@ export default class BaseList {
if (this.list.liberamaReady) {
this.$emit('listEvent', {action: 'submitUrl', data: href});
} else {
const url = this.config.bookReadLink.replace('${DOWNLOAD_LINK}', href);
const bookReadLink = this.config.bookReadLink;
let url = bookReadLink;
if (bookReadLink.indexOf('${DOWNLOAD_LINK}') >= 0) {
url = bookReadLink.replace('${DOWNLOAD_LINK}', href);
} else if (bookReadLink.indexOf('${DOWNLOAD_URI}') >= 0) {
const hrefUrl = new URL(href);
const urlWithoutHost = hrefUrl.pathname + hrefUrl.search + hrefUrl.hash;
url = bookReadLink.replace('${DOWNLOAD_URI}', urlWithoutHost);
}
window.open(url, '_blank');
}
} else if (action == 'bookInfo') {

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "inpx-web",
"version": "1.5.6",
"version": "1.5.7",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "inpx-web",
"version": "1.5.6",
"version": "1.5.7",
"hasInstallScript": true,
"license": "CC0-1.0",
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "inpx-web",
"version": "1.5.6",
"version": "1.5.7",
"author": "Book Pauk <bookpauk@gmail.com>",
"license": "CC0-1.0",
"repository": "bookpauk/inpx-web",
@@ -17,6 +17,10 @@
"build:client-dev": "webpack --config build/webpack.dev.config.js",
"build:all": "npm run build:linux && npm run build:win && npm run build:macos && npm run build:linux-arm64",
"release": "npm run build:all && node build/release.js",
"release:linux": "npm run build:linux && node build/release.js linux",
"release:win": "npm run build:win && node build/release.js win",
"release:macos": "npm run build:macos && node build/release.js macos",
"release:arm64": "npm run build:linux-arm64 && node build/release.js arm64",
"postinstall": "npm run build:client-dev"
},
"bin": "server/index.js",

View File

@@ -10,9 +10,15 @@ module.exports = {
name: pckg.name,
execDir,
dataDir: '',
tempDir: '',
logDir: '',
libDir: '',
inpx: '',
inpxFilterFile: '',
allowConfigRewrite: false,
allowUnsafeFilter: false,
accessPassword: '',
accessTimeout: 0,
extendedSearch: true,

View File

@@ -5,8 +5,14 @@ const fs = require('fs-extra');
const branchFilename = __dirname + '/application_env';
const propsToSave = [
'dataDir',
'tempDir',
'logDir',
'libDir',
'inpx',
'inpxFilterFile',
'allowConfigRewrite',
'allowUnsafeFilter',
'accessPassword',
'accessTimeout',
'extendedSearch',
@@ -46,7 +52,7 @@ class ConfigManager {
return instance;
}
async init(dataDir) {
async init(tempDataDir, configFile) {
if (this.inited)
throw new Error('already inited');
@@ -63,14 +69,16 @@ class ConfigManager {
this.branchConfigFile = __dirname + `/${this.branch}.js`;
const config = require(this.branchConfigFile);
if (dataDir) {
config.dataDir = path.resolve(dataDir);
} else {
config.dataDir = `${config.execDir}/.${config.name}`;
if (!tempDataDir) {
tempDataDir = `${config.execDir}/.${config.name}`;
}
if (configFile) {
config.configFile = path.resolve(configFile);
} else {
config.configFile = `${tempDataDir}/config.json`;
}
await fs.ensureDir(config.dataDir);
this._userConfigFile = `${config.dataDir}/config.json`;
this._config = config;
this.inited = true;
@@ -86,37 +94,31 @@ class ConfigManager {
Object.assign(this._config, value);
}
get userConfigFile() {
return this._userConfigFile;
}
set userConfigFile(value) {
if (value)
this._userConfigFile = value;
}
async load() {
try {
if (!this.inited)
throw new Error('not inited');
if (await fs.pathExists(this.userConfigFile)) {
const data = JSON.parse(await fs.readFile(this.userConfigFile, 'utf8'));
if (await fs.pathExists(this._config.configFile)) {
const data = JSON.parse(await fs.readFile(this._config.configFile, 'utf8'));
const config = _.pick(data, propsToSave);
this.config = config;
//сохраним конфиг, если не все атрибуты присутствуют в файле конфига
for (const prop of propsToSave)
if (!Object.prototype.hasOwnProperty.call(config, prop)) {
await this.save();
break;
if (config.allowConfigRewrite) {
for (const prop of propsToSave) {
if (!Object.prototype.hasOwnProperty.call(config, prop)) {
await this.save();
break;
}
}
}
} else {
await this.save();
}
} catch(e) {
throw new Error(`Error while loading "${this.userConfigFile}": ${e.message}`);
throw new Error(`Error while loading "${this._config.configFile}": ${e.message}`);
}
}
@@ -125,7 +127,7 @@ class ConfigManager {
throw new Error('not inited');
const dataToSave = _.pick(this._config, propsToSave);
await fs.writeFile(this.userConfigFile, JSON.stringify(dataToSave, null, 4));
await fs.writeFile(this._config.configFile, JSON.stringify(dataToSave, null, 4));
}
}

View File

@@ -659,8 +659,6 @@ class WebWorker {
}
}
log(LM_WARN, `clean dir ${dir}, maxSize=${maxSize}, found ${files.length} files, total size=${size}`);
files.sort((a, b) => a.stat.mtimeMs - b.stat.mtimeMs);
let i = 0;
@@ -673,7 +671,10 @@ class WebWorker {
i++;
}
log(LM_WARN, `removed ${i} files`);
if (i) {
log(LM_WARN, `clean dir ${dir}, maxSize=${maxSize}, found ${files.length} files, total size=${size}`);
log(LM_WARN, `removed ${i} files`);
}
}
async periodicCleanDir(dirConfig) {
@@ -727,7 +728,7 @@ class WebWorker {
log('inpx file: changes found, recreating DB');
await this.recreateDb();
} else {
log('inpx file: no changes');
//log('inpx file: no changes');
}
} catch(e) {
log(LM_ERR, `periodicCheckInpx: ${e.message}`);

View File

@@ -1,5 +1,6 @@
const fs = require('fs-extra');
const path = require('path');
const os = require('os');
const express = require('express');
const http = require('http');
@@ -13,7 +14,7 @@ let log;
let config;
let argv;
let branch = '';
const argvStrings = ['host', 'port', 'app-dir', 'lib-dir', 'inpx'];
const argvStrings = ['host', 'port', 'config', 'data-dir', 'app-dir', 'lib-dir', 'inpx'];
function showHelp(defaultConfig) {
console.log(utils.versionText(defaultConfig));
@@ -21,24 +22,27 @@ function showHelp(defaultConfig) {
`Usage: ${defaultConfig.name} [options]
Options:
--help Print ${defaultConfig.name} command line options
--host=<ip> Set web server host, default: ${defaultConfig.server.host}
--port=<port> Set web server port, default: ${defaultConfig.server.port}
--app-dir=<dirpath> Set application working directory, default: <execDir>/.${defaultConfig.name}
--lib-dir=<dirpath> Set library directory, default: the same as ${defaultConfig.name} executable's
--inpx=<filepath> Set INPX collection file, default: the one that found in library dir
--recreate Force recreation of the search database on start
--help Print ${defaultConfig.name} command line options
--host=<ip> Set web server host, default: ${defaultConfig.server.host}
--port=<port> Set web server port, default: ${defaultConfig.server.port}
--config=<filepath> Set config filename, default: <dataDir>/config.json
--data-dir=<dirpath> (or --app-dir) Set application working directory, default: <execDir>/.${defaultConfig.name}
--lib-dir=<dirpath> Set library directory, default: the same as ${defaultConfig.name} executable's
--inpx=<filepath> Set INPX collection file, default: the one that found in library dir
--recreate Force recreation of the search database on start
--unsafe-filter Use filter config at your own risk
`
);
}
async function init() {
argv = require('minimist')(process.argv.slice(2), {string: argvStrings});
const dataDir = argv['app-dir'];
const argvDataDir = argv['data-dir'] || argv['app-dir'];
const configFile = argv['config'];
//config
const configManager = new (require('./config'))();//singleton
await configManager.init(dataDir);
await configManager.init(argvDataDir, configFile);
const defaultConfig = configManager.config;
await configManager.load();
@@ -46,8 +50,12 @@ async function init() {
branch = config.branch;
//dirs
config.tempDir = `${config.dataDir}/tmp`;
config.logDir = `${config.dataDir}/log`;
config.dataDir = config.dataDir || argvDataDir || `${config.execDir}/.${config.name}`;
config.tempDir = config.tempDir || `${config.dataDir}/tmp`;
if (config.tempDir === '${OS}')
config.tempDir = `${os.tmpdir()}/${config.name}`
config.logDir = config.logDir || `${config.dataDir}/log`;
config.publicDir = `${config.dataDir}/public`;
config.publicFilesDir = `${config.dataDir}/public-files`;
config.rootPathStatic = config.server.root || '';
@@ -127,14 +135,22 @@ async function init() {
}
config.recreateDb = argv.recreate || false;
config.inpxFilterFile = `${config.dataDir}/filter.json`;
config.allowUnsafeFilter = argv['unsafe-filter'] || false;
config.inpxFilterFile = config.inpxFilterFile || `${path.dirname(config.configFile)}/filter.json`;
config.allowUnsafeFilter = argv['unsafe-filter'] || config.allowUnsafeFilter || false;
//web app
if (branch !== 'development') {
const createWebApp = require('./createWebApp');
await createWebApp(config);
}
//log dirs
for (const prop of ['configFile', 'dataDir', 'tempDir', 'logDir']) {
log(`${prop}: ${config[prop]}`);
}
if (await fs.pathExists(config.inpxFilterFile))
log(`inpxFilterFile: ${config.inpxFilterFile}`)
}
function logQueries(app) {