Compare commits

...

14 Commits

Author SHA1 Message Date
Book Pauk
cafdb5b04b Merge branch 'release/1.2.0' 2024-03-25 12:54:05 +07:00
Book Pauk
697774978e Добавлена возможность задавать в конфиге любую ссылку для кнопки "Сетевая библиотека", параметр networkLibraryLink (#47) 2024-03-25 12:52:46 +07:00
Book Pauk
8c2c2fe2fc 1.2.0 2023-12-07 16:31:13 +07:00
Book Pauk
e3770463a1 В списке загруженных, книга в архив (из архива) переносится теперь со всей группой своих версий 2023-12-07 16:26:30 +07:00
Book Pauk
d3ad23e9e4 Актуализация пакетов 2023-12-07 15:01:26 +07:00
Book Pauk
79d1e0b30d Merge tag '1.1.3' into develop
1.1.3
2023-02-06 19:48:02 +07:00
Book Pauk
1370bae4d6 Merge branch 'release/1.1.3' 2023-02-06 19:47:56 +07:00
Book Pauk
01fbdf38fa Версия 1.1.3 2023-02-06 19:47:28 +07:00
Book Pauk
be6b07a0cf Исправление бага при обнулении libs 2023-02-06 19:45:13 +07:00
Book Pauk
1b057029c8 Улучшено хранение ключа доступа 2023-02-05 16:04:52 +07:00
Book Pauk
b6b567f20b Улучшение парсинга невалидных fb2 2023-02-03 17:30:22 +07:00
Book Pauk
c4c109fe0e Мелкий рефакторинг 2023-02-03 16:28:24 +07:00
Book Pauk
4c8c921b03 Улучшения механизма запуска периодических задач 2023-02-03 16:23:13 +07:00
Book Pauk
69a2e5cda3 Merge tag '1.1.2-1' into develop
1.1.2-1
2023-01-25 17:06:39 +07:00
14 changed files with 5014 additions and 4147 deletions

View File

@@ -115,6 +115,10 @@ Options:
// Подключение себя, как клиента, к серверу обновлений // Подключение себя, как клиента, к серверу обновлений
"bucServer": false "bucServer": false
// Сcылка для открытия в новом окне брауpера по клику на кнопку "Сетевая библиотека"
// Если не задано, открывается внутренний менеджер библиотек с использванием фрейма
"networkLibraryLink": "http://samlib.ru/"
} }
``` ```

View File

