Compare commits

...

33 Commits

Author SHA1 Message Date
Book Pauk
083151460a Merge branch 'release/0.12.2-3' 2022-10-05 17:59:10 +07:00
Book Pauk
c8f97ef386 Решение проблемы 'unable to verify the first certificate' для некоторых сайтов с валидным сертификатом 2022-10-05 17:58:39 +07:00
Book Pauk
c9a22a5eaf Merge tag '0.12.2-2' into develop
0.12.2-2
2022-10-05 15:16:29 +07:00
Book Pauk
f926732070 Merge branch 'release/0.12.2-2' 2022-10-05 15:16:24 +07:00
Book Pauk
3fbe6e9d9b Улучшение обработки ошибок 2022-10-05 15:15:26 +07:00
Book Pauk
225230381f Добавлена чистка output перед сборкой 2022-10-01 13:39:02 +07:00
Book Pauk
b58d3a1b8b Поправки параметров CopyWebpackPlugin 2022-09-20 20:21:41 +07:00
Book Pauk
ffedce4351 Поправки обработки ошибок сервера 2022-09-12 15:23:22 +07:00
Book Pauk
a4fdb67913 Merge tag '0.12.2-1' into develop
0.12.2-1
2022-09-04 21:44:06 +07:00
Book Pauk
6ba46421b9 Merge branch 'release/0.12.2-1' 2022-09-04 21:43:54 +07:00
Book Pauk
d201961046 Поправка положения notify-сообщений 2022-09-04 21:42:50 +07:00
Book Pauk
614a7f9da7 Merge tag '0.12.2' into develop
0.12.2
2022-09-04 21:22:39 +07:00
Book Pauk
113ab3e596 Merge branch 'release/0.12.2' 2022-09-04 21:22:28 +07:00
Book Pauk
c95870bfe5 Добавлено сохранение во vuex настройки offlineModeActive 2022-09-04 21:20:21 +07:00
Book Pauk
e69e9335f9 Исправлен баг с формой для доната, показывалась каждый день, а не каждый месяц 2022-09-04 21:19:30 +07:00
Book Pauk
fd21cd77dd Node 16 2022-09-01 21:13:31 +07:00
Book Pauk
d1880acaf9 Merge tag '0.12.1' into develop
0.12.1
2022-09-01 21:10:57 +07:00
Book Pauk
428b507257 Merge branch 'release/0.12.1' 2022-09-01 21:10:52 +07:00
Book Pauk
043dab0731 Версия 0.12.1 2022-09-01 21:08:56 +07:00
Book Pauk
a7b4d9c0d8 Добавлена форма доната 2022-09-01 21:05:22 +07:00
Book Pauk
6f9c95e351 Переход на node 16, актуализация пакетов 2022-09-01 15:36:28 +07:00
Book Pauk
7a53063ea8 Исправление багов 2022-09-01 15:31:16 +07:00
Book Pauk
ec4d5cac4f Поправлен баг 2022-08-16 23:40:40 +07:00
Book Pauk
f8557cba88 Исправление багов 2022-08-05 02:25:45 +07:00
Book Pauk
5dead039f5 Дебаг 2022-08-05 01:09:47 +07:00
Book Pauk
ea38392df4 Дебаг 2022-08-05 00:57:18 +07:00
Book Pauk
0cc9d90a94 Поправлен мелкий баг 2022-08-05 00:31:56 +07:00
Book Pauk
8c7b86c458 Поправлен баг 2022-08-05 00:16:54 +07:00
Book Pauk
0e29546fc5 Добавлены таймауты 2022-08-04 23:53:46 +07:00
Book Pauk
c9fa90d07c Поправлен donate-адрес 2022-08-04 15:08:43 +07:00
Book Pauk
7d8e0525b1 Активировал DonateHelpPage 2022-08-04 15:03:48 +07:00
Book Pauk
ddf69876a6 Добавлено сообщение при изменении чекбокса проверки обновления 2022-08-04 13:23:32 +07:00
Book Pauk
1d78e75e38 Merge tag '0.12.0-2' into develop
0.12.0-2
2022-08-03 15:58:49 +07:00
26 changed files with 1209 additions and 1002 deletions

View File

