diff --git a/cps/__init__.py b/cps/__init__.py index 6c241d4f..1791098f 100755 --- a/cps/__init__.py +++ b/cps/__init__.py @@ -134,6 +134,4 @@ def get_timezone(): from .updater import Updater updater_thread = Updater() - - -__all__ = ['app'] +updater_thread.start() diff --git a/cps/admin.py b/cps/admin.py index 8e4f7df2..fa5b1caf 100644 --- a/cps/admin.py +++ b/cps/admin.py @@ -604,8 +604,8 @@ def _configuration_update_helper(): {"active":0}) element["active"] = 0 - _config_int("config_log_level") - _config_string("config_logfile") + reboot_required |= _config_int("config_log_level") + reboot_required |= _config_string("config_logfile") if not logger.is_valid_logfile(config.config_logfile): return _configuration_result('Logfile location is not valid, please enter correct path', gdriveError) @@ -968,7 +968,7 @@ def get_updater_status(): } status['text'] = text updater_thread.status = 0 - updater_thread.start() + updater_thread.resume() status['status'] = updater_thread.get_update_status() elif request.method == "GET": try: diff --git a/cps/helper.py b/cps/helper.py index f82564b8..6187ffa6 100644 --- a/cps/helper.py +++ b/cps/helper.py @@ -492,7 +492,7 @@ def get_book_cover_internal(book, # saves book cover from url def save_cover_from_url(url, book_path): - img = requests.get(url) + img = requests.get(url, timeout=10) # ToDo: Error Handling return save_cover(img, book_path) diff --git a/cps/server.py b/cps/server.py index 74c591ec..a59d7a38 100755 --- a/cps/server.py +++ b/cps/server.py @@ -24,7 +24,7 @@ import signal import socket try: - from gevent.pywsgi import WSGIServer + from gevent.pyewsgi import WSGIServer from gevent.pool import Pool from gevent import __version__ as _version VERSION = 'Gevent ' + _version @@ -193,6 +193,9 @@ class WebServer(object): self.stop() def stop(self, restart=False): + from . import updater_thread + updater_thread.stop() + log.info("webserver stop (restart=%s)", restart) self.restart = restart if self.wsgiserver: diff --git a/cps/static/js/logviewer.js b/cps/static/js/logviewer.js index aa86d09e..7b93f30f 100644 --- a/cps/static/js/logviewer.js +++ b/cps/static/js/logviewer.js @@ -17,8 +17,7 @@ // Upon loading load the logfile for the first option (event log) $(function() { - if ($("#log_group input").length) - { + if ($("#log_group input").length) { var element = $("#log_group input[type='radio']:checked").val(); init(element); } diff --git a/cps/static/js/main.js b/cps/static/js/main.js index 8f54ba45..bd8d04f9 100644 --- a/cps/static/js/main.js +++ b/cps/static/js/main.js @@ -61,6 +61,20 @@ $(function() { $("#RestartDialog").modal("hide"); } + function cleanUp() { + clearInterval(updateTimerID); + $("#spinner2").hide(); + $("#updateFinished").removeClass("hidden"); + $("#check_for_update").removeClass("hidden"); + $("#perform_update").addClass("hidden"); + $("#message").alert("close"); + $("#update_table > tbody > tr").each(function () { + if ($(this).attr("id") !== "current_version") { + $(this).closest("tr").remove(); + } + }); + } + function updateTimer() { $.ajax({ dataType: "json", @@ -69,21 +83,12 @@ $(function() { // console.log(data.status); $("#Updatecontent").html(updateText[data.status]); if (data.status > 6) { - clearInterval(updateTimerID); - $("#spinner2").hide(); - $("#updateFinished").removeClass("hidden"); - $("#check_for_update").removeClass("hidden"); - $("#perform_update").addClass("hidden"); + cleanUp(); } }, error: function error() { - // console.log('Done'); - clearInterval(updateTimerID); - $("#spinner2").hide(); $("#Updatecontent").html(updateText[7]); - $("#updateFinished").removeClass("hidden"); - $("#check_for_update").removeClass("hidden"); - $("#perform_update").addClass("hidden"); + cleanUp(); }, timeout: 2000 }); @@ -141,6 +146,8 @@ $(function() { var $this = $(this); var buttonText = $this.html(); $this.html("..."); + $("#Updatecontent").html(""); + $("#updateFinished").addClass("hidden"); $("#update_error").addClass("hidden"); if ($("#message").length) { $("#message").alert("close"); @@ -246,13 +253,13 @@ $(function() { }) .on("hidden.bs.modal", function() { $(this).find(".modal-body").html("..."); - $("#config_delete_kobo_token").show(); + $("#config_delete_kobo_token").show(); }); $("#btndeletetoken").click(function() { //get data-id attribute of the clicked element - var pathname = document.getElementsByTagName("script"), src = pathname[pathname.length-1].src; - var path = src.substring(0,src.lastIndexOf("/")); + var pathname = document.getElementsByTagName("script"), src = pathname[pathname.length - 1].src; + var path = src.substring(0, src.lastIndexOf("/")); // var domainId = $(this).value("domainId"); $.ajax({ method:"get", diff --git a/cps/static/js/table.js b/cps/static/js/table.js index de570ed8..1478a519 100644 --- a/cps/static/js/table.js +++ b/cps/static/js/table.js @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -/* exported TableActions */ +/* exported TableActions, RestrictionActions*/ $(function() { @@ -94,46 +94,44 @@ $(function() { $(e.currentTarget).find("#btndeletedomain").data("domainId", domainId); }); - $('#restrictModal').on('hidden.bs.modal', function () { + $("#restrictModal").on("hidden.bs.modal", function () { // Destroy table and remove hooks for buttons $("#restrict-elements-table").unbind(); - $('#restrict-elements-table').bootstrapTable('destroy'); + $("#restrict-elements-table").bootstrapTable("destroy"); $("[id^=submit_]").unbind(); - $('#h1').addClass('hidden'); - $('#h2').addClass('hidden'); - $('#h3').addClass('hidden'); - $('#h4').addClass('hidden'); + $("#h1").addClass("hidden"); + $("#h2").addClass("hidden"); + $("#h3").addClass("hidden"); + $("#h4").addClass("hidden"); }); - function startTable(type){ - var pathname = document.getElementsByTagName("script"), src = pathname[pathname.length-1].src; - var path = src.substring(0,src.lastIndexOf("/")); + function startTable(type) { + var pathname = document.getElementsByTagName("script"), src = pathname[pathname.length - 1].src; + var path = src.substring(0, src.lastIndexOf("/")); $("#restrict-elements-table").bootstrapTable({ formatNoMatches: function () { return ""; }, url: path + "/../../ajax/listrestriction/" + type, rowStyle: function(row, index) { - console.log('Reihe :' + row + ' Index :'+ index); - if (row.id.charAt(0) == 'a') { - return {classes: 'bg-primary'} - } - else { - return {classes: 'bg-dark-danger'} + // console.log('Reihe :' + row + " Index :" + index); + if (row.id.charAt(0) === "a") { + return {classes: "bg-primary"}; + } else { + return {classes: "bg-dark-danger"}; } }, onClickCell: function (field, value, row, $element) { - if(field == 3){ - console.log("element") + if (field == 3) { $.ajax ({ - type: 'Post', - data: 'id=' + row.id + '&type=' + row.type + "&Element=" + row.Element, + type: "Post", + data: "id=" + row.id + "&type=" + row.type + "&Element=" + row.Element, url: path + "/../../ajax/deleterestriction/" + type, async: true, timeout: 900, success:function(data) { $.ajax({ method:"get", - url: path + "/../../ajax/listrestriction/"+type, + url: path + "/../../ajax/listrestriction/" + type, async: true, timeout: 900, success:function(data) { @@ -146,12 +144,11 @@ $(function() { }, striped: false }); - $("#restrict-elements-table").removeClass('table-hover'); - $("#restrict-elements-table").on('editable-save.bs.table', function (e, field, row, old, $el) { - console.log("Hallo"); + $("#restrict-elements-table").removeClass("table-hover"); + $("#restrict-elements-table").on("editable-save.bs.table", function (e, field, row, old, $el) { $.ajax({ - url: path + "/../../ajax/editrestriction/"+type, - type: 'Post', + url: path + "/../../ajax/editrestriction/" + type, + type: "Post", data: row //$(this).closest("form").serialize() + "&" + $(this)[0].name + "=", }); }); @@ -159,48 +156,43 @@ $(function() { // event.stopPropagation(); // event.preventDefault(); $(this)[0].blur(); - console.log($(this)[0].name); $.ajax({ - url: path + "/../../ajax/addrestriction/"+type, - type: 'Post', + url: path + "/../../ajax/addrestriction/" + type, + type: "Post", data: $(this).closest("form").serialize() + "&" + $(this)[0].name + "=", success: function () { - $.ajax ({ - method:"get", - url: path + "/../../ajax/listrestriction/"+type, - async: true, - timeout: 900, - success:function(data) { - $("#restrict-elements-table").bootstrapTable("load", data); - } + $.ajax ({ + method:"get", + url: path + "/../../ajax/listrestriction/" + type, + async: true, + timeout: 900, + success:function(data) { + $("#restrict-elements-table").bootstrapTable("load", data); + } }); } }); return; }); } - $('#get_column_values').on('click',function() - { + $("#get_column_values").on("click", function() { startTable(1); - $('#h2').removeClass('hidden'); + $("#h2").removeClass("hidden"); }); - $('#get_tags').on('click',function() - { + $("#get_tags").on("click", function() { startTable(0); - $('#h1').removeClass('hidden'); + $("#h1").removeClass("hidden"); }); - $('#get_user_column_values').on('click',function() - { + $("#get_user_column_values").on("click", function() { startTable(3); - $('#h4').removeClass('hidden'); + $("#h4").removeClass("hidden"); }); - $('#get_user_tags').on('click',function() - { + $("#get_user_tags").on("click", function() { startTable(2); $(this)[0].blur(); - $('#h3').removeClass('hidden'); + $("#h3").removeClass("hidden"); }); }); diff --git a/cps/updater.py b/cps/updater.py index 7af8649c..34f3ceef 100644 --- a/cps/updater.py +++ b/cps/updater.py @@ -53,8 +53,13 @@ class Updater(threading.Thread): def __init__(self): threading.Thread.__init__(self) + self.paused = False + # self.pause_cond = threading.Condition(threading.Lock()) + self.can_run = threading.Event() + self.pause() self.status = -1 self.updateIndex = None + # self.run() def get_current_version_info(self): if config.config_updatechannel == constants.UPDATE_STABLE: @@ -66,12 +71,12 @@ class Updater(threading.Thread): return self._stable_available_updates(request_method) return self._nightly_available_updates(request_method,locale) - def run(self): + def do_work(self): try: self.status = 1 log.debug(u'Download update file') headers = {'Accept': 'application/vnd.github.v3+json'} - r = requests.get(self._get_request_path(), stream=True, headers=headers) + r = requests.get(self._get_request_path(), stream=True, headers=headers, timeout=(10, 600)) r.raise_for_status() self.status = 2 @@ -85,7 +90,8 @@ class Updater(threading.Thread): if not os.path.isdir(foldername): self.status = 11 log.info(u'Extracted contents of zipfile not found in temp folder') - return + self.pause() + return False self.status = 4 log.debug(u'Replacing files') self.update_source(foldername, constants.BASE_DIR) @@ -95,6 +101,7 @@ class Updater(threading.Thread): web_server.stop(True) self.status = 7 time.sleep(2) + return True except requests.exceptions.HTTPError as ex: log.info(u'HTTP Error %s', ex) self.status = 8 @@ -104,9 +111,31 @@ class Updater(threading.Thread): except requests.exceptions.Timeout: log.info(u'Timeout while establishing connection') self.status = 10 - except requests.exceptions.RequestException: + except (requests.exceptions.RequestException, zipfile.BadZipFile): self.status = 11 log.info(u'General error') + self.pause() + return False + + def run(self): + while True: + self.can_run.wait() + if self.status > -1: + if self.do_work(): + break # stop loop and end thread for restart + else: + break + + def pause(self): + self.can_run.clear() + + #should just resume the thread + def resume(self): + self.can_run.set() + + def stop(self): + self.status = -2 + self.can_run.set() def get_update_status(self): return self.status @@ -258,16 +287,19 @@ class Updater(threading.Thread): parents = [] if status['message'] != '': return json.dumps(status) - if 'object' not in commit: + if 'object' not in commit or 'url' not in commit['object']: status['message'] = _(u'Unexpected data while reading update information') return json.dumps(status) - - if commit['object']['sha'] == status['current_commit_hash']: - status.update({ - 'update': False, - 'success': True, - 'message': _(u'No update available. You already have the latest version installed') - }) + try: + if commit['object']['sha'] == status['current_commit_hash']: + status.update({ + 'update': False, + 'success': True, + 'message': _(u'No update available. You already have the latest version installed') + }) + return json.dumps(status) + except (TypeError, KeyError): + status['message'] = _(u'Unexpected data while reading update information') return json.dumps(status) # a new update is available @@ -275,22 +307,25 @@ class Updater(threading.Thread): try: headers = {'Accept': 'application/vnd.github.v3+json'} - r = requests.get(repository_url + '/git/commits/' + commit['object']['sha'], headers=headers) + r = requests.get(repository_url + '/git/commits/' + commit['object']['sha'], + headers=headers, + timeout=10) r.raise_for_status() update_data = r.json() except requests.exceptions.HTTPError as e: - status['error'] = _(u'HTTP Error') + ' ' + str(e) + status['message'] = _(u'HTTP Error') + ' ' + str(e) except requests.exceptions.ConnectionError: - status['error'] = _(u'Connection error') + status['message'] = _(u'Connection error') except requests.exceptions.Timeout: - status['error'] = _(u'Timeout while establishing connection') - except requests.exceptions.RequestException: - status['error'] = _(u'General error') + status['message'] = _(u'Timeout while establishing connection') + except (requests.exceptions.RequestException, ValueError): + status['message'] = _(u'General error') if status['message'] != '': return json.dumps(status) - if 'committer' in update_data and 'message' in update_data: + # if 'committer' in update_data and 'message' in update_data: + try: status['success'] = True status['message'] = _( u'A new update is available. Click on the button below to update to the latest version.') @@ -304,14 +339,13 @@ class Updater(threading.Thread): update_data['sha'] ] ) - # it only makes sense to analyze the parents if we know the current commit hash if status['current_commit_hash'] != '': try: parent_commit = update_data['parents'][0] # limit the maximum search depth remaining_parents_cnt = 10 - except IndexError: + except (IndexError, KeyError): remaining_parents_cnt = None if remaining_parents_cnt is not None: @@ -323,7 +357,7 @@ class Updater(threading.Thread): if parent_commit['sha'] != status['current_commit_hash']: try: headers = {'Accept': 'application/vnd.github.v3+json'} - r = requests.get(parent_commit['url'], headers=headers) + r = requests.get(parent_commit['url'], headers=headers, timeout=10) r.raise_for_status() parent_data = r.json() @@ -343,7 +377,7 @@ class Updater(threading.Thread): # parent is our current version break status['history'] = parents[::-1] - else: + except (IndexError, KeyError): status['success'] = False status['message'] = _(u'Could not fetch update information') return json.dumps(status) @@ -377,8 +411,9 @@ class Updater(threading.Thread): return json.dumps(status) i = len(commit) - 1 + newer = False while i >= 0: - if 'tag_name' not in commit[i] or 'body' not in commit[i]: + if 'tag_name' not in commit[i] or 'body' not in commit[i] or 'zipball_url' not in commit[i]: status['message'] = _(u'Unexpected data while reading update information') return json.dumps(status) major_version_update = int(commit[i]['tag_name'].split('.')[0]) @@ -392,12 +427,13 @@ class Updater(threading.Thread): except ValueError: current_version[2] = int(current_version[2].split(' ')[0])-1 - # Check if major versions are identical search for newest nonenqual commit and update to this one + # Check if major versions are identical search for newest non equal commit and update to this one if major_version_update == current_version[0]: if (minor_version_update == current_version[1] and patch_version_update > current_version[2]) or \ minor_version_update > current_version[1]: parents.append([commit[i]['tag_name'], commit[i]['body'].replace('\r\n', '

')]) + newer=True i -= 1 continue if major_version_update < current_version[0]: @@ -406,7 +442,9 @@ class Updater(threading.Thread): if major_version_update > current_version[0]: # found update update to last version before major update, unless current version is on last version # before major update - if commit[i+1]['tag_name'].split('.')[1] == current_version[1]: + if i == (len(commit) - 1): + i -= 1 + if int(commit[i+1]['tag_name'].split('.')[1]) == current_version[1]: parents.append([commit[i]['tag_name'], commit[i]['body'].replace('\r\n', '

').replace('\n', '

')]) status.update({ @@ -418,16 +456,18 @@ class Updater(threading.Thread): }) self.updateFile = commit[i]['zipball_url'] else: + parents.append([commit[i+1]['tag_name'], + commit[i+1]['body'].replace('\r\n', '

').replace('\n', '

')]) status.update({ 'update': True, 'success': True, 'message': _(u'A new update is available. Click on the button below to ' - u'update to version: %(version)s', version=commit[i]['tag_name']), + u'update to version: %(version)s', version=commit[i+1]['tag_name']), 'history': parents }) self.updateFile = commit[i+1]['zipball_url'] break - if i == -1: + if i == -1 and newer == False: status.update({ 'update': True, 'success': True, @@ -436,6 +476,16 @@ class Updater(threading.Thread): 'history': parents }) self.updateFile = commit[0]['zipball_url'] + elif i == -1 and newer == True: + status.update({ + 'update': True, + 'success': True, + 'message': _(u'A new update is available. Click on the button below to ' + u'update to version: %(version)s', version=commit[0]['tag_name']), + 'history': parents + }) + self.updateFile = commit[0]['zipball_url'] + return json.dumps(status) def _get_request_path(self): @@ -458,7 +508,7 @@ class Updater(threading.Thread): status['current_commit_hash'] = version['version'] try: headers = {'Accept': 'application/vnd.github.v3+json'} - r = requests.get(repository_url, headers=headers) + r = requests.get(repository_url, headers=headers, timeout=10) commit = r.json() r.raise_for_status() except requests.exceptions.HTTPError as e: @@ -471,7 +521,7 @@ class Updater(threading.Thread): status['message'] = _(u'Connection error') except requests.exceptions.Timeout: status['message'] = _(u'Timeout while establishing connection') - except requests.exceptions.RequestException: + except (requests.exceptions.RequestException, ValueError): status['message'] = _(u'General error') - + log.debug('Updater status: %s', status['message']) return status, commit