From 32b7b39223e494e338d87e40ccfe05b7277afbdd Mon Sep 17 00:00:00 2001 From: Ozzieisaacs Date: Sun, 15 Nov 2020 14:19:25 +0100 Subject: [PATCH] Added function to download debug information --- cps/about.py | 13 ++++++--- cps/admin.py | 50 ++++++++++++++++++++++++++------ cps/debug_info.py | 55 ++++++++++++++++++++++++++++++++++++ cps/helper.py | 10 +++++-- cps/templates/admin.html | 10 ++++--- cps/templates/logviewer.html | 11 ++++++++ requirements.txt | 4 +-- 7 files changed, 131 insertions(+), 22 deletions(-) create mode 100644 cps/debug_info.py diff --git a/cps/about.py b/cps/about.py index 676d91db..f9c58738 100644 --- a/cps/about.py +++ b/cps/about.py @@ -82,6 +82,12 @@ _VERSIONS = OrderedDict( _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") @flask_login.login_required def stats(): @@ -89,8 +95,7 @@ def stats(): authors = calibre_db.session.query(db.Authors).count() categorys = calibre_db.session.query(db.Tags).count() series = calibre_db.session.query(db.Series).count() - _VERSIONS['ebook converter'] = _(converter.get_calibre_version()) - _VERSIONS['unrar'] = _(converter.get_unrar_version()) - _VERSIONS['kepubify'] = _(converter.get_kepubify_version()) - return render_title_template('stats.html', bookcounter=counter, authorcounter=authors, versions=_VERSIONS, + return render_title_template('stats.html', bookcounter=counter, authorcounter=authors, versions=collect_stats(), categorycounter=categorys, seriecounter=series, title=_(u"Statistics"), page="stat") + + diff --git a/cps/admin.py b/cps/admin.py index 409c5619..fe2769e2 100644 --- a/cps/admin.py +++ b/cps/admin.py @@ -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 .helper import check_valid_domain, send_test_mail, reset_password, generate_password_hash 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() @@ -218,7 +219,8 @@ def edit_domain(allow): @admin_required def add_domain(allow): 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: new_domain = ub.Registration(domain=domain_name, allow=allow) 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_string(to_save, "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_string(to_save, "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 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_UNAUTHENTICATE: 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: if not config.config_ldap_serv_username: 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.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) 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'), gdriveError) 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) 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'), gdriveError) 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) return reboot_required, None @@ -617,7 +625,10 @@ def _configuration_update_helper(): to_save = request.form.to_dict() 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: db_change |= _config_string(to_save, "config_calibre_dir") @@ -1028,6 +1039,27 @@ def send_logfile(logtype): else: return "" +@admi.route("/admin/logdownload/") +@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']) @login_required diff --git a/cps/debug_info.py b/cps/debug_info.py new file mode 100644 index 00000000..75ef3bb8 --- /dev/null +++ b/cps/debug_info.py @@ -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 . + +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") diff --git a/cps/helper.py b/cps/helper.py index 8561a95a..9845df97 100644 --- a/cps/helper.py +++ b/cps/helper.py @@ -24,7 +24,10 @@ import io import mimetypes import re import shutil +import glob import time +import zipfile +import json import unicodedata from datetime import datetime, timedelta from tempfile import gettempdir @@ -32,14 +35,12 @@ from tempfile import gettempdir import requests from babel.dates import format_datetime 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_login import current_user from sqlalchemy.sql.expression import true, false, and_, text from werkzeug.datastructures import Headers from werkzeug.security import generate_password_hash -from . import calibre_db -from .tasks.convert import TaskConvert try: from urllib.parse import quote @@ -59,6 +60,8 @@ try: except ImportError: use_PIL = False +from . import calibre_db +from .tasks.convert import TaskConvert from . import logger, config, get_locale, db, ub from . import gdriveutils as gd 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) else: abort(404) + diff --git a/cps/templates/admin.html b/cps/templates/admin.html index a9ceefdf..0e495030 100644 --- a/cps/templates/admin.html +++ b/cps/templates/admin.html @@ -132,14 +132,16 @@ -
-
-

{{_('Administration')}}

+
+

{{_('Administration')}}

+ +
+
{{_('Reconnect Calibre Database')}}
{{_('Restart')}}
{{_('Shutdown')}}
-
+
diff --git a/cps/templates/logviewer.html b/cps/templates/logviewer.html index e74ec27e..9827d15e 100644 --- a/cps/templates/logviewer.html +++ b/cps/templates/logviewer.html @@ -12,7 +12,18 @@ {{logfiles[1]}}
{% endif %}
+
+
+ {% if log_enable %} + + {% endif %} + {% if accesslog_enable %} + + {% endif %} +
+
+ {% endblock %} {% block js %} diff --git a/requirements.txt b/requirements.txt index 70359568..16e5844a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,8 +8,8 @@ Flask>=1.0.2,<1.2.0 iso-639>=0.4.5,<0.5.0 PyPDF2==1.26.0,<1.27.0 pytz>=2016.10 -requests>=2.11.1,<2.24.0 +requests>=2.11.1,<2.26.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 unidecode>=0.04.19,<1.2.0