Browse Source

Add the WEBHOOKS_ENABLED setting

pull/474/head
Pēteris Caune 4 years ago
parent
commit
67ff8a9bee
No known key found for this signature in database GPG Key ID: E28D7679E9A9EDE2
12 changed files with 378 additions and 306 deletions
  1. +1
    -0
      CHANGELOG.md
  2. +1
    -0
      docker/.env
  3. +1
    -304
      hc/api/tests/test_notify.py
  4. +344
    -0
      hc/api/tests/test_notify_webhook.py
  5. +3
    -0
      hc/api/transports.py
  6. +7
    -0
      hc/front/tests/test_add_webhook.py
  7. +3
    -0
      hc/front/views.py
  8. +3
    -0
      hc/settings.py
  9. +4
    -1
      templates/docs/self_hosted_configuration.html
  10. +7
    -1
      templates/docs/self_hosted_configuration.md
  11. +2
    -0
      templates/front/channels.html
  12. +2
    -0
      templates/front/welcome.html

+ 1
- 0
CHANGELOG.md View File

@ -13,6 +13,7 @@ All notable changes to this project will be documented in this file.
- Add a section in Docs about running self-hosted instances - Add a section in Docs about running self-hosted instances
- Add experimental Dockerfile and docker-compose.yml - Add experimental Dockerfile and docker-compose.yml
- Add rate limiting for Pushover notifications (6 notifications / user / minute) - Add rate limiting for Pushover notifications (6 notifications / user / minute)
- Add the WEBHOOKS_ENABLED setting (#471)
## Bug Fixes ## Bug Fixes
- Fix unwanted HTML escaping in SMS and WhatsApp notifications - Fix unwanted HTML escaping in SMS and WhatsApp notifications


+ 1
- 0
docker/.env View File

@ -53,3 +53,4 @@ TWILIO_AUTH=
TWILIO_FROM= TWILIO_FROM=
TWILIO_USE_WHATSAPP=False TWILIO_USE_WHATSAPP=False
USE_PAYMENTS=False USE_PAYMENTS=False
WEBHOOKS_ENABLED=True

+ 1
- 304
hc/api/tests/test_notify.py View File

@ -8,7 +8,7 @@ from django.core import mail
from django.utils.timezone import now from django.utils.timezone import now
from hc.api.models import Channel, Check, Notification, TokenBucket from hc.api.models import Channel, Check, Notification, TokenBucket
from hc.test import BaseTestCase from hc.test import BaseTestCase
from requests.exceptions import ConnectionError, Timeout
from requests.exceptions import Timeout
from django.test.utils import override_settings from django.test.utils import override_settings
@ -26,309 +26,6 @@ class NotifyTestCase(BaseTestCase):
self.channel.save() self.channel.save()
self.channel.checks.add(self.check) self.channel.checks.add(self.check)
@patch("hc.api.transports.requests.request")
def test_webhook(self, mock_get):
definition = {
"method_down": "GET",
"url_down": "http://example",
"body_down": "",
"headers_down": {},
}
self._setup_data("webhook", json.dumps(definition))
mock_get.return_value.status_code = 200
self.channel.notify(self.check)
mock_get.assert_called_with(
"get",
"http://example",
headers={"User-Agent": "healthchecks.io"},
timeout=5,
)
@patch("hc.api.transports.requests.request", side_effect=Timeout)
def test_webhooks_handle_timeouts(self, mock_get):
definition = {
"method_down": "GET",
"url_down": "http://example",
"body_down": "",
"headers_down": {},
}
self._setup_data("webhook", json.dumps(definition))
self.channel.notify(self.check)
# The transport should have retried 3 times
self.assertEqual(mock_get.call_count, 3)
n = Notification.objects.get()
self.assertEqual(n.error, "Connection timed out")
self.channel.refresh_from_db()
self.assertEqual(self.channel.last_error, "Connection timed out")
@patch("hc.api.transports.requests.request", side_effect=ConnectionError)
def test_webhooks_handle_connection_errors(self, mock_get):
definition = {
"method_down": "GET",
"url_down": "http://example",
"body_down": "",
"headers_down": {},
}
self._setup_data("webhook", json.dumps(definition))
self.channel.notify(self.check)
# The transport should have retried 3 times
self.assertEqual(mock_get.call_count, 3)
n = Notification.objects.get()
self.assertEqual(n.error, "Connection failed")
@patch("hc.api.transports.requests.request")
def test_webhooks_handle_500(self, mock_get):
definition = {
"method_down": "GET",
"url_down": "http://example",
"body_down": "",
"headers_down": {},
}
self._setup_data("webhook", json.dumps(definition))
mock_get.return_value.status_code = 500
self.channel.notify(self.check)
# The transport should have retried 3 times
self.assertEqual(mock_get.call_count, 3)
n = Notification.objects.get()
self.assertEqual(n.error, "Received status code 500")
@patch("hc.api.transports.requests.request", side_effect=Timeout)
def test_webhooks_dont_retry_when_sending_test_notifications(self, mock_get):
definition = {
"method_down": "GET",
"url_down": "http://example",
"body_down": "",
"headers_down": {},
}
self._setup_data("webhook", json.dumps(definition))
self.channel.notify(self.check, is_test=True)
# is_test flag is set, the transport should not retry:
self.assertEqual(mock_get.call_count, 1)
n = Notification.objects.get()
self.assertEqual(n.error, "Connection timed out")
@patch("hc.api.transports.requests.request")
def test_webhooks_support_variables(self, mock_get):
definition = {
"method_down": "GET",
"url_down": "http://host/$CODE/$STATUS/$TAG1/$TAG2/?name=$NAME",
"body_down": "",
"headers_down": {},
}
self._setup_data("webhook", json.dumps(definition))
self.check.name = "Hello World"
self.check.tags = "foo bar"
self.check.save()
self.channel.notify(self.check)
url = "http://host/%s/down/foo/bar/?name=Hello%%20World" % self.check.code
args, kwargs = mock_get.call_args
self.assertEqual(args[0], "get")
self.assertEqual(args[1], url)
self.assertEqual(kwargs["headers"], {"User-Agent": "healthchecks.io"})
self.assertEqual(kwargs["timeout"], 5)
@patch("hc.api.transports.requests.request")
def test_webhooks_handle_variable_variables(self, mock_get):
definition = {
"method_down": "GET",
"url_down": "http://host/$$NAMETAG1",
"body_down": "",
"headers_down": {},
}
self._setup_data("webhook", json.dumps(definition))
self.check.tags = "foo bar"
self.check.save()
self.channel.notify(self.check)
# $$NAMETAG1 should *not* get transformed to "foo"
args, kwargs = mock_get.call_args
self.assertEqual(args[1], "http://host/$TAG1")
@patch("hc.api.transports.requests.request")
def test_webhooks_support_post(self, mock_request):
definition = {
"method_down": "POST",
"url_down": "http://example.com",
"body_down": "The Time Is $NOW",
"headers_down": {},
}
self._setup_data("webhook", json.dumps(definition))
self.check.save()
self.channel.notify(self.check)
args, kwargs = mock_request.call_args
self.assertEqual(args[0], "post")
self.assertEqual(args[1], "http://example.com")
# spaces should not have been urlencoded:
payload = kwargs["data"].decode()
self.assertTrue(payload.startswith("The Time Is 2"))
@patch("hc.api.transports.requests.request")
def test_webhooks_dollarsign_escaping(self, mock_get):
# If name or tag contains what looks like a variable reference,
# that should be left alone:
definition = {
"method_down": "GET",
"url_down": "http://host/$NAME",
"body_down": "",
"headers_down": {},
}
self._setup_data("webhook", json.dumps(definition))
self.check.name = "$TAG1"
self.check.tags = "foo"
self.check.save()
self.channel.notify(self.check)
url = "http://host/%24TAG1"
mock_get.assert_called_with(
"get", url, headers={"User-Agent": "healthchecks.io"}, timeout=5
)
@patch("hc.api.transports.requests.request")
def test_webhooks_handle_up_events(self, mock_get):
definition = {
"method_up": "GET",
"url_up": "http://bar",
"body_up": "",
"headers_up": {},
}
self._setup_data("webhook", json.dumps(definition), status="up")
self.channel.notify(self.check)
mock_get.assert_called_with(
"get", "http://bar", headers={"User-Agent": "healthchecks.io"}, timeout=5
)
@patch("hc.api.transports.requests.request")
def test_webhooks_handle_noop_up_events(self, mock_get):
definition = {
"method_up": "GET",
"url_up": "",
"body_up": "",
"headers_up": {},
}
self._setup_data("webhook", json.dumps(definition), status="up")
self.channel.notify(self.check)
self.assertFalse(mock_get.called)
self.assertEqual(Notification.objects.count(), 0)
@patch("hc.api.transports.requests.request")
def test_webhooks_handle_unicode_post_body(self, mock_request):
definition = {
"method_down": "POST",
"url_down": "http://foo.com",
"body_down": "(╯°□°)╯︵ ┻━┻",
"headers_down": {},
}
self._setup_data("webhook", json.dumps(definition))
self.check.save()
self.channel.notify(self.check)
args, kwargs = mock_request.call_args
# unicode should be encoded into utf-8
self.assertIsInstance(kwargs["data"], bytes)
@patch("hc.api.transports.requests.request")
def test_webhooks_handle_post_headers(self, mock_request):
definition = {
"method_down": "POST",
"url_down": "http://foo.com",
"body_down": "data",
"headers_down": {"Content-Type": "application/json"},
}
self._setup_data("webhook", json.dumps(definition))
self.channel.notify(self.check)
headers = {"User-Agent": "healthchecks.io", "Content-Type": "application/json"}
mock_request.assert_called_with(
"post", "http://foo.com", data=b"data", headers=headers, timeout=5
)
@patch("hc.api.transports.requests.request")
def test_webhooks_handle_get_headers(self, mock_request):
definition = {
"method_down": "GET",
"url_down": "http://foo.com",
"body_down": "",
"headers_down": {"Content-Type": "application/json"},
}
self._setup_data("webhook", json.dumps(definition))
self.channel.notify(self.check)
headers = {"User-Agent": "healthchecks.io", "Content-Type": "application/json"}
mock_request.assert_called_with(
"get", "http://foo.com", headers=headers, timeout=5
)
@patch("hc.api.transports.requests.request")
def test_webhooks_allow_user_agent_override(self, mock_request):
definition = {
"method_down": "GET",
"url_down": "http://foo.com",
"body_down": "",
"headers_down": {"User-Agent": "My-Agent"},
}
self._setup_data("webhook", json.dumps(definition))
self.channel.notify(self.check)
headers = {"User-Agent": "My-Agent"}
mock_request.assert_called_with(
"get", "http://foo.com", headers=headers, timeout=5
)
@patch("hc.api.transports.requests.request")
def test_webhooks_support_variables_in_headers(self, mock_request):
definition = {
"method_down": "GET",
"url_down": "http://foo.com",
"body_down": "",
"headers_down": {"X-Message": "$NAME is DOWN"},
}
self._setup_data("webhook", json.dumps(definition))
self.check.name = "Foo"
self.check.save()
self.channel.notify(self.check)
headers = {"User-Agent": "healthchecks.io", "X-Message": "Foo is DOWN"}
mock_request.assert_called_with(
"get", "http://foo.com", headers=headers, timeout=5
)
@patch("hc.api.transports.requests.request") @patch("hc.api.transports.requests.request")
def test_pd(self, mock_post): def test_pd(self, mock_post):
self._setup_data("pd", "123") self._setup_data("pd", "123")


+ 344
- 0
hc/api/tests/test_notify_webhook.py View File

@ -0,0 +1,344 @@
# coding: utf-8
from datetime import timedelta as td
import json
from unittest.mock import patch
from django.utils.timezone import now
from hc.api.models import Channel, Check, Notification
from hc.test import BaseTestCase
from requests.exceptions import ConnectionError, Timeout
from django.test.utils import override_settings
class NotifyWebhookTestCase(BaseTestCase):
def _setup_data(self, value, status="down", email_verified=True):
self.check = Check(project=self.project)
self.check.status = status
self.check.last_ping = now() - td(minutes=61)
self.check.save()
self.channel = Channel(project=self.project)
self.channel.kind = "webhook"
self.channel.value = value
self.channel.email_verified = email_verified
self.channel.save()
self.channel.checks.add(self.check)
@patch("hc.api.transports.requests.request")
def test_webhook(self, mock_get):
definition = {
"method_down": "GET",
"url_down": "http://example",
"body_down": "",
"headers_down": {},
}
self._setup_data(json.dumps(definition))
mock_get.return_value.status_code = 200
self.channel.notify(self.check)
mock_get.assert_called_with(
"get",
"http://example",
headers={"User-Agent": "healthchecks.io"},
timeout=5,
)
@patch("hc.api.transports.requests.request", side_effect=Timeout)
def test_webhooks_handle_timeouts(self, mock_get):
definition = {
"method_down": "GET",
"url_down": "http://example",
"body_down": "",
"headers_down": {},
}
self._setup_data(json.dumps(definition))
self.channel.notify(self.check)
# The transport should have retried 3 times
self.assertEqual(mock_get.call_count, 3)
n = Notification.objects.get()
self.assertEqual(n.error, "Connection timed out")
self.channel.refresh_from_db()
self.assertEqual(self.channel.last_error, "Connection timed out")
@patch("hc.api.transports.requests.request", side_effect=ConnectionError)
def test_webhooks_handle_connection_errors(self, mock_get):
definition = {
"method_down": "GET",
"url_down": "http://example",
"body_down": "",
"headers_down": {},
}
self._setup_data(json.dumps(definition))
self.channel.notify(self.check)
# The transport should have retried 3 times
self.assertEqual(mock_get.call_count, 3)
n = Notification.objects.get()
self.assertEqual(n.error, "Connection failed")
@patch("hc.api.transports.requests.request")
def test_webhooks_handle_500(self, mock_get):
definition = {
"method_down": "GET",
"url_down": "http://example",
"body_down": "",
"headers_down": {},
}
self._setup_data(json.dumps(definition))
mock_get.return_value.status_code = 500
self.channel.notify(self.check)
# The transport should have retried 3 times
self.assertEqual(mock_get.call_count, 3)
n = Notification.objects.get()
self.assertEqual(n.error, "Received status code 500")
@patch("hc.api.transports.requests.request", side_effect=Timeout)
def test_webhooks_dont_retry_when_sending_test_notifications(self, mock_get):
definition = {
"method_down": "GET",
"url_down": "http://example",
"body_down": "",
"headers_down": {},
}
self._setup_data(json.dumps(definition))
self.channel.notify(self.check, is_test=True)
# is_test flag is set, the transport should not retry:
self.assertEqual(mock_get.call_count, 1)
n = Notification.objects.get()
self.assertEqual(n.error, "Connection timed out")
@patch("hc.api.transports.requests.request")
def test_webhooks_support_variables(self, mock_get):
definition = {
"method_down": "GET",
"url_down": "http://host/$CODE/$STATUS/$TAG1/$TAG2/?name=$NAME",
"body_down": "",
"headers_down": {},
}
self._setup_data(json.dumps(definition))
self.check.name = "Hello World"
self.check.tags = "foo bar"
self.check.save()
self.channel.notify(self.check)
url = "http://host/%s/down/foo/bar/?name=Hello%%20World" % self.check.code
args, kwargs = mock_get.call_args
self.assertEqual(args[0], "get")
self.assertEqual(args[1], url)
self.assertEqual(kwargs["headers"], {"User-Agent": "healthchecks.io"})
self.assertEqual(kwargs["timeout"], 5)
@patch("hc.api.transports.requests.request")
def test_webhooks_handle_variable_variables(self, mock_get):
definition = {
"method_down": "GET",
"url_down": "http://host/$$NAMETAG1",
"body_down": "",
"headers_down": {},
}
self._setup_data(json.dumps(definition))
self.check.tags = "foo bar"
self.check.save()
self.channel.notify(self.check)
# $$NAMETAG1 should *not* get transformed to "foo"
args, kwargs = mock_get.call_args
self.assertEqual(args[1], "http://host/$TAG1")
@patch("hc.api.transports.requests.request")
def test_webhooks_support_post(self, mock_request):
definition = {
"method_down": "POST",
"url_down": "http://example.com",
"body_down": "The Time Is $NOW",
"headers_down": {},
}
self._setup_data(json.dumps(definition))
self.check.save()
self.channel.notify(self.check)
args, kwargs = mock_request.call_args
self.assertEqual(args[0], "post")
self.assertEqual(args[1], "http://example.com")
# spaces should not have been urlencoded:
payload = kwargs["data"].decode()
self.assertTrue(payload.startswith("The Time Is 2"))
@patch("hc.api.transports.requests.request")
def test_webhooks_dollarsign_escaping(self, mock_get):
# If name or tag contains what looks like a variable reference,
# that should be left alone:
definition = {
"method_down": "GET",
"url_down": "http://host/$NAME",
"body_down": "",
"headers_down": {},
}
self._setup_data(json.dumps(definition))
self.check.name = "$TAG1"
self.check.tags = "foo"
self.check.save()
self.channel.notify(self.check)
url = "http://host/%24TAG1"
mock_get.assert_called_with(
"get", url, headers={"User-Agent": "healthchecks.io"}, timeout=5
)
@patch("hc.api.transports.requests.request")
def test_webhooks_handle_up_events(self, mock_get):
definition = {
"method_up": "GET",
"url_up": "http://bar",
"body_up": "",
"headers_up": {},
}
self._setup_data(json.dumps(definition), status="up")
self.channel.notify(self.check)
mock_get.assert_called_with(
"get", "http://bar", headers={"User-Agent": "healthchecks.io"}, timeout=5
)
@patch("hc.api.transports.requests.request")
def test_webhooks_handle_noop_up_events(self, mock_get):
definition = {
"method_up": "GET",
"url_up": "",
"body_up": "",
"headers_up": {},
}
self._setup_data(json.dumps(definition), status="up")
self.channel.notify(self.check)
self.assertFalse(mock_get.called)
self.assertEqual(Notification.objects.count(), 0)
@patch("hc.api.transports.requests.request")
def test_webhooks_handle_unicode_post_body(self, mock_request):
definition = {
"method_down": "POST",
"url_down": "http://foo.com",
"body_down": "(╯°□°)╯︵ ┻━┻",
"headers_down": {},
}
self._setup_data(json.dumps(definition))
self.check.save()
self.channel.notify(self.check)
args, kwargs = mock_request.call_args
# unicode should be encoded into utf-8
self.assertIsInstance(kwargs["data"], bytes)
@patch("hc.api.transports.requests.request")
def test_webhooks_handle_post_headers(self, mock_request):
definition = {
"method_down": "POST",
"url_down": "http://foo.com",
"body_down": "data",
"headers_down": {"Content-Type": "application/json"},
}
self._setup_data(json.dumps(definition))
self.channel.notify(self.check)
headers = {"User-Agent": "healthchecks.io", "Content-Type": "application/json"}
mock_request.assert_called_with(
"post", "http://foo.com", data=b"data", headers=headers, timeout=5
)
@patch("hc.api.transports.requests.request")
def test_webhooks_handle_get_headers(self, mock_request):
definition = {
"method_down": "GET",
"url_down": "http://foo.com",
"body_down": "",
"headers_down": {"Content-Type": "application/json"},
}
self._setup_data(json.dumps(definition))
self.channel.notify(self.check)
headers = {"User-Agent": "healthchecks.io", "Content-Type": "application/json"}
mock_request.assert_called_with(
"get", "http://foo.com", headers=headers, timeout=5
)
@patch("hc.api.transports.requests.request")
def test_webhooks_allow_user_agent_override(self, mock_request):
definition = {
"method_down": "GET",
"url_down": "http://foo.com",
"body_down": "",
"headers_down": {"User-Agent": "My-Agent"},
}
self._setup_data(json.dumps(definition))
self.channel.notify(self.check)
headers = {"User-Agent": "My-Agent"}
mock_request.assert_called_with(
"get", "http://foo.com", headers=headers, timeout=5
)
@patch("hc.api.transports.requests.request")
def test_webhooks_support_variables_in_headers(self, mock_request):
definition = {
"method_down": "GET",
"url_down": "http://foo.com",
"body_down": "",
"headers_down": {"X-Message": "$NAME is DOWN"},
}
self._setup_data(json.dumps(definition))
self.check.name = "Foo"
self.check.save()
self.channel.notify(self.check)
headers = {"User-Agent": "healthchecks.io", "X-Message": "Foo is DOWN"}
mock_request.assert_called_with(
"get", "http://foo.com", headers=headers, timeout=5
)
@override_settings(WEBHOOKS_ENABLED=False)
def test_it_requires_webhooks_enabled(self):
definition = {
"method_down": "GET",
"url_down": "http://example",
"body_down": "",
"headers_down": {},
}
self._setup_data(json.dumps(definition))
self.channel.notify(self.check)
n = Notification.objects.get()
self.assertEqual(n.error, "Webhook notifications are not enabled.")

+ 3
- 0
hc/api/transports.py View File

@ -232,6 +232,9 @@ class Webhook(HttpTransport):
return False return False
def notify(self, check): def notify(self, check):
if not settings.WEBHOOKS_ENABLED:
return "Webhook notifications are not enabled."
spec = self.channel.webhook_spec(check.status) spec = self.channel.webhook_spec(check.status)
if not spec["url"]: if not spec["url"]:
return "Empty webhook URL" return "Empty webhook URL"


+ 7
- 0
hc/front/tests/test_add_webhook.py View File

@ -1,3 +1,4 @@
from django.test.utils import override_settings
from hc.api.models import Channel from hc.api.models import Channel
from hc.test import BaseTestCase from hc.test import BaseTestCase
@ -185,3 +186,9 @@ class AddWebhookTestCase(BaseTestCase):
self.client.login(username="[email protected]", password="password") self.client.login(username="[email protected]", password="password")
r = self.client.get(self.url) r = self.client.get(self.url)
self.assertEqual(r.status_code, 403) self.assertEqual(r.status_code, 403)
@override_settings(WEBHOOKS_ENABLED=False)
def test_it_handles_disabled_integration(self):
self.client.login(username="[email protected]", password="password")
r = self.client.get(self.url)
self.assertEqual(r.status_code, 404)

+ 3
- 0
hc/front/views.py View File

@ -304,6 +304,7 @@ def index(request):
"enable_sms": settings.TWILIO_AUTH is not None, "enable_sms": settings.TWILIO_AUTH is not None,
"enable_telegram": settings.TELEGRAM_TOKEN is not None, "enable_telegram": settings.TELEGRAM_TOKEN is not None,
"enable_trello": settings.TRELLO_APP_KEY is not None, "enable_trello": settings.TRELLO_APP_KEY is not None,
"enable_webhooks": settings.WEBHOOKS_ENABLED is True,
"enable_whatsapp": settings.TWILIO_USE_WHATSAPP, "enable_whatsapp": settings.TWILIO_USE_WHATSAPP,
"registration_open": settings.REGISTRATION_OPEN, "registration_open": settings.REGISTRATION_OPEN,
} }
@ -770,6 +771,7 @@ def channels(request, code):
"enable_sms": settings.TWILIO_AUTH is not None, "enable_sms": settings.TWILIO_AUTH is not None,
"enable_telegram": settings.TELEGRAM_TOKEN is not None, "enable_telegram": settings.TELEGRAM_TOKEN is not None,
"enable_trello": settings.TRELLO_APP_KEY is not None, "enable_trello": settings.TRELLO_APP_KEY is not None,
"enable_webhooks": settings.WEBHOOKS_ENABLED is True,
"enable_whatsapp": settings.TWILIO_USE_WHATSAPP, "enable_whatsapp": settings.TWILIO_USE_WHATSAPP,
"use_payments": settings.USE_PAYMENTS, "use_payments": settings.USE_PAYMENTS,
} }
@ -931,6 +933,7 @@ def add_email(request, code):
return render(request, "integrations/add_email.html", ctx) return render(request, "integrations/add_email.html", ctx)
@require_setting("WEBHOOKS_ENABLED")
@login_required @login_required
def add_webhook(request, code): def add_webhook(request, code):
project = _get_rw_project_for_user(request, code) project = _get_rw_project_for_user(request, code)


