diff --git a/README.md b/README.md index 369adfb..426555b 100644 --- a/README.md +++ b/README.md @@ -164,7 +164,7 @@ The use of environment variables allow you to provide over-rides to default sett | `SECRET_KEY` | A Django variable acting as a *salt* for most things that require security. This API uses it for the hash sequences when writing the configuration files to disk (`hash` mode only). | `ALLOWED_HOSTS` | A list of strings representing the host/domain names that this API can serve. This is a security measure to prevent HTTP Host header attacks, which are possible even under many seemingly-safe web server configurations. By default this is set to `*` allowing any host. Use space to delimit more than one host. | `BASE_URL` | Those who are hosting the API behind a proxy that requires a subpath to gain access to this API should specify this path here as well. By default this is not set at all. -| `LOG_LEVEL` | Adjust the log level to the console; the default value is `DEBUG` if the `DEBUG` is set below otherwise the default is `INFO`. Possible values are `ERROR`, `WARNING`, `INFO`, and `DEBUG`. +| `LOG_LEVEL` | Adjust the log level to the console. Possible values are `CRITICAL`, `ERROR`, `WARNING`, `INFO`, and `DEBUG`. | `DEBUG` | This defaults to `False` however can be set to `True` if defined with a non-zero value (such as `1`). diff --git a/apprise_api/api/templates/config.html b/apprise_api/api/templates/config.html index dac0da2..6175803 100644 --- a/apprise_api/api/templates/config.html +++ b/apprise_api/api/templates/config.html @@ -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; } diff --git a/apprise_api/api/tests/test_notify.py b/apprise_api/api/tests/test_notify.py index 51bc2b2..3af0f2b 100644 --- a/apprise_api/api/tests/test_notify.py +++ b/apprise_api/api/tests/test_notify.py @@ -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' diff --git a/apprise_api/api/views.py b/apprise_api/api/views.py index f0f89ef..dfbf51a 100644 --- a/apprise_api/api/views.py +++ b/apprise_api/api/views.py @@ -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 = '' + fmt = '