1
0
mirror of https://github.com/janeczku/calibre-web synced 2024-12-25 09:30:31 +00:00

Added function to download debug information

This commit is contained in:
Ozzieisaacs 2020-11-15 14:19:25 +01:00
parent 4081895a78
commit 32b7b39223
7 changed files with 131 additions and 22 deletions

View File

@ -82,6 +82,12 @@ _VERSIONS = OrderedDict(
_VERSIONS.update(uploader.get_versions()) _VERSIONS.update(uploader.get_versions())
def collect_stats():
_VERSIONS['ebook converter'] = _(converter.get_calibre_version())
_VERSIONS['unrar'] = _(converter.get_unrar_version())
_VERSIONS['kepubify'] = _(converter.get_kepubify_version())
return _VERSIONS
@about.route("/stats") @about.route("/stats")
@flask_login.login_required @flask_login.login_required
def stats(): def stats():
@ -89,8 +95,7 @@ def stats():
authors = calibre_db.session.query(db.Authors).count() authors = calibre_db.session.query(db.Authors).count()
categorys = calibre_db.session.query(db.Tags).count() categorys = calibre_db.session.query(db.Tags).count()
series = calibre_db.session.query(db.Series).count() series = calibre_db.session.query(db.Series).count()
_VERSIONS['ebook converter'] = _(converter.get_calibre_version()) return render_title_template('stats.html', bookcounter=counter, authorcounter=authors, versions=collect_stats(),
_VERSIONS['unrar'] = _(converter.get_unrar_version())
_VERSIONS['kepubify'] = _(converter.get_kepubify_version())
return render_title_template('stats.html', bookcounter=counter, authorcounter=authors, versions=_VERSIONS,
categorycounter=categorys, seriecounter=series, title=_(u"Statistics"), page="stat") categorycounter=categorys, seriecounter=series, title=_(u"Statistics"), page="stat")

View File

@ -41,7 +41,8 @@ from . import constants, logger, helper, services
from . import db, calibre_db, ub, web_server, get_locale, config, updater_thread, babel, gdriveutils from . import db, calibre_db, ub, web_server, get_locale, config, updater_thread, babel, gdriveutils
from .helper import check_valid_domain, send_test_mail, reset_password, generate_password_hash from .helper import check_valid_domain, send_test_mail, reset_password, generate_password_hash
from .gdriveutils import is_gdrive_ready, gdrive_support from .gdriveutils import is_gdrive_ready, gdrive_support
from .web import admin_required, render_title_template, before_request, unconfigured, login_required_if_no_ano from .web import admin_required, render_title_template, before_request, unconfigured
from . import debug_info
log = logger.create() log = logger.create()
@ -218,7 +219,8 @@ def edit_domain(allow):
@admin_required @admin_required
def add_domain(allow): def add_domain(allow):
domain_name = request.form.to_dict()['domainname'].replace('*', '%').replace('?', '_').lower() domain_name = request.form.to_dict()['domainname'].replace('*', '%').replace('?', '_').lower()
check = ub.session.query(ub.Registration).filter(ub.Registration.domain == domain_name).filter(ub.Registration.allow == allow).first() check = ub.session.query(ub.Registration).filter(ub.Registration.domain == domain_name)\
.filter(ub.Registration.allow == allow).first()
if not check: if not check:
new_domain = ub.Registration(domain=domain_name, allow=allow) new_domain = ub.Registration(domain=domain_name, allow=allow)
ub.session.add(new_domain) ub.session.add(new_domain)
@ -548,12 +550,14 @@ def _configuration_logfile_helper(to_save, gdriveError):
reboot_required |= _config_int(to_save, "config_log_level") reboot_required |= _config_int(to_save, "config_log_level")
reboot_required |= _config_string(to_save, "config_logfile") reboot_required |= _config_string(to_save, "config_logfile")
if not logger.is_valid_logfile(config.config_logfile): if not logger.is_valid_logfile(config.config_logfile):
return reboot_required, _configuration_result(_('Logfile Location is not Valid, Please Enter Correct Path'), gdriveError) return reboot_required, \
_configuration_result(_('Logfile Location is not Valid, Please Enter Correct Path'), gdriveError)
reboot_required |= _config_checkbox_int(to_save, "config_access_log") reboot_required |= _config_checkbox_int(to_save, "config_access_log")
reboot_required |= _config_string(to_save, "config_access_logfile") reboot_required |= _config_string(to_save, "config_access_logfile")
if not logger.is_valid_logfile(config.config_access_logfile): if not logger.is_valid_logfile(config.config_access_logfile):
return reboot_required, _configuration_result(_('Access Logfile Location is not Valid, Please Enter Correct Path'), gdriveError) return reboot_required, \
_configuration_result(_('Access Logfile Location is not Valid, Please Enter Correct Path'), gdriveError)
return reboot_required, None return reboot_required, None
def _configuration_ldap_helper(to_save, gdriveError): def _configuration_ldap_helper(to_save, gdriveError):
@ -585,28 +589,32 @@ def _configuration_ldap_helper(to_save, gdriveError):
if config.config_ldap_authentication > constants.LDAP_AUTH_ANONYMOUS: if config.config_ldap_authentication > constants.LDAP_AUTH_ANONYMOUS:
if config.config_ldap_authentication > constants.LDAP_AUTH_UNAUTHENTICATE: if config.config_ldap_authentication > constants.LDAP_AUTH_UNAUTHENTICATE:
if not config.config_ldap_serv_username or not bool(config.config_ldap_serv_password): if not config.config_ldap_serv_username or not bool(config.config_ldap_serv_password):
return reboot_required, _configuration_result('Please Enter a LDAP Service Account and Password', gdriveError) return reboot_required, _configuration_result('Please Enter a LDAP Service Account and Password',
gdriveError)
else: else:
if not config.config_ldap_serv_username: if not config.config_ldap_serv_username:
return reboot_required, _configuration_result('Please Enter a LDAP Service Account', gdriveError) return reboot_required, _configuration_result('Please Enter a LDAP Service Account', gdriveError)
if config.config_ldap_group_object_filter: if config.config_ldap_group_object_filter:
if config.config_ldap_group_object_filter.count("%s") != 1: if config.config_ldap_group_object_filter.count("%s") != 1:
return reboot_required, _configuration_result(_('LDAP Group Object Filter Needs to Have One "%s" Format Identifier'), return reboot_required, \
_configuration_result(_('LDAP Group Object Filter Needs to Have One "%s" Format Identifier'),
gdriveError) gdriveError)
if config.config_ldap_group_object_filter.count("(") != config.config_ldap_group_object_filter.count(")"): if config.config_ldap_group_object_filter.count("(") != config.config_ldap_group_object_filter.count(")"):
return reboot_required, _configuration_result(_('LDAP Group Object Filter Has Unmatched Parenthesis'), return reboot_required, _configuration_result(_('LDAP Group Object Filter Has Unmatched Parenthesis'),
gdriveError) gdriveError)
if config.config_ldap_user_object.count("%s") != 1: if config.config_ldap_user_object.count("%s") != 1:
return reboot_required, _configuration_result(_('LDAP User Object Filter needs to Have One "%s" Format Identifier'), return reboot_required, \
_configuration_result(_('LDAP User Object Filter needs to Have One "%s" Format Identifier'),
gdriveError) gdriveError)
if config.config_ldap_user_object.count("(") != config.config_ldap_user_object.count(")"): if config.config_ldap_user_object.count("(") != config.config_ldap_user_object.count(")"):
return reboot_required, _configuration_result(_('LDAP User Object Filter Has Unmatched Parenthesis'), return reboot_required, _configuration_result(_('LDAP User Object Filter Has Unmatched Parenthesis'),
gdriveError) gdriveError)
if config.config_ldap_cert_path and not os.path.isfile(config.config_ldap_cert_path): if config.config_ldap_cert_path and not os.path.isfile(config.config_ldap_cert_path):
return reboot_required, _configuration_result(_('LDAP Certificate Location is not Valid, Please Enter Correct Path'), return reboot_required, \
_configuration_result(_('LDAP Certificate Location is not Valid, Please Enter Correct Path'),
gdriveError) gdriveError)
return reboot_required, None return reboot_required, None
@ -617,7 +625,10 @@ def _configuration_update_helper():
to_save = request.form.to_dict() to_save = request.form.to_dict()
gdriveError = None gdriveError = None
to_save['config_calibre_dir'] = re.sub('[\\/]metadata\.db$', '', to_save['config_calibre_dir'], flags=re.IGNORECASE) to_save['config_calibre_dir'] = re.sub('[\\/]metadata\.db$',
'',
to_save['config_calibre_dir'],
flags=re.IGNORECASE)
try: try:
db_change |= _config_string(to_save, "config_calibre_dir") db_change |= _config_string(to_save, "config_calibre_dir")
@ -1028,6 +1039,27 @@ def send_logfile(logtype):
else: else:
return "" return ""
@admi.route("/admin/logdownload/<int:logtype>")
@login_required
@admin_required
def download_log(logtype):
if logtype == 0:
file_name = logger.get_logfile(config.config_logfile)
elif logtype == 1:
file_name = logger.get_accesslogfile(config.config_access_logfile)
else:
abort(404)
if logger.is_valid_logfile(file_name):
return debug_info.assemble_logfiles(file_name)
abort(404)
@admi.route("/admin/debug")
@login_required
@admin_required
def download_debug():
return debug_info.send_debug()
@admi.route("/get_update_status", methods=['GET']) @admi.route("/get_update_status", methods=['GET'])
@login_required @login_required

55
cps/debug_info.py Normal file
View File

@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
# This file is part of the Calibre-Web (https://github.com/janeczku/calibre-web)
# Copyright (C) 2012-2019 cervinko, idalin, SiphonSquirrel, ouzklcn, akushsky,
# OzzieIsaacs, bodybybuddha, jkrehm, matthazinski, janeczku
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import shutil
import glob
import zipfile
import json
import io
import os
from flask import send_file
from . import logger, config
from .about import collect_stats
log = logger.create()
def assemble_logfiles(file_name):
log_list = glob.glob(file_name + '*')
wfd = io.StringIO()
for f in log_list:
with open(f, 'r') as fd:
shutil.copyfileobj(fd, wfd)
return send_file(wfd,
as_attachment=True,
attachment_filename=os.path.basename(file_name))
def send_debug():
file_list = glob.glob(logger.get_logfile(config.config_logfile) + '*')
file_list.extend(glob.glob(logger.get_accesslogfile(config.config_access_logfile) + '*'))
memory_zip = io.BytesIO()
with zipfile.ZipFile(memory_zip, 'w', compression=zipfile.ZIP_DEFLATED) as zf:
zf.writestr('libs.txt', json.dumps(collect_stats()))
for fp in file_list:
zf.write(fp, os.path.basename(fp))
memory_zip.seek(0)
return send_file(memory_zip,
as_attachment=True,
attachment_filename="Calibre-Web-debug-pack.zip")

View File

@ -24,7 +24,10 @@ import io
import mimetypes import mimetypes
import re import re
import shutil import shutil
import glob
import time import time
import zipfile
import json
import unicodedata import unicodedata
from datetime import datetime, timedelta from datetime import datetime, timedelta
from tempfile import gettempdir from tempfile import gettempdir
@ -32,14 +35,12 @@ from tempfile import gettempdir
import requests import requests
from babel.dates import format_datetime from babel.dates import format_datetime
from babel.units import format_unit from babel.units import format_unit
from flask import send_from_directory, make_response, redirect, abort, url_for from flask import send_from_directory, make_response, redirect, abort, url_for, send_file
from flask_babel import gettext as _ from flask_babel import gettext as _
from flask_login import current_user from flask_login import current_user
from sqlalchemy.sql.expression import true, false, and_, text from sqlalchemy.sql.expression import true, false, and_, text
from werkzeug.datastructures import Headers from werkzeug.datastructures import Headers
from werkzeug.security import generate_password_hash from werkzeug.security import generate_password_hash
from . import calibre_db
from .tasks.convert import TaskConvert
try: try:
from urllib.parse import quote from urllib.parse import quote
@ -59,6 +60,8 @@ try:
except ImportError: except ImportError:
use_PIL = False use_PIL = False
from . import calibre_db
from .tasks.convert import TaskConvert
from . import logger, config, get_locale, db, ub from . import logger, config, get_locale, db, ub
from . import gdriveutils as gd from . import gdriveutils as gd
from .constants import STATIC_DIR as _STATIC_DIR from .constants import STATIC_DIR as _STATIC_DIR
@ -824,3 +827,4 @@ def get_download_link(book_id, book_format, client):
return do_download_file(book, book_format, client, data1, headers) return do_download_file(book, book_format, client, data1, headers)
else: else:
abort(404) abort(404)

View File

@ -132,14 +132,16 @@
</div> </div>
</div> </div>
<div class="row"> <div class="row form-group">
<div class="col"> <h2>{{_('Administration')}}</h2>
<h2>{{_('Administration')}}</h2> <div class="btn btn-default"><a id="debug" href="{{url_for('admin.download_debug')}}">{{_('Download Debug Package')}}</a></div>
<div class="btn btn-default"><a id="logfile" href="{{url_for('admin.view_logfile')}}">{{_('View Logs')}}</a></div> <div class="btn btn-default"><a id="logfile" href="{{url_for('admin.view_logfile')}}">{{_('View Logs')}}</a></div>
</div>
<div class="row form-group">
<div class="btn btn-default" id="restart_database" data-toggle="modal" data-target="#StatusDialog">{{_('Reconnect Calibre Database')}}</div> <div class="btn btn-default" id="restart_database" data-toggle="modal" data-target="#StatusDialog">{{_('Reconnect Calibre Database')}}</div>
<div class="btn btn-default" id="admin_restart" data-toggle="modal" data-target="#RestartDialog">{{_('Restart')}}</div> <div class="btn btn-default" id="admin_restart" data-toggle="modal" data-target="#RestartDialog">{{_('Restart')}}</div>
<div class="btn btn-default" id="admin_stop" data-toggle="modal" data-target="#ShutdownDialog">{{_('Shutdown')}}</div> <div class="btn btn-default" id="admin_stop" data-toggle="modal" data-target="#ShutdownDialog">{{_('Shutdown')}}</div>
</div>
</div> </div>
<div class="row"> <div class="row">

View File

@ -12,7 +12,18 @@
<label for="log0">{{_('Show Access Log: ')}}</label>{{logfiles[1]}}</div> <label for="log0">{{_('Show Access Log: ')}}</label>{{logfiles[1]}}</div>
{% endif %} {% endif %}
</div> </div>
<div class="row">
<div class="col-xs-6 col-sm-7">
{% if log_enable %}
<div class="btn btn-default"><a id="log_file" href="{{url_for('admin.download_log', logtype=0)}}">{{_('Download Calibre-Web Log')}}</a></div>
{% endif %}
{% if accesslog_enable %}
<div class="btn btn-default"><a id="log_file" href="{{url_for('admin.download_log', logtype=1)}}">{{_('Download Access Log')}}</a></div>
{% endif %}
</div>
</div>
<div id="renderer" class="log"></div> <div id="renderer" class="log"></div>
{% endblock %} {% endblock %}
{% block js %} {% block js %}
<script src="{{ url_for('static', filename='js/logviewer.js') }}"></script> <script src="{{ url_for('static', filename='js/logviewer.js') }}"></script>

View File

@ -8,8 +8,8 @@ Flask>=1.0.2,<1.2.0
iso-639>=0.4.5,<0.5.0 iso-639>=0.4.5,<0.5.0
PyPDF2==1.26.0,<1.27.0 PyPDF2==1.26.0,<1.27.0
pytz>=2016.10 pytz>=2016.10
requests>=2.11.1,<2.24.0 requests>=2.11.1,<2.26.0
SQLAlchemy>=1.3.0,<1.4.0 SQLAlchemy>=1.3.0,<1.4.0
tornado>=4.1,<6.1 tornado>=4.1,<6.2
Wand>=0.4.4,<0.6.0 Wand>=0.4.4,<0.6.0
unidecode>=0.04.19,<1.2.0 unidecode>=0.04.19,<1.2.0