+ 3
- 0
hc/settings.py View File

@ -235,6 +235,9 @@ TWILIO_USE_WHATSAPP = envbool("TWILIO_USE_WHATSAPP", "False")
# Trello # Trello
TRELLO_APP_KEY = os.getenv("TRELLO_APP_KEY") TRELLO_APP_KEY = os.getenv("TRELLO_APP_KEY")
# Webhooks
WEBHOOKS_ENABLED = envbool("WEBHOOKS_ENABLED", "True")
# Read additional configuration from hc/local_settings.py if it exists # Read additional configuration from hc/local_settings.py if it exists
if os.path.exists(os.path.join(BASE_DIR, "hc/local_settings.py")): if os.path.exists(os.path.join(BASE_DIR, "hc/local_settings.py")):
from .local_settings import * from .local_settings import *

+ 4
- 1
templates/docs/self_hosted_configuration.html View File

@ -321,4 +321,7 @@ scheme.</p>
<p>Default: <code>False</code></p> <p>Default: <code>False</code></p>
<h2 id="USE_PAYMENTS"><code>USE_PAYMENTS</code></h2> <h2 id="USE_PAYMENTS"><code>USE_PAYMENTS</code></h2>
<p>Default: <code>False</code></p> <p>Default: <code>False</code></p>
<p>A boolean that turns on/off billing features.</p>
<p>A boolean that turns on/off billing features.</p>
<h2 id="WEBHOOKS_ENABLED"><code>WEBHOOKS_ENABLED</code></h2>
<p>Default: <code>True</code></p>
<p>A boolean that turns on/off the Webhooks integration. Enabled by default.</p>