@@ -1,43 +1,43 @@
# Liberama # Liberama
Браузерная онлайн-читалка книг и децентрализованная библиотека. Браузерная онлайн-читалка книг и децентрализованная библиотека.
Читалка <img src="https://omnireader.ru/favicon.ico" width="14px"/>[OmniReader](https://omnireader.ru) является частью данного проекта, размещенной на VPS: Читалка <img src="https://omnireader.ru/favicon.ico" width="14px"/>[OmniReader](https://omnireader.ru) является частью данного проекта, размещенной на VPS:
![](docs/assets/face.jpg) ![](docs/assets/face.jpg)
![](docs/assets/reader.jpg) ![](docs/assets/reader.jpg)
## VPS ## VPS
Для разворачивания читалки на чистом VPS с нуля смотрите [docs/omnireader.ru](docs/omnireader.ru/README.md) Для разворачивания читалки на чистом VPS с нуля смотрите [docs/omnireader.ru](docs/omnireader.ru/README.md)
## Сборка проекта ## Сборка проекта
Необходима версия node.js не ниже 14. Необходима версия node.js не ниже 14.
``` ```
$ git clone https://github.com/bookpauk/liberama $ git clone https://github.com/bookpauk/liberama
$ cd liberama $ cd liberama
$ npm i $ npm i
``` ```
### Windows ### Windows
``` ```
$ npm run build:win $ npm run build:win
``` ```
### Linux ### Linux
``` ```
$ npm run build:linux $ npm run build:linux
``` ```
Результат сборки будет доступен в каталоге `dist/linux|win` в виде исполнимого (standalone) файла Результат сборки будет доступен в каталоге `dist/linux|win` в виде исполнимого (standalone) файла
### Разработка ### Разработка
``` ```
$ npm run dev $ npm run dev
``` ```
## Помочь проекту ## Помочь проекту
* bitcoin: 3EbgZ7MK1UVaN38Gty5DCBtS4PknM4Ut85 * bitcoin: bc1q3tyumaj648pp2e69jalsez2lnt462ttc33nup9
* litecoin: MP39Riec4oSNB3XMjiquKoLWxbufRYNXxZ * litecoin: MP39Riec4oSNB3XMjiquKoLWxbufRYNXxZ
* monero: 8BQPnvHcPSHM5gMQsmuypDgx9NNsYqwXKfDDuswEyF2Q2ewQSfd2pkK6ydH2wmMyq2JViZvy9DQ35hLMx7g72mFWNJTPtnz * monero: 8BQPnvHcPSHM5gMQsmuypDgx9NNsYqwXKfDDuswEyF2Q2ewQSfd2pkK6ydH2wmMyq2JViZvy9DQ35hLMx7g72mFWNJTPtnz

View File

@@ -14,6 +14,7 @@ module.exports = {
entry: [`${clientDir}/main.js`], entry: [`${clientDir}/main.js`],
output: { output: {
publicPath: '/app/', publicPath: '/app/',
clean: true
}, },
module: { module: {

View File

@@ -16,7 +16,8 @@ module.exports = merge(baseWpConfig, {
devtool: 'inline-source-map', devtool: 'inline-source-map',
output: { output: {
path: `${publicDir}/app`, path: `${publicDir}/app`,
filename: 'bundle.js' filename: 'bundle.js',
clean: true
}, },
module: { module: {
@@ -38,6 +39,6 @@ module.exports = merge(baseWpConfig, {
template: `${clientDir}/index.html.template`, template: `${clientDir}/index.html.template`,
filename: `${publicDir}/index.html` filename: `${publicDir}/index.html`
}), }),
new CopyWebpackPlugin({patterns: [{from: `${clientDir}/assets/*`, to: `${publicDir}/`}]}) new CopyWebpackPlugin({patterns: [{context: `${clientDir}/assets`, from: `${clientDir}/assets/*`, to: `${publicDir}/`}]})
] ]
}); });

View File

