Logging details available via Apprise/API/Website (#34)

This commit is contained in:
Chris Caron
2021-01-01 17:10:44 -05:00
committed by GitHub
parent b29ddf15c0
commit a57b621c29
7 changed files with 220 additions and 31 deletions

View File

@ -267,11 +267,18 @@ document.querySelector('#addconfig').onsubmit = function(event) {
'{% trans "Successfully saved the specified URL(s)." %}',
'success'
);
} else if(response.status > 500) {
// Disk issue
Swal.fire(
'{% trans "Save" %}',
'{% trans "There was an issue writing the configuration to your filesystem. Check your file permissions and try again." %}',
'error'
);
} else {
// user notification
Swal.fire(
'{% trans "Save" %}',
'{% trans "Failed to save the specified URL(s)." %}',
'{% trans "Failed to save the specified URL(s). Check your syntax and try again." %}',
'error'
);
}
@ -338,23 +345,36 @@ document.querySelector('#donotify').onsubmit = function(event) {
let response = fetch('{% url "notify" key %}', {
method: 'POST',
body: body,
headers: {
'Accept': 'text/html',
'X-Apprise-Log-Level': 'info'
}
}).then(function(response) {
if(response.status == 200)
{
// user notification
Swal.fire(
'{% trans "Notification" %}',
'{% trans "Successfully sent the notification(s)." %}',
'success'
);
} else {
// user notification
Swal.fire(
'{% trans "Notification" %}',
'{% trans "Failed to send the notification(s)." %}',
'error'
);
}
response.text().then(function (html) {
if(response.status == 200)
{
// user notification
Swal.fire(
'{% trans "Notification" %}',
'{% trans "Successfully sent the notification(s)." %}' + html,
'success'
);
} else if(response.status == 424) {
// user notification
Swal.fire(
'{% trans "Notification" %}',
'{% trans "One or more of the notification(s) were not sent." %}' + html,
'warning'
);
} else {
// user notification
Swal.fire(
'{% trans "Notification" %}',
'{% trans "Failed to send the notification(s)." %}' + html,
'error'
);
}
});
});
return false;
}

View File

@ -241,7 +241,7 @@ class NotifyTests(SimpleTestCase):
'format': 'invalid'
}
# Test referencing a key that doesn't exist
# Test case with format set to invalid
response = self.client.post(
'/notify/{}'.format(key),
data=json.dumps(json_data),
@ -261,7 +261,7 @@ class NotifyTests(SimpleTestCase):
'format': None,
}
# Test referencing a key that doesn't exist
# Test case with format changed
response = self.client.post(
'/notify/{}'.format(key),
data=json.dumps(json_data),
@ -284,3 +284,61 @@ class NotifyTests(SimpleTestCase):
assert response.status_code == 200
assert mock_notify.call_count == 1
# Reset our count
mock_notify.reset_mock()
headers = {
'HTTP_X_APPRISE_LOG_LEVEL': 'debug',
'HTTP_ACCEPT': 'text/plain',
}
# Test referencing a key that doesn't exist
response = self.client.post(
'/notify/{}'.format(key),
data=json.dumps(json_data),
content_type='application/json',
**headers,
)
assert response.status_code == 200
assert mock_notify.call_count == 1
assert response['content-type'] == 'text/plain'
headers = {
'HTTP_X_APPRISE_LOG_LEVEL': 'debug',
'HTTP_ACCEPT': 'text/html',
}
mock_notify.reset_mock()
# Test referencing a key that doesn't exist
response = self.client.post(
'/notify/{}'.format(key),
data=json.dumps(json_data),
content_type='application/json',
**headers,
)
assert response.status_code == 200
assert mock_notify.call_count == 1
assert response['content-type'] == 'text/html'
headers = {
'HTTP_X_APPRISE_LOG_LEVEL': 'invalid',
'HTTP_ACCEPT': 'text/*',
}
mock_notify.reset_mock()
# Test referencing a key that doesn't exist
response = self.client.post(
'/notify/{}'.format(key),
data=json.dumps(json_data),
content_type='application/json',
**headers,
)
assert response.status_code == 200
assert mock_notify.call_count == 1
assert response['content-type'] == 'text/html'

View File

@ -27,6 +27,7 @@ from django.http import HttpResponse
from django.http import JsonResponse
from django.views import View
from django.conf import settings
from django.utils.html import escape
from django.utils.decorators import method_decorator
from django.views.decorators.cache import never_cache
from django.views.decorators.gzip import gzip_page
@ -422,25 +423,88 @@ class NotifyView(View):
# Add our configuration
a_obj.add(ac_obj)
# Perform our notification at this point
result = a_obj.notify(
content.get('body'),
title=content.get('title', ''),
notify_type=content.get('type', apprise.NotifyType.INFO),
tag=content.get('tag'),
)
# Our return content type can be controlled by the Accept keyword
# If it includes /* or /html somewhere then we return html, otherwise
# we return the logs as they're processed in their text format.
# The HTML response type has a bit of overhead where as it's not
# the case with text/plain
content_type = \
'text/html' if re.search(r'text\/(\*|html)',
request.headers.get('Accept', ''),
re.IGNORECASE) \
else 'text/plain'
# Acquire our log level from headers if defined, otherwise use
# the global one set in the settings
level = request.headers.get(
'X-Apprise-Log-Level',
settings.LOGGING['loggers']['apprise']['level']).upper()
# Initialize our response object
response = None
if level in ('CRITICAL', 'ERROR' 'WARNING', 'INFO', 'DEBUG'):
level = getattr(apprise.logging, level)
esc = '<!!-!ESC!-!!>'
fmt = '<li class="log_%(levelname)s">' \
'<div class="log_time">%(asctime)s</div>' \
'<div class="log_level">%(levelname)s</div>' \
f'<div class="log_msg">{esc}%(message)s{esc}</div></li>' \
if content_type == 'text/html' else \
settings.LOGGING['formatters']['standard']['format']
# Now specify our format (and over-ride the default):
with apprise.LogCapture(level=level, fmt=fmt) as logs:
# Perform our notification at this point
result = a_obj.notify(
content.get('body'),
title=content.get('title', ''),
notify_type=content.get('type', apprise.NotifyType.INFO),
tag=content.get('tag'),
)
if content_type == 'text/html':
# Iterate over our entries so that we can prepare to escape
# things to be presented as HTML
esc = re.escape(esc)
entries = re.findall(
r'(?P<head><li .+?){}(?P<to_escape>.*?)'
r'{}(?P<tail>.+li>$)(?=$|<li .+{})'.format(
esc, esc, esc), logs.getvalue(),
re.DOTALL)
# Wrap logs in `<ul>` tag and escape our message body:
response = '<ul class="logs">{}</ul>'.format(
''.join([e[0] + escape(e[1]) + e[2] for e in entries]))
else: # content_type == 'text/plain'
response = logs.getvalue()
else:
# Perform our notification at this point without logging
result = a_obj.notify(
content.get('body'),
title=content.get('title', ''),
notify_type=content.get('type', apprise.NotifyType.INFO),
tag=content.get('tag'),
)
if not result:
# If at least one notification couldn't be sent; change up
# the response to a 424 error code
return HttpResponse(
response if response is not None else
_('One or more notification could not be sent.'),
content_type=content_type,
status=ResponseCode.failed_dependency)
# Return our retrieved content
return HttpResponse(
response if response is not None else
_('Notification(s) sent.'),
status=ResponseCode.okay
content_type=content_type,
status=ResponseCode.okay,
)