+ 7
- 1
templates/docs/self_hosted_configuration.md View File

@ -508,4 +508,10 @@ Default: `False`
Default: `False` Default: `False`
A boolean that turns on/off billing features.
A boolean that turns on/off billing features.
## `WEBHOOKS_ENABLED` {: #WEBHOOKS_ENABLED }
Default: `True`
A boolean that turns on/off the Webhooks integration. Enabled by default.

+ 2
- 0
templates/front/channels.html View File

@ -207,6 +207,7 @@
<a href="{% url 'hc-add-email' project.code %}" class="btn btn-primary">Add Integration</a> <a href="{% url 'hc-add-email' project.code %}" class="btn btn-primary">Add Integration</a>
</li> </li>
{% if enable_webhooks %}
<li> <li>
<img src="{% static 'img/integrations/webhook.png' %}" <img src="{% static 'img/integrations/webhook.png' %}"
class="icon" alt="Webhook icon" /> class="icon" alt="Webhook icon" />
@ -215,6 +216,7 @@
<p>Receive an HTTP callback when a check goes down.</p> <p>Receive an HTTP callback when a check goes down.</p>
<a href="{% url 'hc-add-webhook' project.code %}" class="btn btn-primary">Add Integration</a> <a href="{% url 'hc-add-webhook' project.code %}" class="btn btn-primary">Add Integration</a>
</li> </li>
{% endif %}
{% if enable_apprise %} {% if enable_apprise %}
<li> <li>


+ 2
- 0
templates/front/welcome.html View File

@ -381,6 +381,7 @@
</div> </div>
</div> </div>
{% if enable_webhooks %}
<div class="col-lg-2 col-md-3 col-sm-4 col-xs-6"> <div class="col-lg-2 col-md-3 col-sm-4 col-xs-6">
<div class="integration"> <div class="integration">
<img src="{% static 'img/integrations/webhook.png' %}" class="icon" alt="" /> <img src="{% static 'img/integrations/webhook.png' %}" class="icon" alt="" />
@ -390,6 +391,7 @@
</h3> </h3>
</div> </div>
</div> </div>
{% endif %}
<div class="col-lg-2 col-md-3 col-sm-4 col-xs-6"> <div class="col-lg-2 col-md-3 col-sm-4 col-xs-6">
{% if enable_slack_btn %} {% if enable_slack_btn %}


Loading…
Cancel
Save