فهرست منبع

Merge branch 'develop' into feature

jeremystretch 3 سال پیش
والد
کامیت
e208404e3a

+ 1 - 1
.github/ISSUE_TEMPLATE/bug_report.yaml

@@ -14,7 +14,7 @@ body:
     attributes:
     attributes:
       label: NetBox version
       label: NetBox version
       description: What version of NetBox are you currently running?
       description: What version of NetBox are you currently running?
-      placeholder: v3.2.2
+      placeholder: v3.2.3
     validations:
     validations:
       required: true
       required: true
   - type: dropdown
   - type: dropdown

+ 1 - 1
.github/ISSUE_TEMPLATE/feature_request.yaml

@@ -14,7 +14,7 @@ body:
     attributes:
     attributes:
       label: NetBox version
       label: NetBox version
       description: What version of NetBox are you currently running?
       description: What version of NetBox are you currently running?
-      placeholder: v3.2.2
+      placeholder: v3.2.3
     validations:
     validations:
       required: true
       required: true
   - type: dropdown
   - type: dropdown

+ 2 - 0
README.md

@@ -60,6 +60,8 @@ The complete documentation for NetBox can be found at [docs.netbox.dev](https://
             
             
   [![NS1](https://raw.githubusercontent.com/wiki/netbox-community/netbox/images/sponsors/ns1.png)](https://ns1.com/)
   [![NS1](https://raw.githubusercontent.com/wiki/netbox-community/netbox/images/sponsors/ns1.png)](https://ns1.com/)
   <br />
   <br />
+  [![Sentry](https://raw.githubusercontent.com/wiki/netbox-community/netbox/images/sponsors/sentry.png)](https://sentry.io/)
+  &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
   [![Stellar Technologies](https://raw.githubusercontent.com/wiki/netbox-community/netbox/images/sponsors/stellar.png)](https://stellar.tech/)
   [![Stellar Technologies](https://raw.githubusercontent.com/wiki/netbox-community/netbox/images/sponsors/stellar.png)](https://stellar.tech/)
 
 
 </div>
 </div>

+ 4 - 0
base_requirements.txt

@@ -102,6 +102,10 @@ psycopg2-binary
 # https://github.com/yaml/pyyaml
 # https://github.com/yaml/pyyaml
 PyYAML
 PyYAML
 
 
+# Sentry SDK
+# https://github.com/getsentry/sentry-python
+sentry-sdk
+
 # Social authentication framework
 # Social authentication framework
 # https://github.com/python-social-auth/social-core
 # https://github.com/python-social-auth/social-core
 social-auth-core
 social-auth-core

+ 46 - 0
docs/administration/error-reporting.md

@@ -0,0 +1,46 @@
+# Error Reporting
+
+## Sentry
+
+### Enabling Error Reporting
+
+NetBox v3.2.3 and later support native integration with [Sentry](https://sentry.io/) for automatic error reporting. To enable this functionality, simply set `SENTRY_ENABLED` to True in `configuration.py`. Errors will be sent to a Sentry ingestor maintained by the NetBox team for analysis.
+
+```python
+SENTRY_ENABLED = True
+```
+
+### Using a Custom DSN
+
+If you prefer instead to use your own Sentry ingestor, you'll need to first create a new project under your Sentry account to represent your NetBox deployment and obtain its corresponding data source name (DSN). This looks like a URL similar to the example below:
+
+```
+https://examplePublicKey@o0.ingest.sentry.io/0
+```
+
+Once you have obtained a DSN, configure Sentry in NetBox's `configuration.py` file with the following parameters:
+
+```python
+SENTRY_ENABLED = True
+SENTRY_DSN = "https://examplePublicKey@o0.ingest.sentry.io/0"
+```
+
+### Assigning Tags
+
+You can optionally attach one or more arbitrary tags to the outgoing error reports if desired by setting the `SENTRY_TAGS` parameter:
+
+```python
+SENTRY_TAGS = {
+    "custom.foo": "123",
+    "custom.bar": "abc",
+}
+```
+
+!!! warning "Reserved tag prefixes"
+    Avoid using any tag names which begin with `netbox.`, as this prefix is reserved by the NetBox application.
+
+### Testing
+
+Once the configuration has been saved, restart the NetBox service.
+
+To test Sentry operation, try generating a 404 (page not found) error by navigating to an invalid URL, such as `https://netbox/404-error-testing`. (Be sure that debug mode has been disabled.) After receiving a 404 response from the NetBox server, you should see the issue appear shortly in Sentry.

+ 54 - 0
docs/configuration/error-reporting.md

@@ -0,0 +1,54 @@
+# Error Reporting Settings
+
+## SENTRY_DSN
+
+Default: None
+
+Defines a Sentry data source name (DSN) for automated error reporting. `SENTRY_ENABLED` must be True for this parameter to take effect. For example:
+
+```
+SENTRY_DSN = "https://examplePublicKey@o0.ingest.sentry.io/0"
+```
+
+---
+
+## SENTRY_ENABLED
+
+Default: False
+
+Set to True to enable automatic error reporting via [Sentry](https://sentry.io/).
+
+---
+
+## SENTRY_SAMPLE_RATE
+
+Default: 1.0 (all)
+
+The sampling rate for errors. Must be a value between 0 (disabled) and 1.0 (report on all errors).
+
+---
+
+## SENTRY_TAGS
+
+An optional dictionary of tag names and values to apply to Sentry error reports.For example:
+
+```
+SENTRY_TAGS = {
+    "custom.foo": "123",
+    "custom.bar": "abc",
+}
+```
+
+!!! warning "Reserved tag prefixes"
+    Avoid using any tag names which begin with `netbox.`, as this prefix is reserved by the NetBox application.
+
+---
+
+## SENTRY_TRACES_SAMPLE_RATE
+
+Default: 0 (disabled)
+
+The sampling rate for transactions. Must be a value between 0 (disabled) and 1.0 (report on all transactions).
+
+!!! warning "Consider performance implications"
+    A high sampling rate for transactions can induce significant performance penalties. If transaction reporting is desired, it is recommended to use a relatively low sample rate of 10% to 20% (0.1 to 0.2).

+ 10 - 1
docs/release-notes/version-3.2.md

@@ -1,21 +1,30 @@
 # NetBox v3.2
 # NetBox v3.2
 
 
-## v3.2.3 (FUTURE)
+## v3.2.4 (FUTURE)
+
+---
+
+## v3.2.3 (2022-05-12)
 
 
 ### Enhancements
 ### Enhancements
 
 
+* [#8805](https://github.com/netbox-community/netbox/issues/8805) - Add "mixed" option for device airflow indication
 * [#8894](https://github.com/netbox-community/netbox/issues/8894) - Include full names when listing users
 * [#8894](https://github.com/netbox-community/netbox/issues/8894) - Include full names when listing users
 * [#8998](https://github.com/netbox-community/netbox/issues/8998) - Enable filtering racks & reservations by site group
 * [#8998](https://github.com/netbox-community/netbox/issues/8998) - Enable filtering racks & reservations by site group
 * [#9122](https://github.com/netbox-community/netbox/issues/9122) - Introduce `clearcache` management command & clear cache during upgrade
 * [#9122](https://github.com/netbox-community/netbox/issues/9122) - Introduce `clearcache` management command & clear cache during upgrade
+* [#9221](https://github.com/netbox-community/netbox/issues/9221) - Add definition list support for Markdown
 * [#9260](https://github.com/netbox-community/netbox/issues/9260) - Apply user preferences to tables under object detail views
 * [#9260](https://github.com/netbox-community/netbox/issues/9260) - Apply user preferences to tables under object detail views
 * [#9278](https://github.com/netbox-community/netbox/issues/9278) - Linkify device types count under manufacturers list
 * [#9278](https://github.com/netbox-community/netbox/issues/9278) - Linkify device types count under manufacturers list
 * [#9280](https://github.com/netbox-community/netbox/issues/9280) - Allow adopting existing components when installing a module
 * [#9280](https://github.com/netbox-community/netbox/issues/9280) - Allow adopting existing components when installing a module
 * [#9314](https://github.com/netbox-community/netbox/issues/9314) - Add device and VM filters for FHRP group assignments
 * [#9314](https://github.com/netbox-community/netbox/issues/9314) - Add device and VM filters for FHRP group assignments
+* [#9340](https://github.com/netbox-community/netbox/issues/9340) - Introduce support for error reporting via Sentry
+* [#9343](https://github.com/netbox-community/netbox/issues/9343) - Add Ubiquiti SmartPower power outlet type
 
 
 ### Bug Fixes
 ### Bug Fixes
 
 
 * [#9190](https://github.com/netbox-community/netbox/issues/9190) - Prevent exception when attempting to instantiate module components which already exist on the parent device
 * [#9190](https://github.com/netbox-community/netbox/issues/9190) - Prevent exception when attempting to instantiate module components which already exist on the parent device
 * [#9267](https://github.com/netbox-community/netbox/issues/9267) - Remove invalid entry in IP address role choices
 * [#9267](https://github.com/netbox-community/netbox/issues/9267) - Remove invalid entry in IP address role choices
+* [#9296](https://github.com/netbox-community/netbox/issues/9296) - Improve Markdown link sanitization
 * [#9306](https://github.com/netbox-community/netbox/issues/9306) - Include VC master interfaces when selecting a LAG/bridge for a VC member interface
 * [#9306](https://github.com/netbox-community/netbox/issues/9306) - Include VC master interfaces when selecting a LAG/bridge for a VC member interface
 * [#9311](https://github.com/netbox-community/netbox/issues/9311) - Permit creating contact assignment without a priority via the REST API
 * [#9311](https://github.com/netbox-community/netbox/issues/9311) - Permit creating contact assignment without a priority via the REST API
 * [#9313](https://github.com/netbox-community/netbox/issues/9313) - Remove HTML code from CSV output of many-to-many relationships
 * [#9313](https://github.com/netbox-community/netbox/issues/9313) - Remove HTML code from CSV output of many-to-many relationships

+ 2 - 0
mkdocs.yml

@@ -73,6 +73,7 @@ nav:
         - Required Settings: 'configuration/required-settings.md'
         - Required Settings: 'configuration/required-settings.md'
         - Optional Settings: 'configuration/optional-settings.md'
         - Optional Settings: 'configuration/optional-settings.md'
         - Dynamic Settings: 'configuration/dynamic-settings.md'
         - Dynamic Settings: 'configuration/dynamic-settings.md'
+        - Error Reporting: 'configuration/error-reporting.md'
         - Remote Authentication: 'configuration/remote-authentication.md'
         - Remote Authentication: 'configuration/remote-authentication.md'
     - Core Functionality:
     - Core Functionality:
         - IP Address Management: 'core-functionality/ipam.md'
         - IP Address Management: 'core-functionality/ipam.md'
@@ -123,6 +124,7 @@ nav:
             - Microsoft Azure AD: 'administration/authentication/microsoft-azure-ad.md'
             - Microsoft Azure AD: 'administration/authentication/microsoft-azure-ad.md'
             - Okta: 'administration/authentication/okta.md'
             - Okta: 'administration/authentication/okta.md'
         - Permissions: 'administration/permissions.md'
         - Permissions: 'administration/permissions.md'
+        - Error Reporting: 'administration/error-reporting.md'
         - Housekeeping: 'administration/housekeeping.md'
         - Housekeeping: 'administration/housekeeping.md'
         - Replicating NetBox: 'administration/replicating-netbox.md'
         - Replicating NetBox: 'administration/replicating-netbox.md'
         - NetBox Shell: 'administration/netbox-shell.md'
         - NetBox Shell: 'administration/netbox-shell.md'

+ 4 - 0
netbox/dcim/choices.py

@@ -159,6 +159,7 @@ class DeviceAirflowChoices(ChoiceSet):
     AIRFLOW_RIGHT_TO_LEFT = 'right-to-left'
     AIRFLOW_RIGHT_TO_LEFT = 'right-to-left'
     AIRFLOW_SIDE_TO_REAR = 'side-to-rear'
     AIRFLOW_SIDE_TO_REAR = 'side-to-rear'
     AIRFLOW_PASSIVE = 'passive'
     AIRFLOW_PASSIVE = 'passive'
+    AIRFLOW_MIXED = 'mixed'
 
 
     CHOICES = (
     CHOICES = (
         (AIRFLOW_FRONT_TO_REAR, 'Front to rear'),
         (AIRFLOW_FRONT_TO_REAR, 'Front to rear'),
@@ -167,6 +168,7 @@ class DeviceAirflowChoices(ChoiceSet):
         (AIRFLOW_RIGHT_TO_LEFT, 'Right to left'),
         (AIRFLOW_RIGHT_TO_LEFT, 'Right to left'),
         (AIRFLOW_SIDE_TO_REAR, 'Side to rear'),
         (AIRFLOW_SIDE_TO_REAR, 'Side to rear'),
         (AIRFLOW_PASSIVE, 'Passive'),
         (AIRFLOW_PASSIVE, 'Passive'),
+        (AIRFLOW_MIXED, 'Mixed'),
     )
     )
 
 
 
 
@@ -575,6 +577,7 @@ class PowerOutletTypeChoices(ChoiceSet):
     TYPE_NEUTRIK_POWERCON_32A = 'neutrik-powercon-32a'
     TYPE_NEUTRIK_POWERCON_32A = 'neutrik-powercon-32a'
     TYPE_NEUTRIK_POWERCON_TRUE1 = 'neutrik-powercon-true1'
     TYPE_NEUTRIK_POWERCON_TRUE1 = 'neutrik-powercon-true1'
     TYPE_NEUTRIK_POWERCON_TRUE1_TOP = 'neutrik-powercon-true1-top'
     TYPE_NEUTRIK_POWERCON_TRUE1_TOP = 'neutrik-powercon-true1-top'
+    TYPE_UBIQUITI_SMARTPOWER = 'ubiquiti-smartpower'
     # Other
     # Other
     TYPE_HARDWIRED = 'hardwired'
     TYPE_HARDWIRED = 'hardwired'
 
 
@@ -683,6 +686,7 @@ class PowerOutletTypeChoices(ChoiceSet):
             (TYPE_NEUTRIK_POWERCON_32A, 'Neutrik powerCON (32A)'),
             (TYPE_NEUTRIK_POWERCON_32A, 'Neutrik powerCON (32A)'),
             (TYPE_NEUTRIK_POWERCON_TRUE1, 'Neutrik powerCON TRUE1'),
             (TYPE_NEUTRIK_POWERCON_TRUE1, 'Neutrik powerCON TRUE1'),
             (TYPE_NEUTRIK_POWERCON_TRUE1_TOP, 'Neutrik powerCON TRUE1 TOP'),
             (TYPE_NEUTRIK_POWERCON_TRUE1_TOP, 'Neutrik powerCON TRUE1 TOP'),
+            (TYPE_UBIQUITI_SMARTPOWER, 'Ubiquiti SmartPower'),
         )),
         )),
         ('Other', (
         ('Other', (
             (TYPE_HARDWIRED, 'Hardwired'),
             (TYPE_HARDWIRED, 'Hardwired'),

+ 42 - 0
netbox/netbox/settings.py

@@ -1,3 +1,4 @@
+import hashlib
 import importlib
 import importlib
 import logging
 import logging
 import os
 import os
@@ -8,9 +9,11 @@ import sys
 import warnings
 import warnings
 from urllib.parse import urlsplit
 from urllib.parse import urlsplit
 
 
+import sentry_sdk
 from django.contrib.messages import constants as messages
 from django.contrib.messages import constants as messages
 from django.core.exceptions import ImproperlyConfigured, ValidationError
 from django.core.exceptions import ImproperlyConfigured, ValidationError
 from django.core.validators import URLValidator
 from django.core.validators import URLValidator
+from sentry_sdk.integrations.django import DjangoIntegration
 
 
 from netbox.config import PARAMS
 from netbox.config import PARAMS
 
 
@@ -40,6 +43,7 @@ if sys.version_info < (3, 8):
         f"NetBox requires Python 3.8 or later. (Currently installed: Python {platform.python_version()})"
         f"NetBox requires Python 3.8 or later. (Currently installed: Python {platform.python_version()})"
     )
     )
 
 
+DEFAULT_SENTRY_DSN = 'https://198cf560b29d4054ab8e583a1d10ea58@o1242133.ingest.sentry.io/6396485'
 
 
 #
 #
 # Configuration import
 # Configuration import
@@ -68,6 +72,9 @@ DATABASE = getattr(configuration, 'DATABASE')
 REDIS = getattr(configuration, 'REDIS')
 REDIS = getattr(configuration, 'REDIS')
 SECRET_KEY = getattr(configuration, 'SECRET_KEY')
 SECRET_KEY = getattr(configuration, 'SECRET_KEY')
 
 
+# Calculate a unique deployment ID from the secret key
+DEPLOYMENT_ID = hashlib.sha256(SECRET_KEY.encode('utf-8')).hexdigest()[:16]
+
 # Set static config parameters
 # Set static config parameters
 ADMINS = getattr(configuration, 'ADMINS', [])
 ADMINS = getattr(configuration, 'ADMINS', [])
 AUTH_PASSWORD_VALIDATORS = getattr(configuration, 'AUTH_PASSWORD_VALIDATORS', [])
 AUTH_PASSWORD_VALIDATORS = getattr(configuration, 'AUTH_PASSWORD_VALIDATORS', [])
@@ -113,6 +120,11 @@ REMOTE_AUTH_GROUP_SEPARATOR = getattr(configuration, 'REMOTE_AUTH_GROUP_SEPARATO
 REPORTS_ROOT = getattr(configuration, 'REPORTS_ROOT', os.path.join(BASE_DIR, 'reports')).rstrip('/')
 REPORTS_ROOT = getattr(configuration, 'REPORTS_ROOT', os.path.join(BASE_DIR, 'reports')).rstrip('/')
 RQ_DEFAULT_TIMEOUT = getattr(configuration, 'RQ_DEFAULT_TIMEOUT', 300)
 RQ_DEFAULT_TIMEOUT = getattr(configuration, 'RQ_DEFAULT_TIMEOUT', 300)
 SCRIPTS_ROOT = getattr(configuration, 'SCRIPTS_ROOT', os.path.join(BASE_DIR, 'scripts')).rstrip('/')
 SCRIPTS_ROOT = getattr(configuration, 'SCRIPTS_ROOT', os.path.join(BASE_DIR, 'scripts')).rstrip('/')
+SENTRY_DSN = getattr(configuration, 'SENTRY_DSN', DEFAULT_SENTRY_DSN)
+SENTRY_ENABLED = getattr(configuration, 'SENTRY_ENABLED', False)
+SENTRY_SAMPLE_RATE = getattr(configuration, 'SENTRY_SAMPLE_RATE', 1.0)
+SENTRY_TRACES_SAMPLE_RATE = getattr(configuration, 'SENTRY_TRACES_SAMPLE_RATE', 0)
+SENTRY_TAGS = getattr(configuration, 'SENTRY_TAGS', {})
 SESSION_FILE_PATH = getattr(configuration, 'SESSION_FILE_PATH', None)
 SESSION_FILE_PATH = getattr(configuration, 'SESSION_FILE_PATH', None)
 SESSION_COOKIE_NAME = getattr(configuration, 'SESSION_COOKIE_NAME', 'sessionid')
 SESSION_COOKIE_NAME = getattr(configuration, 'SESSION_COOKIE_NAME', 'sessionid')
 SHORT_DATE_FORMAT = getattr(configuration, 'SHORT_DATE_FORMAT', 'Y-m-d')
 SHORT_DATE_FORMAT = getattr(configuration, 'SHORT_DATE_FORMAT', 'Y-m-d')
@@ -428,6 +440,36 @@ EXEMPT_PATHS = (
 )
 )
 
 
 
 
+#
+# Sentry
+#
+
+if SENTRY_ENABLED:
+    if not SENTRY_DSN:
+        raise ImproperlyConfigured("SENTRY_ENABLED is True but SENTRY_DSN has not been defined.")
+    # If using the default DSN, force sampling rates
+    if SENTRY_DSN == DEFAULT_SENTRY_DSN:
+        SENTRY_SAMPLE_RATE = 1.0
+        SENTRY_TRACES_SAMPLE_RATE = 0
+    # Initialize the SDK
+    sentry_sdk.init(
+        dsn=SENTRY_DSN,
+        release=VERSION,
+        integrations=[DjangoIntegration()],
+        sample_rate=SENTRY_SAMPLE_RATE,
+        traces_sample_rate=SENTRY_TRACES_SAMPLE_RATE,
+        send_default_pii=True,
+        http_proxy=HTTP_PROXIES.get('http') if HTTP_PROXIES else None,
+        https_proxy=HTTP_PROXIES.get('https') if HTTP_PROXIES else None
+    )
+    # Assign any configured tags
+    for k, v in SENTRY_TAGS.items():
+        sentry_sdk.set_tag(k, v)
+    # If using the default DSN, append a unique deployment ID tag for error correlation
+    if SENTRY_DSN == DEFAULT_SENTRY_DSN:
+        sentry_sdk.set_tag('netbox.deployment_id', DEPLOYMENT_ID)
+
+
 #
 #
 # Django social auth
 # Django social auth
 #
 #

+ 1 - 0
netbox/netbox/urls.py

@@ -100,4 +100,5 @@ urlpatterns = [
     path('{}'.format(settings.BASE_PATH), include(_patterns))
     path('{}'.format(settings.BASE_PATH), include(_patterns))
 ]
 ]
 
 
+handler404 = 'netbox.views.handler_404'
 handler500 = 'netbox.views.server_error'
 handler500 = 'netbox.views.server_error'

+ 11 - 3
netbox/netbox/views/__init__.py

@@ -2,7 +2,6 @@ import platform
 import sys
 import sys
 
 
 from django.conf import settings
 from django.conf import settings
-from django.contrib.contenttypes.models import ContentType
 from django.core.cache import cache
 from django.core.cache import cache
 from django.db.models import F
 from django.db.models import F
 from django.http import HttpResponseServerError
 from django.http import HttpResponseServerError
@@ -11,9 +10,10 @@ from django.template import loader
 from django.template.exceptions import TemplateDoesNotExist
 from django.template.exceptions import TemplateDoesNotExist
 from django.urls import reverse
 from django.urls import reverse
 from django.views.decorators.csrf import requires_csrf_token
 from django.views.decorators.csrf import requires_csrf_token
-from django.views.defaults import ERROR_500_TEMPLATE_NAME
+from django.views.defaults import ERROR_500_TEMPLATE_NAME, page_not_found
 from django.views.generic import View
 from django.views.generic import View
 from packaging import version
 from packaging import version
+from sentry_sdk import capture_message
 
 
 from circuits.models import Circuit, Provider
 from circuits.models import Circuit, Provider
 from dcim.models import (
 from dcim.models import (
@@ -190,13 +190,21 @@ class StaticMediaFailureView(View):
     """
     """
     Display a user-friendly error message with troubleshooting tips when a static media file fails to load.
     Display a user-friendly error message with troubleshooting tips when a static media file fails to load.
     """
     """
-
     def get(self, request):
     def get(self, request):
         return render(request, 'media_failure.html', {
         return render(request, 'media_failure.html', {
             'filename': request.GET.get('filename')
             'filename': request.GET.get('filename')
         })
         })
 
 
 
 
+def handler_404(request, exception):
+    """
+    Wrap Django's default 404 handler to enable Sentry reporting.
+    """
+    capture_message("Page not found", level="error")
+
+    return page_not_found(request, exception)
+
+
 @requires_csrf_token
 @requires_csrf_token
 def server_error(request, template_name=ERROR_500_TEMPLATE_NAME):
 def server_error(request, template_name=ERROR_500_TEMPLATE_NAME):
     """
     """

+ 3 - 3
netbox/utilities/templatetags/builtins/filters.py

@@ -150,15 +150,15 @@ def render_markdown(value):
     value = strip_tags(value)
     value = strip_tags(value)
 
 
     # Sanitize Markdown links
     # Sanitize Markdown links
-    pattern = fr'\[([^\]]+)\]\((?!({schemes})).*:(.+)\)'
+    pattern = fr'\[([^\]]+)\]\(\s*(?!({schemes})).*:(.+)\)'
     value = re.sub(pattern, '[\\1](\\3)', value, flags=re.IGNORECASE)
     value = re.sub(pattern, '[\\1](\\3)', value, flags=re.IGNORECASE)
 
 
     # Sanitize Markdown reference links
     # Sanitize Markdown reference links
-    pattern = fr'\[(.+)\]:\s*(?!({schemes}))\w*:(.+)'
+    pattern = fr'\[([^\]]+)\]:\s*(?!({schemes}))\w*:(.+)'
     value = re.sub(pattern, '[\\1]: \\3', value, flags=re.IGNORECASE)
     value = re.sub(pattern, '[\\1]: \\3', value, flags=re.IGNORECASE)
 
 
     # Render Markdown
     # Render Markdown
-    html = markdown(value, extensions=['fenced_code', 'tables', StrikethroughExtension()])
+    html = markdown(value, extensions=['def_list', 'fenced_code', 'tables', StrikethroughExtension()])
 
 
     # If the string is not empty wrap it in rendered-markdown to style tables
     # If the string is not empty wrap it in rendered-markdown to style tables
     if html:
     if html:

+ 4 - 3
requirements.txt

@@ -1,5 +1,5 @@
 Django==4.0.4
 Django==4.0.4
-django-cors-headers==3.11.0
+django-cors-headers==3.12.0
 django-debug-toolbar==3.2.4
 django-debug-toolbar==3.2.4
 django-filter==21.1
 django-filter==21.1
 django-graphiql-debug-toolbar==0.2.0
 django-graphiql-debug-toolbar==0.2.0
@@ -16,14 +16,15 @@ drf-yasg[validation]==1.20.0
 graphene-django==2.15.0
 graphene-django==2.15.0
 gunicorn==20.1.0
 gunicorn==20.1.0
 Jinja2==3.1.2
 Jinja2==3.1.2
-Markdown==3.3.6
+Markdown==3.3.7
 markdown-include==0.6.0
 markdown-include==0.6.0
-mkdocs-material==8.2.11
+mkdocs-material==8.2.14
 mkdocstrings[python-legacy]==0.18.1
 mkdocstrings[python-legacy]==0.18.1
 netaddr==0.8.0
 netaddr==0.8.0
 Pillow==9.1.0
 Pillow==9.1.0
 psycopg2-binary==2.9.3
 psycopg2-binary==2.9.3
 PyYAML==6.0
 PyYAML==6.0
+sentry-sdk==1.5.12
 social-auth-app-django==5.0.0
 social-auth-app-django==5.0.0
 social-auth-core==4.2.0
 social-auth-core==4.2.0
 svgwrite==1.4.2
 svgwrite==1.4.2