@@ -18,7 +18,8 @@ module.exports = merge(baseWpConfig, {
mode: 'production', mode: 'production',
output: { output: {
path: `${publicDir}/app_new`, path: `${publicDir}/app_new`,
filename: 'bundle.[contenthash].js' filename: 'bundle.[contenthash].js',
clean: true
}, },
module: { module: {
rules: [ rules: [
@@ -54,7 +55,7 @@ module.exports = merge(baseWpConfig, {
filename: `${publicDir}/index.html` filename: `${publicDir}/index.html`
}), }),
new CopyWebpackPlugin({patterns: new CopyWebpackPlugin({patterns:
[{from: `${clientDir}/assets/*`, to: `${publicDir}/`, context: `${clientDir}/assets` }] [{context: `${clientDir}/assets`, from: `${clientDir}/assets/*`, to: `${publicDir}/` }]
}), }),
new GenerateSW({ new GenerateSW({
cacheId: 'liberama', cacheId: 'liberama',

View File

@@ -238,7 +238,7 @@ class App {
const url = s[1] || ''; const url = s[1] || '';
const q = utils.parseQuery(s[0] || ''); const q = utils.parseQuery(s[0] || '');
if (url) { if (url) {
q.url = decodeURIComponent(url); q.url = url;
} }
window.history.replaceState({}, '', '/'); window.history.replaceState({}, '', '/');
@@ -271,6 +271,10 @@ body, html, #app {
font: normal 12pt ReaderDefault; font: normal 12pt ReaderDefault;
} }
.notify-margin {
margin-top: 55px;
}
.dborder { .dborder {
border: 2px solid magenta !important; border: 2px solid magenta !important;
} }

View File

@@ -1,70 +1,17 @@
<template> <template>
<div class="page"> <div class="page">
<div class="box"> <div class="column items-center" style="width: 500px">
<p class="p"> <p class="p">
Вы можете пожертвовать на развитие проекта любую сумму: Здесь вы можете пожертвовать на развитие проекта:
</p> </p>
<div class="address">
<img class="logo" src="./assets/yoomoney.png">
<q-btn class="q-ml-sm q-px-sm" dense no-caps @click="donateYooMoney">
Пожертвовать
</q-btn><br>
<div class="para">
{{ yooAddress }}
<q-icon class="copy-icon" name="la la-copy" @click="copyAddress(yooAddress, 'Кошелёк ЮMoney')">
<q-tooltip :delay="1000" anchor="top middle" self="center middle" content-style="font-size: 80%">
Скопировать
</q-tooltip>
</q-icon>
</div>
</div>
<!--div class="address"> <q-btn no-caps class="q-my-lg" color="green-8" size="14px" style="width: 200px" @click="makeDonation">
<img class="logo" src="./assets/paypal.png"> <q-icon class="q-mr-xs" name="la la-donate" size="24px" />
<div class="para"> Поддержать проект
{{ paypalAddress }} </q-btn>
<q-icon class="copy-icon" name="la la-copy" @click="copyAddress(paypalAddress, 'Paypal-адрес')">
<q-tooltip :delay="1000" anchor="top middle" self="center middle" content-style="font-size: 80%">
Скопировать
</q-tooltip>
</q-icon>
</div>
</div-->
<div class="address"> <div style="font-size: 60%">
<img class="logo" src="./assets/bitcoin.png"> * Ваш донат является подарком автору проекта
<div class="para">
{{ bitcoinAddress }}
<q-icon class="copy-icon" name="la la-copy" @click="copyAddress(bitcoinAddress, 'Bitcoin-адрес')">
<q-tooltip :delay="1000" anchor="top middle" self="center middle" content-style="font-size: 80%">
Скопировать
</q-tooltip>
</q-icon>
</div>
</div>
<div class="address">
<img class="logo" src="./assets/litecoin.png">
<div class="para">
{{ litecoinAddress }}
<q-icon class="copy-icon" name="la la-copy" @click="copyAddress(litecoinAddress, 'Litecoin-адрес')">
<q-tooltip :delay="1000" anchor="top middle" self="center middle" content-style="font-size: 80%">
Скопировать
</q-tooltip>
</q-icon>
</div>
</div>
<div class="address">
<img class="logo" src="./assets/monero.png">
<div class="para">
{{ moneroAddress }}
<q-icon class="copy-icon" name="la la-copy" @click="copyAddress(moneroAddress, 'Monero-адрес')">
<q-tooltip :delay="1000" anchor="top middle" self="center middle" content-style="font-size: 80%">
Скопировать
</q-tooltip>
</q-icon>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -74,28 +21,14 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
import vueComponent from '../../../vueComponent.js'; import vueComponent from '../../../vueComponent.js';
import {copyTextToClipboard} from '../../../../share/utils'; import * as utils from '../../../../share/utils';
class DonateHelpPage { class DonateHelpPage {
yooAddress = '410018702323056';
paypalAddress = 'bookpauk@gmail.com';
bitcoinAddress = '3EbgZ7MK1UVaN38Gty5DCBtS4PknM4Ut85';
litecoinAddress = 'MP39Riec4oSNB3XMjiquKoLWxbufRYNXxZ';
moneroAddress = '8BQPnvHcPSHM5gMQsmuypDgx9NNsYqwXKfDDuswEyF2Q2ewQSfd2pkK6ydH2wmMyq2JViZvy9DQ35hLMx7g72mFWNJTPtnz';
created() { created() {
} }
donateYooMoney() { makeDonation() {
window.open(`https://yoomoney.ru/to/${this.yooAddress}`, '_blank'); utils.makeDonation();
}
async copyAddress(address, prefix) {
const result = await copyTextToClipboard(address);
if (result)
this.$root.notify.success(`${prefix} ${address} успешно скопирован в буфер обмена`);
else
this.$root.notify.error('Копирование не удалось');
} }
} }
@@ -116,31 +49,4 @@ export default vueComponent(DonateHelpPage);
padding: 0; padding: 0;
text-indent: 20px; text-indent: 20px;
} }
.box {
max-width: 550px;
overflow-wrap: break-word;
}
.address {
padding-top: 10px;
margin-top: 20px;
}
.para {
margin: 10px 10px 10px 40px;
}
.logo {
width: 130px;
position: relative;
top: 10px;
}
.copy-icon {
margin-left: 10px;
cursor: pointer;
font-size: 120%;
color: blue;
}
</style> </style>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

View File

@@ -1,5 +1,5 @@
<template> <template>
<Window @close="close"> <Window @close="close" style="z-index: 200">
<template #header> <template #header>
Справка Справка
</template> </template>
@@ -36,14 +36,14 @@ import CommonHelpPage from './CommonHelpPage/CommonHelpPage.vue';
import HotkeysHelpPage from './HotkeysHelpPage/HotkeysHelpPage.vue'; import HotkeysHelpPage from './HotkeysHelpPage/HotkeysHelpPage.vue';
import MouseHelpPage from './MouseHelpPage/MouseHelpPage.vue'; import MouseHelpPage from './MouseHelpPage/MouseHelpPage.vue';
import VersionHistoryPage from './VersionHistoryPage/VersionHistoryPage.vue'; import VersionHistoryPage from './VersionHistoryPage/VersionHistoryPage.vue';
//import DonateHelpPage from './DonateHelpPage/DonateHelpPage.vue'; import DonateHelpPage from './DonateHelpPage/DonateHelpPage.vue';
const pages = { const pages = {
'CommonHelpPage': CommonHelpPage, 'CommonHelpPage': CommonHelpPage,
'HotkeysHelpPage': HotkeysHelpPage, 'HotkeysHelpPage': HotkeysHelpPage,
'MouseHelpPage': MouseHelpPage, 'MouseHelpPage': MouseHelpPage,
'VersionHistoryPage': VersionHistoryPage, 'VersionHistoryPage': VersionHistoryPage,
//'DonateHelpPage': DonateHelpPage, 'DonateHelpPage': DonateHelpPage,
}; };
const tabs = [ const tabs = [
@@ -51,7 +51,7 @@ const tabs = [
['MouseHelpPage', 'Мышь/тачскрин'], ['MouseHelpPage', 'Мышь/тачскрин'],
['HotkeysHelpPage', 'Клавиатура'], ['HotkeysHelpPage', 'Клавиатура'],
['VersionHistoryPage', 'История версий'], ['VersionHistoryPage', 'История версий'],
//['DonateHelpPage', 'Помочь проекту'], ['DonateHelpPage', 'Помочь проекту'],
]; ];
const componentOptions = { const componentOptions = {
@@ -80,7 +80,7 @@ class HelpPage {
} }
activateDonateHelpPage() { activateDonateHelpPage() {
//this.selectedTab = 'DonateHelpPage'; this.selectedTab = 'DonateHelpPage';
} }
activateVersionHistoryHelpPage() { activateVersionHistoryHelpPage() {

View File

@@ -57,7 +57,7 @@
<div class="col column justify-end items-center no-wrap overflow-hidden"> <div class="col column justify-end items-center no-wrap overflow-hidden">
<span v-if="mode == 'omnireader'" class="bottom-span clickable" @click="findBook">Найти книгу</span> <span v-if="mode == 'omnireader'" class="bottom-span clickable" @click="findBook">Найти книгу</span>
<span class="bottom-span clickable" @click="openHelp">Справка</span> <span class="bottom-span clickable" @click="openHelp">Справка</span>
<!--span class="bottom-span clickable" @click="openDonate">Помочь проекту</span--> <span class="bottom-span clickable" @click="openDonate">Помочь проекту</span>
<span v-if="version == clientVersion" class="bottom-span">v{{ version }}</span> <span v-if="version == clientVersion" class="bottom-span">v{{ version }}</span>
<span v-else class="bottom-span">Версия сервера {{ version }}, версия клиента {{ clientVersion }}, необходимо обновить страницу</span> <span v-else class="bottom-span">Версия сервера {{ version }}, версия клиента {{ clientVersion }}, необходимо обновить страницу</span>

View File

@@ -292,7 +292,6 @@ class Reader {
libsActive = false; libsActive = false;
recentBooksActive = false; recentBooksActive = false;
clickControlActive = false; clickControlActive = false;
offlineModeActive = false;
settingsActive = false; settingsActive = false;
clickMapActive = false; clickMapActive = false;
@@ -721,7 +720,7 @@ class Reader {
return; return;
const recent = this.mostRecentBook(); const recent = this.mostRecentBook();
const pos = (recent && recent.bookPos && this.allowUrlParamBookPos ? `__p=${recent.bookPos}&` : ''); const pos = (recent && recent.bookPos && this.allowUrlParamBookPos ? `__p=${recent.bookPos}&` : '');
const url = (recent ? `url=${recent.url}` : ''); const url = (recent ? `url=${encodeURIComponent(recent.url)}` : '');
if (isNewRoute) if (isNewRoute)
this.$router.push(`/reader?${pos}${url}`).catch(() => {}); this.$router.push(`/reader?${pos}${url}`).catch(() => {});
else else
@@ -807,6 +806,10 @@ class Reader {
return this.reader.toolBarActive; return this.reader.toolBarActive;
} }
get offlineModeActive() {
return this.reader.offlineModeActive;
}
mostRecentBook() { mostRecentBook() {
const result = bookManager.mostRecentBook(); const result = bookManager.mostRecentBook();
this.mostRecentBookReactive = result; this.mostRecentBookReactive = result;
@@ -1019,7 +1022,7 @@ class Reader {
} }
offlineModeToggle() { offlineModeToggle() {
this.offlineModeActive = !this.offlineModeActive; this.commit('reader/setOfflineModeActive', !this.offlineModeActive);
this.$refs.serverStorage.offlineModeActive = this.offlineModeActive; this.$refs.serverStorage.offlineModeActive = this.offlineModeActive;
} }

View File

@@ -18,56 +18,51 @@
</template> </template>
</Dialog> </Dialog>
<Dialog ref="dialog2" v-model="donationVisible"> <q-dialog ref="dialog2" v-model="donationVisible" style="z-index: 100" no-route-dismiss no-esc-dismiss no-backdrop-dismiss>
<template #header> <div class="column bg-white no-wrap q-pa-md">
Здравствуйте, уважаемые читатели! <div class="row justify-center q-mb-md" style="font-size: 110%">
</template> Здравствуйте, дорогие читатели!
</div>
<div style="word-break: normal"> <div class="q-mx-md column" style="word-break: normal">
Стартовала ежегодная акция "Оплатим хостинг вместе".<br><br> <div>
Вот уже много лет мы все вместе пользуемся нашей любимой читалкой.<br><br>
Для оплаты годового хостинга читалки, необходимо собрать около 2000 рублей. Напоминаем вам, что проект является некоммерческим и обладает такими
В настоящий момент у автора эта сумма есть в наличии. Однако будет справедливо, если каждый достоинствами, как:
сможет проголосовать рублем за то, чтобы читалка так и оставалась:
<ul> <ul>
<li>непрерывно улучшаемой</li> <li>все функции читалки открыты и доступны совершенно бесплатно</li>
<li>без рекламы</li> <li>в проекте отсутствует какая-либо реклама или баннеры</li>
<li>без регистрации</li> <li>нет никакой регистрации и монетизации</li>
<li>Open Source</li> <li>нет сбора персональных данных</li>
</ul> <li>открытый исходный код</li>
<li>проект постепенно улучшается, по мере возможности</li>
</ul>
Автор также обращается с просьбой о помощи в распространении Однако на оплату хостинга читалки и сервера обновлений автор тратит свои
<a href="https://omnireader.ru" target="_blank">ссылки</a> собственные средства, а также тратит свое время и силы на улучшение проекта.
<q-icon class="copy-icon" name="la la-copy" @click="copyLink('https://omnireader.ru')"> <br><br>
<q-tooltip :delay="1000" anchor="top middle" self="center middle" content-style="font-size: 80%"> Поддержим же материально наш ресурс, чтобы и дальше спокойно существовать и развиваться:
Скопировать </div>
</q-tooltip>
</q-icon>
на читалку через тематические форумы, соцсети, мессенджеры и пр.
Чем нас больше, тем легче оставаться на плаву и тем больше мотивации у разработчика, чтобы продолжать работать над проектом.
<br><br> <q-btn style="margin: 10px 50px 10px 50px" color="green-8" size="14px" no-caps @click="makeDonation">
Если соберется бóльшая сумма, то разработка децентрализованной библиотеки для свободного обмена книгами будет по возможности ускорена. <q-icon class="q-mr-xs" name="la la-donate" size="24px" />
<br><br> Поддержать проект
P.S. При необходимости можно воспользоваться подходящим обменником на <a href="https://www.bestchange.ru" target="_blank">bestchange.ru</a> </q-btn>
<br><br> <q-btn style="margin: 0 50px 20px 50px" size="14px" no-caps @click="donationDialogRemind">
<div class="row justify-center"> Напомнить в следующем месяце
<!--q-btn class="q-px-sm" color="primary" dense no-caps @click="openDonate"> </q-btn>
Помочь проекту
</q-btn--> <div class="row justify-center">
<div class="q-px-sm clickable" style="font-size: 80%" @click="openDonate">
Помочь проекту можно в любое время
</div>
</div>
</div> </div>
</div> </div>
</q-dialog>
<template #footer>
<span class="clickable row justify-end" style="font-size: 60%; color: grey" @click="donationDialogDisable">Больше не показывать</span>
<br>
<q-btn class="q-px-sm" dense no-caps @click="donationDialogRemind">
Напомнить позже
</q-btn>
</template>
</Dialog>
<Dialog ref="dialog3" v-model="urlHelpVisible"> <Dialog ref="dialog3" v-model="urlHelpVisible">
<template #header> <template #header>
@@ -134,7 +129,7 @@ class ReaderDialogs {
loadSettings() { loadSettings() {
const settings = this.settings; const settings = this.settings;
this.showWhatsNewDialog = settings.showWhatsNewDialog; this.showWhatsNewDialog = settings.showWhatsNewDialog;
this.showDonationDialog2020 = settings.showDonationDialog2020; this.showDonationDialog = settings.showDonationDialog;
} }
async showWhatsNew() { async showWhatsNew() {
@@ -149,9 +144,9 @@ class ReaderDialogs {
} }
async showDonation() { async showDonation() {
const today = utils.formatDate(new Date(), 'coDate'); const today = utils.formatDate(new Date(), 'coMonth');
if ((this.mode == 'omnireader' || this.mode == 'liberama.top') && today < '2020-03-01' && this.showDonationDialog2020 && this.donationRemindDate != today) { if ((this.mode == 'omnireader' || this.mode == 'liberama.top') && this.showDonationDialog && this.donationRemindDate != today) {
await utils.sleep(3000); await utils.sleep(3000);
this.donationVisible = true; this.donationVisible = true;
} }
@@ -166,20 +161,17 @@ class ReaderDialogs {
this.urlHelpVisible = false; this.urlHelpVisible = false;
} }
donationDialogDisable() {
this.donationVisible = false;
if (this.showDonationDialog2020) {
this.commit('reader/setSettings', { showDonationDialog2020: false });
}
}
donationDialogRemind() { donationDialogRemind() {
this.donationVisible = false; this.donationVisible = false;
this.commit('reader/setDonationRemindDate', utils.formatDate(new Date(), 'coDate')); this.commit('reader/setDonationRemindDate', utils.formatDate(new Date(), 'coMonth'));
}
makeDonation() {
utils.makeDonation();
this.donationDialogRemind();
} }
openDonate() { openDonate() {
this.donationVisible = false;
this.$emit('donate-toggle'); this.$emit('donate-toggle');
} }

View File

@@ -813,6 +813,12 @@ class RecentBooksPage {
const book = await bookManager.getRecentBook(item); const book = await bookManager.getRecentBook(item);
if (book) { if (book) {
await bookManager.setCheckBuc(book, item.checkBuc); await bookManager.setCheckBuc(book, item.checkBuc);
this.$root.notify.info(item.checkBuc
? 'Проверка обновлений книги включена'
: 'Проверка обновлений книги отключена'
);
} }
} }

View File

@@ -41,15 +41,15 @@
</q-checkbox> </q-checkbox>
</div> </div>
<!--div class="item row"> <div class="item row">
<div class="label-6">Уведомление</div> <div class="label-6">Уведомление</div>
<q-checkbox size="xs" v-model="showDonationDialog2020"> <q-checkbox size="xs" v-model="showDonationDialog">
Показывать "Оплатим хостинг вместе" Показывать форму доната
<q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%"> <q-tooltip :delay="1000" anchor="top middle" self="bottom middle" content-style="font-size: 80%">
Показывать уведомление "Оплатим хостинг вместе" Показывать диалог для сбора пожертвований
</q-tooltip> </q-tooltip>
</q-checkbox> </q-checkbox>
</div--> </div>
<!----------------------------------------------> <!---------------------------------------------->
<div class="part-header">Другое</div> <div class="part-header">Другое</div>

View File

@@ -1,4 +1,32 @@
export const versionHistory = [ export const versionHistory = [
{
version: '0.12.2',
releaseDate: '2022-09-04',
showUntil: '2022-09-11',
content:
`
<ul>
<li>исправлен баг с формой для доната, показывалась каждый день, а не каждый месяц</li>
<li>автор приносит извинения за доставленные неудобства</li>
</ul>
`
},
{
version: '0.12.1',
releaseDate: '2022-09-01',
showUntil: '2022-08-30',
content:
`
<ul>
<li>добавлена форма для доната</li>
<li>исправления багов</li>
</ul>
`
},
{ {
version: '0.12.0', version: '0.12.0',
releaseDate: '2022-07-27', releaseDate: '2022-07-27',

View File

@@ -27,9 +27,10 @@ class Notify {
icon, icon,
actions: [{icon: 'la la-times notify-button-icon', color: 'black'}], actions: [{icon: 'la la-times notify-button-icon', color: 'black'}],
html: true, html: true,
classes: 'notify-margin',
message: message:
`<div style="max-width: 350px;"> `<div style="max-width: 350px">
${caption} ${caption}
<div style="color: ${messageColor}; overflow-wrap: break-word; word-wrap: break-word;">${message}</div> <div style="color: ${messageColor}; overflow-wrap: break-word; word-wrap: break-word;">${message}</div>
</div>` </div>`

View File

@@ -45,6 +45,8 @@ export function formatDate(d, format) {
`${d.getHours().toString().padStart(2, '0')}:${d.getMinutes().toString().padStart(2, '0')}`; `${d.getHours().toString().padStart(2, '0')}:${d.getMinutes().toString().padStart(2, '0')}`;
case 'coDate': case 'coDate':
return `${d.getFullYear()}-${(d.getMonth() + 1).toString().padStart(2, '0')}-${d.getDate().toString().padStart(2, '0')}`; return `${d.getFullYear()}-${(d.getMonth() + 1).toString().padStart(2, '0')}-${d.getDate().toString().padStart(2, '0')}`;
case 'coMonth':
return `${(d.getMonth() + 1).toString().padStart(2, '0')}`;
case 'noDate': case 'noDate':
return `${d.getDate().toString().padStart(2, '0')}.${(d.getMonth() + 1).toString().padStart(2, '0')}.${d.getFullYear()}`; return `${d.getDate().toString().padStart(2, '0')}.${(d.getMonth() + 1).toString().padStart(2, '0')}.${d.getFullYear()}`;
} }
@@ -409,4 +411,8 @@ export function resizeImage(dataUrl, toWidth, toHeight, quality = 0.9) {
if (!resolved) if (!resolved)
reject('Не удалось изменить размер'); reject('Не удалось изменить размер');
})().catch(reject); }); })().catch(reject); });
} }
export function makeDonation() {
window.open('https://donatty.com/liberama', '_blank');
}

View File

@@ -180,7 +180,7 @@ const settingDefaults = {
showServerStorageMessages: true, showServerStorageMessages: true,
showWhatsNewDialog: true, showWhatsNewDialog: true,
showDonationDialog2020: true, showDonationDialog: true,
showNeedUpdateNotify: true, showNeedUpdateNotify: true,
fontShifts: {}, fontShifts: {},
@@ -255,6 +255,7 @@ const libsDefaults = {
// initial state // initial state
const state = { const state = {
toolBarActive: true, toolBarActive: true,
offlineModeActive: false,
serverSyncEnabled: false, serverSyncEnabled: false,
serverStorageKey: '', serverStorageKey: '',
profiles: {}, profiles: {},
@@ -280,6 +281,9 @@ const mutations = {
setToolBarActive(state, value) { setToolBarActive(state, value) {
state.toolBarActive = value; state.toolBarActive = value;
}, },
setOfflineModeActive(state, value) {
state.offlineModeActive = value;
},
setServerSyncEnabled(state, value) { setServerSyncEnabled(state, value) {
state.serverSyncEnabled = value; state.serverSyncEnabled = value;
}, },

View File

@@ -10,7 +10,7 @@ git clone https://github.com/bookpauk/liberama
### node.js ### node.js
``` ```
sudo apt install -y curl sudo apt install -y curl
curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash - curl -sL https://deb.nodesource.com/setup_16.x | sudo -E bash -
sudo apt install -y nodejs sudo apt install -y nodejs
``` ```

1697
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,17 +1,17 @@
{ {
"name": "Liberama", "name": "Liberama",
"version": "0.12.0", "version": "0.12.2",
"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",
"engines": { "engines": {
"node": ">=14.4.0" "node": ">=16.16.0"
}, },
"scripts": { "scripts": {
"dev": "nodemon --inspect --ignore server/public --ignore server/data --ignore client --exec 'node server'", "dev": "nodemon --inspect --ignore server/public --ignore server/data --ignore client --exec 'node server'",
"build:client": "webpack --config build/webpack.prod.config.js", "build:client": "webpack --config build/webpack.prod.config.js",
"build:linux": "npm run build:client && node build/linux && pkg -t node14-linux-x64 -C GZip -o dist/linux/liberama .", "build:linux": "npm run build:client && node build/linux && pkg -t node16-linux-x64 -C GZip -o dist/linux/liberama .",
"build:win": "npm run build:client && node build/win && pkg -t node14-win-x64 -C GZip -o dist/win/liberama .", "build:win": "npm run build:client && node build/win && pkg -t node16-win-x64 -C GZip -o dist/win/liberama .",
"lint": "eslint --ext=.js,.vue client server", "lint": "eslint --ext=.js,.vue client server",
"build:client-dev": "webpack --config build/webpack.dev.config.js", "build:client-dev": "webpack --config build/webpack.dev.config.js",
"postinstall": "npm run build:client-dev && node build/linux" "postinstall": "npm run build:client-dev && node build/linux"
@@ -21,35 +21,35 @@
"scripts": "server/config/*.js" "scripts": "server/config/*.js"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.18.9", "@babel/core": "^7.18.13",
"@babel/eslint-parser": "^7.18.9", "@babel/eslint-parser": "^7.18.9",
"@babel/eslint-plugin": "^7.17.7", "@babel/eslint-plugin": "^7.18.10",
"@babel/plugin-proposal-decorators": "^7.18.9", "@babel/plugin-proposal-decorators": "^7.18.10",
"@babel/preset-env": "^7.18.9", "@babel/preset-env": "^7.18.10",
"@vue/compiler-sfc": "^3.2.22", "@vue/compiler-sfc": "^3.2.22",
"babel-loader": "^8.2.5", "babel-loader": "^8.2.5",
"copy-webpack-plugin": "^11.0.0", "copy-webpack-plugin": "^11.0.0",
"css-loader": "^6.7.1", "css-loader": "^6.7.1",
"css-minimizer-webpack-plugin": "^4.0.0", "css-minimizer-webpack-plugin": "^4.0.0",
"eslint": "^8.20.0", "eslint": "^8.23.0",
"eslint-plugin-vue": "^9.3.0", "eslint-plugin-vue": "^9.4.0",
"html-webpack-plugin": "^5.5.0", "html-webpack-plugin": "^5.5.0",
"mini-css-extract-plugin": "^2.6.1", "mini-css-extract-plugin": "^2.6.1",
"pkg": "^5.8.0", "pkg": "^5.8.0",
"terser-webpack-plugin": "^5.3.3", "terser-webpack-plugin": "^5.3.6",
"vue-eslint-parser": "^9.0.3", "vue-eslint-parser": "^9.0.3",
"vue-loader": "^17.0.0", "vue-loader": "^17.0.0",
"vue-style-loader": "^4.1.3", "vue-style-loader": "^4.1.3",
"webpack": "^5.74.0", "webpack": "^5.74.0",
"webpack-cli": "^4.10.0", "webpack-cli": "^4.10.0",
"webpack-dev-middleware": "^5.3.3", "webpack-dev-middleware": "^5.3.3",
"webpack-hot-middleware": "^2.25.1", "webpack-hot-middleware": "^2.25.2",
"webpack-merge": "^5.8.0", "webpack-merge": "^5.8.0",
"workbox-webpack-plugin": "^6.5.3" "workbox-webpack-plugin": "^6.5.4"
}, },
"dependencies": { "dependencies": {
"@quasar/extras": "^1.15.0", "@quasar/extras": "^1.15.2",
"@vue/compat": "^3.2.37", "@vue/compat": "^3.2.38",
"axios": "^0.27.2", "axios": "^0.27.2",
"base-x": "^4.0.0", "base-x": "^4.0.0",
"chardet": "^1.4.0", "chardet": "^1.4.0",
@@ -59,7 +59,7 @@
"fs-extra": "^10.1.0", "fs-extra": "^10.1.0",
"he": "^1.2.0", "he": "^1.2.0",
"iconv-lite": "^0.6.3", "iconv-lite": "^0.6.3",
"jembadb": "^3.0.9", "jembadb": "^4.2.0",
"localforage": "^1.10.0", "localforage": "^1.10.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"minimist": "^1.2.6", "minimist": "^1.2.6",
@@ -67,17 +67,17 @@
"pako": "^2.0.4", "pako": "^2.0.4",
"path-browserify": "^1.0.1", "path-browserify": "^1.0.1",
"pidusage": "^3.0.0", "pidusage": "^3.0.0",
"quasar": "^2.7.5", "quasar": "^2.7.7",
"safe-buffer": "^5.2.1", "safe-buffer": "^5.2.1",
"sanitize-html": "^2.7.1", "sanitize-html": "^2.7.1",
"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.2", "vue-router": "^4.1.5",
"vuex": "^4.0.2", "vuex": "^4.0.2",
"vuex-persist": "^3.1.3", "vuex-persist": "^3.1.3",
"webdav": "^4.10.0", "webdav": "^4.11.0",
"ws": "^8.8.1", "ws": "^8.8.1",
"zip-stream": "^4.1.0" "zip-stream": "^4.1.0"
} }

View File

@@ -177,8 +177,10 @@ class BUCClient {
const ids = new Set(); const ids = new Set();
let id = iter.next(); let id = iter.next();
while (!id.done && ids.size < 1000) { while (!id.done) {
ids.add(id.value); ids.add(id.value);
if (ids.size >= 1000)
break;
id = iter.next(); id = iter.next();
} }

View File

@@ -78,8 +78,10 @@ class BUCServer {
const ids = new Set(); const ids = new Set();
let id = iter.next(); let id = iter.next();
while (!id.done && ids.size < 100) { while (!id.done) {
ids.add(id.value); ids.add(id.value);
if (ids.size >= 100)
break;
id = iter.next(); id = iter.next();
} }
@@ -222,8 +224,10 @@ class BUCServer {
}); });
//пушим в очередь, после этого их обработает periodicCheck //пушим в очередь, после этого их обработает periodicCheck
for (const row of rowsToPush) for (const row of rowsToPush) {
this.checkQueue.push(row); this.checkQueue.push(row);
log(LM_INFO, ` add ${row.id}`);
}
log(LM_WARN, `checkQueue: added ${ids.length} recs, total ${this.checkQueue.length}`); log(LM_WARN, `checkQueue: added ${ids.length} recs, total ${this.checkQueue.length}`);
} }
@@ -271,6 +275,7 @@ class BUCServer {
&& (!modTime || !row.modTime || (modTime !== row.modTime)) && (!modTime || !row.modTime || (modTime !== row.modTime))
&& (!size || !row.size || (size !== row.size)) && (!size || !row.size || (size !== row.size))
) { ) {
const downdata = await this.down.load(row.id); const downdata = await this.down.load(row.id);
size = downdata.length; size = downdata.length;

View File

@@ -1,4 +1,6 @@
const https = require('https');
const axios = require('axios'); const axios = require('axios');
const utils = require('./utils');
const userAgent = 'Mozilla/5.0 (X11; HasCodingOs 1.0; Linux x64) AppleWebKit/637.36 (KHTML, like Gecko) Chrome/70.0.3112.101 Safari/637.36 HasBrowser/5.0'; const userAgent = 'Mozilla/5.0 (X11; HasCodingOs 1.0; Linux x64) AppleWebKit/637.36 (KHTML, like Gecko) Chrome/70.0.3112.101 Safari/637.36 HasBrowser/5.0';
@@ -12,8 +14,12 @@ class FileDownloader {
const options = { const options = {
headers: { headers: {
'user-agent': userAgent 'user-agent': userAgent,
timeout: 300*1000,
}, },
httpsAgent: new https.Agent({
rejectUnauthorized: false // решение проблемы 'unable to verify the first certificate' для некоторых сайтов с валидным сертификатом
}),
responseType: 'stream', responseType: 'stream',
}; };
@@ -67,7 +73,8 @@ class FileDownloader {
async head(url) { async head(url) {
const options = { const options = {
headers: { headers: {
'user-agent': userAgent 'user-agent': userAgent,
timeout: 10*1000,
}, },
}; };
@@ -75,25 +82,42 @@ class FileDownloader {
return res.headers; return res.headers;
} }
streamToBuffer(stream, progress) { streamToBuffer(stream, progress, timeout = 30*1000) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!progress) if (!progress)
progress = () => {}; progress = () => {};
const _buf = []; const _buf = [];
let resolved = false;
let timer = 0;
stream.on('data', (chunk) => { stream.on('data', (chunk) => {
timer = 0;
_buf.push(chunk); _buf.push(chunk);
progress(chunk); progress(chunk);
}); });
stream.on('end', () => resolve(Buffer.concat(_buf))); stream.on('end', () => {
resolved = true;
timer = timeout;
resolve(Buffer.concat(_buf));
});
stream.on('error', (err) => { stream.on('error', (err) => {
reject(err); reject(err);
}); });
stream.on('aborted', () => { stream.on('aborted', () => {
reject(new Error('aborted')); reject(new Error('aborted'));
}); });
//бодяга с timer и timeout, чтобы гарантировать отсутствие зависания по каким-либо причинам
(async() => {
while (timer < timeout) {
await utils.sleep(1000);
timer += 1000;
}
if (!resolved)
reject(new Error('FileDownloader: timed out'))
})();
}); });
} }
} }

View File

@@ -83,6 +83,7 @@ class ReaderWorker {
let convertFilename = ''; let convertFilename = '';
const overLoadMes = 'Слишком большая очередь загрузки. Пожалуйста, попробуйте позже.'; const overLoadMes = 'Слишком большая очередь загрузки. Пожалуйста, попробуйте позже.';
const fileNotFoundMes = 'Файл не найден';
const overLoadErr = new Error(overLoadMes); const overLoadErr = new Error(overLoadMes);
let q = null; let q = null;
@@ -184,26 +185,33 @@ class ReaderWorker {
})(); })();
} catch (e) { } catch (e) {
log(LM_ERR, `url: ${url}, downloadedFilename: ${downloadedFilename}`);
log(LM_ERR, e.stack); log(LM_ERR, e.stack);
let mes = e.message.split('|FORLOG|'); let mes = e.message.split('|FORLOG|');
if (mes[1]) if (mes[1])
log(LM_ERR, mes[0] + mes[1]); log(LM_ERR, mes[0] + mes[1]);
log(LM_ERR, `downloadedFilename: ${downloadedFilename}`);
mes = mes[0]; mes = mes[0];
if (mes == 'abort') if (mes == 'abort')
mes = overLoadMes; mes = overLoadMes;
if (mes.indexOf('ENOTDIR') >= 0)
mes = fileNotFoundMes;
wState.set({state: 'error', error: mes}); wState.set({state: 'error', error: mes});
} finally { } finally {
//clean //clean
if (q) try {
q.ret(); if (q)
if (decompDir) q.ret();
await fs.remove(decompDir); if (decompDir)
if (downloadedFilename && !isUploaded) await fs.remove(decompDir);
await fs.remove(downloadedFilename); if (downloadedFilename && !isUploaded)
if (convertFilename) await fs.remove(downloadedFilename);
await fs.remove(convertFilename); if (convertFilename)
await fs.remove(convertFilename);
} catch (e) {
log(LM_ERR, `Remove error: ${e.stack}`);
}
} }
} }

View File

@@ -25,7 +25,7 @@ class WorkerState {
return { return {
set: state => this.setState(workerId, state), set: state => this.setState(workerId, state),
finish: state => this.finishState(workerId, state), finish: state => this.finishState(workerId, state),
get: workerId => this.getState(workerId), get: () => this.getState(workerId),
}; };
} }