@@ -4,7 +4,7 @@ class Misc {
async loadConfig() { async loadConfig() {
const query = {params: [ const query = {params: [
'name', 'version', 'mode', 'maxUploadFileSize', 'useExternalBookConverter', 'acceptFileExt', 'bucEnabled', 'branch', 'name', 'version', 'mode', 'maxUploadFileSize', 'useExternalBookConverter', 'acceptFileExt', 'bucEnabled', 'branch', 'networkLibraryLink',
]}; ]};
const config = await wsc.message(await wsc.send(Object.assign({action: 'get-config'}, query))); const config = await wsc.message(await wsc.send(Object.assign({action: 'get-config'}, query)));

View File

@@ -34,8 +34,8 @@ class LibsPage {
if (!this.mode) if (!this.mode)
return; return;
//TODO: убрать второе условие в 24г //TODO: убрать условие с mode в 24г
if (!this.libs || (this.mode === 'omnireader' && this.libs.mode !== this.mode)) { if (!this.libs || !this.libs.groups || (this.mode === 'omnireader' && this.libs.mode !== this.mode)) {
const defaults = rstore.getLibsDefaults(this.mode); const defaults = rstore.getLibsDefaults(this.mode);
this.commit('reader/setLibs', defaults); this.commit('reader/setLibs', defaults);
} }

View File

@@ -393,6 +393,9 @@ class Reader {
this.recentItemKeys = []; this.recentItemKeys = [];
//сохранение в удаленном хранилище //сохранение в удаленном хранилище
await this.$refs.serverStorage.saveRecent(itemKeys); await this.$refs.serverStorage.saveRecent(itemKeys);
//periodicTasks
this.periodicTasks();//no await
} catch (e) { } catch (e) {
if (!this.offlineModeActive) if (!this.offlineModeActive)
this.$root.notify.error(e.message); this.$root.notify.error(e.message);
@@ -442,26 +445,15 @@ class Reader {
this.$refs.recentBooksPage.init(); this.$refs.recentBooksPage.init();
})(); })();
//проверки обновлений читалки //единственный запуск periodicTasks при инициализации
//дальнейшие запуски periodicTasks выполняются из debouncedSaveRecent
//т.е. только по действию пользователя
(async() => { (async() => {
await utils.sleep(15*1000);
this.isFirstNeedUpdateNotify = true; this.isFirstNeedUpdateNotify = true;
//вечный цикл, запрашиваем периодически конфиг для проверки выхода новой версии читалки
while (1) {// eslint-disable-line no-constant-condition
await this.checkNewVersionAvailable();
await utils.sleep(60*60*1000); //каждый час
}
//дальше хода нет
})();
//проверки обновлений книг this.allowPeriodicTasks = true;
(async() => { this.periodicTasks();//no await
await utils.sleep(15*1000); //подождем неск. секунд перед первым запросом
//вечный цикл, запрашиваем периодически обновления
while (1) {// eslint-disable-line no-constant-condition
await this.checkBuc();
await utils.sleep(70*60*1000); //каждые 70 минут
}
//дальше хода нет
})(); })();
} }
@@ -560,26 +552,56 @@ class Reader {
} }
} }
async checkNewVersionAvailable() { async periodicTasks() {
if (!this.checkingNewVersion && this.showNeedUpdateNotify) { if (!this.allowPeriodicTasks || this.doingPeriodicTasks)
this.checkingNewVersion = true; return;
try {
await utils.sleep(15*1000); //подождем 15 секунд, чтобы прогрузился ServiceWorker при выходе новой версии
const config = await miscApi.loadConfig();
this.commit('config/setConfig', config);
let againMes = ''; this.doingPeriodicTasks = true;
if (this.isFirstNeedUpdateNotify) { try {
againMes = ' еще один раз'; if (!this.taskList) {
const taskArr = [
[this.checkNewVersionAvailable, 60], //проверки обновлений читалки, каждый час
[this.checkBuc, 70], //проверки обновлений книг, каждые 70 минут
];
this.taskList = [];
for (const task of taskArr) {
const [method, period] = task;
this.taskList.push({method, period, lastRunTime: 0});
} }
}
for (const task of this.taskList) {
if (Date.now() - task.lastRunTime >= task.period*60*1000) {
try {
//console.log('task run', task.method.name);
await task.method();
} catch (e) {
console.error(e);
}
task.lastRunTime = Date.now();
}
}
} catch (e) {
console.error(e);
} finally {
this.doingPeriodicTasks = false;
}
}
async checkNewVersionAvailable() {
if (this.showNeedUpdateNotify) {
const config = await miscApi.loadConfig();
this.commit('config/setConfig', config);
let againMes = '';
if (this.isFirstNeedUpdateNotify) {
againMes = ' еще один раз';
}
if (this.version != this.clientVersion)
this.$root.notify.info(`Вышла новая версия (v${this.version}) читалки.<br>Пожалуйста, обновите страницу${againMes}.`, 'Обновление');
if (this.version != this.clientVersion)
this.$root.notify.info(`Вышла новая версия (v${this.version}) читалки.<br>Пожалуйста, обновите страницу${againMes}.`, 'Обновление');
} catch(e) {
console.error(e);
} finally {
this.checkingNewVersion = false;
}
this.isFirstNeedUpdateNotify = false; this.isFirstNeedUpdateNotify = false;
} }
} }
@@ -588,82 +610,78 @@ class Reader {
if (!this.bothBucEnabled) if (!this.bothBucEnabled)
return; return;
try { const sorted = bookManager.getSortedRecent();
const sorted = bookManager.getSortedRecent();
//выберем все кандидиаты на обновление //выберем все кандидиаты на обновление
const updateUrls = new Set(); const updateUrls = new Set();
for (const book of sorted) { for (const book of sorted) {
if (!book.deleted && book.checkBuc && book.url && book.url.indexOf('disk://') !== 0) if (!book.deleted && book.checkBuc && book.url && book.url.indexOf('disk://') !== 0)
updateUrls.add(book.url); updateUrls.add(book.url);
}
//теперь по кусочкам запросим сервер
const arr = Array.from(updateUrls);
const bucSize = {};
const chunkSize = 100;
for (let i = 0; i < arr.length; i += chunkSize) {
const chunk = arr.slice(i, i + chunkSize);
const data = await readerApi.checkBuc(chunk);
for (const item of data) {
bucSize[item.id] = item.size;
} }
//теперь по кусочкам запросим сервер await utils.sleep(1000);//чтобы не ддосить сервер
const arr = Array.from(updateUrls); }
const bucSize = {};
const chunkSize = 100;
for (let i = 0; i < arr.length; i += chunkSize) {
const chunk = arr.slice(i, i + chunkSize);
const data = await readerApi.checkBuc(chunk); const checkSetTime = {};
//проставим новые размеры у книг
for (const item of data) { for (const book of sorted) {
bucSize[item.id] = item.size; if (book.deleted)
} continue;
await utils.sleep(1000);//чтобы не ддосить сервер //размер 0 считаем отсутствующим
if (book.url && bucSize[book.url] && bucSize[book.url] !== book.bucSize) {
book.bucSize = bucSize[book.url];
await bookManager.recentSetItem(book);
} }
const checkSetTime = {}; //подготовка к следующему шагу, ищем книгу по url с максимальной датой установки checkBucTime/loadTime
//проставим новые размеры у книг //от этой даты будем потом отсчитывать bucCancelDays
for (const book of sorted) { if (updateUrls.has(book.url)) {
if (book.deleted) let rec = checkSetTime[book.url] || {time: 0, loadTime: 0};
continue;
//размер 0 считаем отсутствующим
if (book.url && bucSize[book.url] && bucSize[book.url] !== book.bucSize) {
book.bucSize = bucSize[book.url];
await bookManager.recentSetItem(book);
}
//подготовка к следующему шагу, ищем книгу по url с максимальной датой установки checkBucTime/loadTime const time = (book.checkBucTime ? book.checkBucTime : (rec.loadTime || 0));
//от этой даты будем потом отсчитывать bucCancelDays if (time > rec.time || (time == rec.time && (book.loadTime > rec.loadTime)))
if (updateUrls.has(book.url)) { rec = {time, loadTime: book.loadTime, key: book.key};
let rec = checkSetTime[book.url] || {time: 0, loadTime: 0};
const time = (book.checkBucTime ? book.checkBucTime : (rec.loadTime || 0)); checkSetTime[book.url] = rec;
if (time > rec.time || (time == rec.time && (book.loadTime > rec.loadTime)))
rec = {time, loadTime: book.loadTime, key: book.key};
checkSetTime[book.url] = rec;
}
} }
}
//bucCancelEnabled и bucCancelDays //bucCancelEnabled и bucCancelDays
//снимем флаг checkBuc у необновлявшихся bucCancelDays //снимем флаг checkBuc у необновлявшихся bucCancelDays
if (this.bucCancelEnabled) { if (this.bucCancelEnabled) {
for (const rec of Object.values(checkSetTime)) { for (const rec of Object.values(checkSetTime)) {
if (rec.time && Date.now() - rec.time > this.bucCancelDays*24*3600*1000) { if (rec.time && Date.now() - rec.time > this.bucCancelDays*24*3600*1000) {
const book = await bookManager.getRecentBook({key: rec.key}); const book = await bookManager.getRecentBook({key: rec.key});
const needBookUpdate = const needBookUpdate =
book.checkBuc book.checkBuc
&& book.bucSize && book.bucSize
&& utils.hasProp(book, 'downloadSize') && utils.hasProp(book, 'downloadSize')
&& book.bucSize !== book.downloadSize && book.bucSize !== book.downloadSize
&& (book.bucSize - book.downloadSize >= this.bucSizeDiff) && (book.bucSize - book.downloadSize >= this.bucSizeDiff)
; ;
if (book && !needBookUpdate) { if (book && !needBookUpdate) {
await bookManager.setCheckBuc(book, undefined);//!!! await bookManager.setCheckBuc(book, undefined);//!!!
}
} }
} }
} }
await this.$refs.recentBooksPage.updateTableData();
} catch (e) {
console.error(e);
} }
await this.$refs.recentBooksPage.updateTableData();
} }
updateCountChanged(event) { updateCountChanged(event) {
@@ -1014,6 +1032,11 @@ class Reader {
} }
libsToogle() { libsToogle() {
if (this.config.networkLibraryLink) {
window.open(this.config.networkLibraryLink, '_blank');
return;
}
this.libsActive = !this.libsActive; this.libsActive = !this.libsActive;
if (this.libsActive) { if (this.libsActive) {
this.$refs.libsPage.init();//no await this.$refs.libsPage.init();//no await
@@ -1361,6 +1384,7 @@ class Reader {
found = (found ? _.cloneDeep(found) : found); found = (found ? _.cloneDeep(found) : found);
if (found) { if (found) {
//если такой файл уже не загружен (path не совпадают)
if (wasOpened.sameBookKey != found.sameBookKey) { if (wasOpened.sameBookKey != found.sameBookKey) {
//спрашиваем, надо ли объединить файлы //спрашиваем, надо ли объединить файлы
const askResult = bookManager.keysEqual(found.path, addedBook.path) || const askResult = bookManager.keysEqual(found.path, addedBook.path) ||
@@ -1409,8 +1433,6 @@ class Reader {
if (!this.showHelpOnErrorIfNeeded(url)) { if (!this.showHelpOnErrorIfNeeded(url)) {
this.$root.stdDialog.alert(e.message, 'Ошибка', {color: 'negative'}); this.$root.stdDialog.alert(e.message, 'Ошибка', {color: 'negative'});
} }
} finally {
this.checkNewVersionAvailable();
} }
} }

View File

@@ -201,7 +201,7 @@
<div <div
class="del-button self-end row justify-center items-center clickable" class="del-button self-end row justify-center items-center clickable"
@click="handleDel(item.key)" @click="handleDel(item)"
> >
<q-icon class="la la-times" size="12px" /> <q-icon class="la la-times" size="12px" />
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%"> <q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">
@@ -212,7 +212,7 @@
<div <div
v-show="showArchive" v-show="showArchive"
class="restore-button self-start row justify-center items-center clickable" class="restore-button self-start row justify-center items-center clickable"
@click="handleRestore(item.key)" @click="handleRestore(item)"
> >
<q-icon class="la la-arrow-left" size="14px" /> <q-icon class="la la-arrow-left" size="14px" />
<q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%"> <q-tooltip :delay="1500" anchor="bottom middle" content-style="font-size: 80%">
@@ -593,21 +593,46 @@ class RecentBooksPage {
} }
} }
async handleDel(key) { async handleDel(item) {
if (!this.showArchive) { if (item.group) {
await bookManager.delRecentBook({key}); const keys = [{key: item.key}];
this.$root.notify.info('Перенесено в архив'); for (const book of item.group)
keys.push({key: book.key});
if (!this.showArchive) {
await bookManager.delRecentBooks(keys);
this.$root.notify.info(`Группа книг (всего ${keys.length}) перенесена в архив`);
} else {
if (await this.$root.stdDialog.confirm(`Подтвердите удаление группы книг (всего ${keys.length}) из архива:`, ' ')) {
await bookManager.delRecentBooks(keys, 2);
this.$root.notify.info('Группа книг удалена безвозвратно');
}
}
} else { } else {
if (await this.$root.stdDialog.confirm('Подтвердите удаление из архива:', ' ')) { if (!this.showArchive) {
await bookManager.delRecentBook({key}, 2); await bookManager.delRecentBooks([{key: item.key}]);
this.$root.notify.info('Удалено безвозвратно'); this.$root.notify.info('Книга перенесена в архив');
} else {
if (await this.$root.stdDialog.confirm('Подтвердите удаление книги из архива:', ' ')) {
await bookManager.delRecentBooks([{key: item.key}], 2);
this.$root.notify.info('Книга удалено безвозвратно');
}
} }
} }
} }
async handleRestore(key) { async handleRestore(item) {
await bookManager.restoreRecentBook({key}); if (item.group) {
this.$root.notify.info('Восстановлено из архива'); const keys = [{key: item.key}];
for (const book of item.group)
keys.push({key: book.key});
await bookManager.restoreRecentBooks(keys);
this.$root.notify.info(`Группа книг (всего ${keys.length}) восстановлена из архива`);
} else {
await bookManager.restoreRecentBooks([{key: item.key}]);
this.$root.notify.info('Книга восстановлена из архива');
}
} }
async loadBook(item, force = false) { async loadBook(item, force = false) {

View File

@@ -22,10 +22,12 @@ const ssCacheStore = localForage.createInstance({
const componentOptions = { const componentOptions = {
watch: { watch: {
serverSyncEnabled: function() { serverSyncEnabled: function() {
this.serverSyncEnabledChanged(); if (this.inited)
this.serverSyncEnabledChanged();
}, },
serverStorageKey: function() { serverStorageKey: function() {
this.serverStorageKeyChanged(true); if (this.inited)
this.serverStorageKeyChanged(true);
}, },
settings: function() { settings: function() {
this.debouncedSaveSettings(); this.debouncedSaveSettings();
@@ -85,6 +87,13 @@ class ServerStorage {
if (!this.cachedRecentMod) if (!this.cachedRecentMod)
await this.cleanCachedRecent('cachedRecentMod'); await this.cleanCachedRecent('cachedRecentMod');
//подстраховка хранения ключа, восстановим из IndexedDB при проблемах в localStorage
if (!this.serverStorageKey) {
const key = await ssCacheStore.getItem('storageKey');
if (key)
this.commit('reader/setServerStorageKey', key);
}
if (!this.serverStorageKey) { if (!this.serverStorageKey) {
//генерируем новый ключ //генерируем новый ключ
await this.generateNewServerStorageKey(); await this.generateNewServerStorageKey();
@@ -123,6 +132,7 @@ class ServerStorage {
async generateNewServerStorageKey() { async generateNewServerStorageKey() {
const key = utils.toBase58(utils.randomArray(32)); const key = utils.toBase58(utils.randomArray(32));
this.commit('reader/setServerStorageKey', key); this.commit('reader/setServerStorageKey', key);
//дождемся serverStorageKeyChanged, событие по watch не работает при this.inited == false
await this.serverStorageKeyChanged(true); await this.serverStorageKeyChanged(true);
} }
@@ -141,6 +151,10 @@ class ServerStorage {
async serverStorageKeyChanged(force) { async serverStorageKeyChanged(force) {
if (this.prevServerStorageKey != this.serverStorageKey) { if (this.prevServerStorageKey != this.serverStorageKey) {
this.prevServerStorageKey = this.serverStorageKey; this.prevServerStorageKey = this.serverStorageKey;
//сохраним ключ также в IndexedDB, чтобы была возможность восстановить при проблемах с localStorage
await ssCacheStore.setItem('storageKey', this.serverStorageKey);
this.hashedStorageKey = utils.toBase58(cryptoUtils.sha256(this.serverStorageKey)); this.hashedStorageKey = utils.toBase58(cryptoUtils.sha256(this.serverStorageKey));
this.keyInited = true; this.keyInited = true;

View File

@@ -438,61 +438,63 @@ export default class BookParser {
}; };
const onEndNode = (elemName) => {// eslint-disable-line no-unused-vars const onEndNode = (elemName) => {// eslint-disable-line no-unused-vars
if (tag == elemName) { tag = elemName;
if (tag == 'binary') {
binaryId = '';
}
if (path.indexOf('/fictionbook/body') == 0) {
if (tag == 'title') {
isFirstTitlePara = false;
bold = false;
center = false;
inTitle = false;
}
if (tag == 'section') { if (tag == 'binary') {
sectionLevel--; binaryId = '';
} }
if (tag == 'emphasis' || tag == 'strong' || tag == 'sup' || tag == 'sub') { if (path.indexOf('/fictionbook/body') == 0) {
growParagraph(`</${tag}>`, 0); if (tag == 'title') {
} isFirstTitlePara = false;
bold = false;
if (tag == 'p') { center = false;
inPara = false; inTitle = false;
}
if (tag == 'subtitle') {
isFirstTitlePara = false;
bold = false;
center = false;
inSubtitle = false;
}
if (tag == 'epigraph' || tag == 'annotation') {
italic = false;
space -= 1;
newParagraph();
}
if (tag == 'stanza') {
newParagraph();
}
if (tag == 'text-author') {
bold = false;
space -= 1;
}
} }
path = path.substr(0, path.length - tag.length - 1); if (tag == 'section') {
let i = path.lastIndexOf('/'); sectionLevel--;
if (i >= 0) { }
tag = path.substr(i + 1);
} else { if (tag == 'emphasis' || tag == 'strong' || tag == 'sup' || tag == 'sub') {
growParagraph(`</${tag}>`, 0);
}
if (tag == 'p') {
inPara = false;
}
if (tag == 'subtitle') {
isFirstTitlePara = false;
bold = false;
center = false;
inSubtitle = false;
}
if (tag == 'epigraph' || tag == 'annotation') {
italic = false;
space -= 1;
newParagraph();
}
if (tag == 'stanza') {
newParagraph();
}
if (tag == 'text-author') {
bold = false;
space -= 1;
}
}
let i = path.lastIndexOf(tag);
if (i >= 0) {
path = path.substring(0, i - 1);
i = path.lastIndexOf('/');
if (i >= 0)
tag = path.substring(i + 1);
else
tag = path; tag = path;
}
} }
}; };

View File

@@ -467,7 +467,7 @@ class BookManager {
async getRecentBook(value) { async getRecentBook(value) {
return this.recent[value.key]; return this.recent[value.key];
} }
/*
async delRecentBook(value, delFlag = 1) { async delRecentBook(value, delFlag = 1) {
const item = this.recent[value.key]; const item = this.recent[value.key];
item.deleted = delFlag; item.deleted = delFlag;
@@ -479,13 +479,37 @@ class BookManager {
await this.recentSetItem(item); await this.recentSetItem(item);
this.emit('recent-deleted', value.key); this.emit('recent-deleted', value.key);
} }
*/
async delRecentBooks(values, delFlag = 1) {
for (const value of values) {
const item = this.recent[value.key];
item.deleted = delFlag;
if (this.recentLastKey == value.key) {
await this.recentSetLastKey(null);
}
await this.recentSetItem(item);
}
this.emit('recent-deleted');
}
/*
async restoreRecentBook(value) { async restoreRecentBook(value) {
const item = this.recent[value.key]; const item = this.recent[value.key];
item.deleted = 0; item.deleted = 0;
await this.recentSetItem(item); await this.recentSetItem(item);
} }
*/
async restoreRecentBooks(values) {
for (const value of values) {
const item = this.recent[value.key];
item.deleted = 0;
await this.recentSetItem(item);
}
}
async setCheckBuc(value, checkBuc) { async setCheckBuc(value, checkBuc) {
const item = this.recent[value.key]; const item = this.recent[value.key];

View File

@@ -1,4 +1,31 @@
export const versionHistory = [ export const versionHistory = [
{
version: '1.2.0',
releaseDate: '2024-03-25',
showUntil: '2024-03-24',
content:
`
<ul>
<li>в списке загруженных, книга в архив (из архива) переносится теперь со всей группой своих версий</li>
<li>добавлена возможность задавать в конфиге любую ссылку для кнопки "Сетевая библиотека", параматр networkLibraryLink (#47)</li>
</ul>
`
},
{
version: '1.1.3',
releaseDate: '2023-02-06',
showUntil: '2023-02-05',
content:
`
<ul>
<li>исправление багов</li>
</ul>
`
},
{ {
version: '1.1.2', version: '1.1.2',
releaseDate: '2023-01-22', releaseDate: '2023-01-22',

View File

@@ -325,7 +325,7 @@ const state = {
currentProfile: '', currentProfile: '',
settings: _.cloneDeep(settingDefaults), settings: _.cloneDeep(settingDefaults),
settingsRev: {}, settingsRev: {},
libs: false, libs: {},
libsRev: 0, libsRev: 0,
}; };

8639
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "liberama", "name": "liberama",
"version": "1.1.2", "version": "1.2.0",
"author": "Book Pauk <bookpauk@gmail.com>", "author": "Book Pauk <bookpauk@gmail.com>",
"license": "CC0-1.0", "license": "CC0-1.0",
"repository": "bookpauk/liberama", "repository": "bookpauk/liberama",
@@ -25,41 +25,41 @@
"scripts": "server/config/*.js" "scripts": "server/config/*.js"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.20.5", "@babel/core": "^7.23.5",
"@babel/eslint-parser": "^7.19.1", "@babel/eslint-parser": "^7.23.3",
"@babel/eslint-plugin": "^7.19.1", "@babel/eslint-plugin": "^7.23.5",
"@babel/plugin-proposal-decorators": "^7.20.5", "@babel/plugin-proposal-decorators": "^7.23.5",
"@babel/preset-env": "^7.20.2", "@babel/preset-env": "^7.23.5",
"@vue/compiler-sfc": "^3.2.22", "@vue/compiler-sfc": "^3.2.22",
"babel-loader": "^9.1.0", "babel-loader": "^9.1.3",
"copy-webpack-plugin": "^11.0.0", "copy-webpack-plugin": "^11.0.0",
"css-loader": "^6.7.3", "css-loader": "^6.8.1",
"css-minimizer-webpack-plugin": "^4.2.2", "css-minimizer-webpack-plugin": "^4.2.2",
"eslint": "^8.29.0", "eslint": "^8.55.0",
"eslint-plugin-vue": "^9.8.0", "eslint-plugin-vue": "^9.19.2",
"html-webpack-plugin": "^5.5.0", "html-webpack-plugin": "^5.5.4",
"mini-css-extract-plugin": "^2.7.2", "mini-css-extract-plugin": "^2.7.6",
"pkg": "^5.8.0", "pkg": "^5.8.1",
"showdown": "^2.1.0", "showdown": "^2.1.0",
"terser-webpack-plugin": "^5.3.6", "terser-webpack-plugin": "^5.3.9",
"vue-eslint-parser": "^9.1.0", "vue-eslint-parser": "^9.3.2",
"vue-loader": "^17.0.1", "vue-loader": "^17.3.1",
"vue-style-loader": "^4.1.3", "vue-style-loader": "^4.1.3",
"webpack": "^5.75.0", "webpack": "^5.89.0",
"webpack-cli": "^5.0.1", "webpack-cli": "^5.1.4",
"webpack-dev-middleware": "^6.0.1", "webpack-dev-middleware": "^6.1.1",
"webpack-hot-middleware": "^2.25.3", "webpack-hot-middleware": "^2.25.4",
"webpack-merge": "^5.8.0", "webpack-merge": "^5.10.0",
"workbox-webpack-plugin": "^6.5.4" "workbox-webpack-plugin": "^6.6.0"
}, },
"dependencies": { "dependencies": {
"@quasar/extras": "^1.15.8", "@quasar/extras": "^1.16.9",
"@vue/compat": "^3.2.45", "@vue/compat": "^3.3.10",
"axios": "^0.27.2", "axios": "^0.27.2",
"base-x": "^4.0.0", "base-x": "^4.0.0",
"chardet": "^1.5.0", "chardet": "^1.6.0",
"compression": "^1.7.4", "compression": "^1.7.4",
"dayjs": "^1.11.7", "dayjs": "^1.11.10",
"express": "^4.18.2", "express": "^4.18.2",
"fg-loadcss": "^3.1.0", "fg-loadcss": "^3.1.0",
"fs-extra": "^10.1.0", "fs-extra": "^10.1.0",
@@ -68,23 +68,23 @@
"jembadb": "^5.1.7", "jembadb": "^5.1.7",
"localforage": "^1.10.0", "localforage": "^1.10.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"minimist": "^1.2.7", "minimist": "^1.2.8",
"multer": "^1.4.5-lts.1", "multer": "^1.4.5-lts.1",
"pako": "^2.1.0", "pako": "^2.1.0",
"path-browserify": "^1.0.1", "path-browserify": "^1.0.1",
"pidusage": "^3.0.2", "pidusage": "^3.0.2",
"quasar": "^2.10.2", "quasar": "^2.14.1",
"safe-buffer": "^5.2.1", "safe-buffer": "^5.2.1",
"sanitize-html": "^2.8.0", "sanitize-html": "^2.11.0",
"sjcl": "^1.0.8", "sjcl": "^1.0.8",
"tar-fs": "^2.1.1", "tar-fs": "^2.1.1",
"unbzip2-stream": "^1.4.3", "unbzip2-stream": "^1.4.3",
"vue": "^3.2.37", "vue": "^3.2.37",
"vue-router": "^4.1.6", "vue-router": "^4.2.5",
"vuex": "^4.1.0", "vuex": "^4.1.0",
"vuex-persist": "^3.1.3", "vuex-persist": "^3.1.3",
"webdav": "^4.11.2", "webdav": "^4.11.3",
"ws": "^8.11.0", "ws": "^8.14.2",
"zip-stream": "^4.1.0" "zip-stream": "^4.1.1"
} }
} }

View File

@@ -18,7 +18,7 @@ module.exports = {
useExternalBookConverter: false, useExternalBookConverter: false,
acceptFileExt: '.fb2, .fb3, .html, .txt, .zip, .bz2, .gz, .rar, .epub, .mobi, .rtf, .doc, .docx, .pdf, .djvu, .jpg, .jpeg, .png', acceptFileExt: '.fb2, .fb3, .html, .txt, .zip, .bz2, .gz, .rar, .epub, .mobi, .rtf, .doc, .docx, .pdf, .djvu, .jpg, .jpeg, .png',
webConfigParams: ['name', 'version', 'mode', 'maxUploadFileSize', 'useExternalBookConverter', 'acceptFileExt', 'bucEnabled', 'branch'], webConfigParams: ['name', 'version', 'mode', 'maxUploadFileSize', 'useExternalBookConverter', 'acceptFileExt', 'bucEnabled', 'branch', 'networkLibraryLink'],
jembaDb: [ jembaDb: [
{ {
@@ -74,5 +74,6 @@ module.exports = {
accessToken: '', accessToken: '',
} }
*/ */
networkLibraryLink: '',
}; };

View File

@@ -14,6 +14,7 @@ const propsToSave = [
'remoteStorage', 'remoteStorage',
'bucEnabled', 'bucEnabled',
'bucServer', 'bucServer',
'networkLibraryLink',
]; ];
let instance = null; let instance = null;