Ver código fonte

Closes #4332: Redirect to a user-friendly error page when CSS/JS resources fail to load

Jeremy Stretch 6 anos atrás
pai
commit
0e49a7a1fd

+ 1 - 0
docs/release-notes/version-2.7.md

@@ -10,6 +10,7 @@
 * [#4323](https://github.com/netbox-community/netbox/issues/4323) - Add bulk edit view for power panels
 * [#4324](https://github.com/netbox-community/netbox/issues/4324) - Add CSV import view for services
 * [#4325](https://github.com/netbox-community/netbox/issues/4324) - Add CSV import view for rack reservations
+* [#4332](https://github.com/netbox-community/netbox/issues/4332) - Redirect to a user-friendly error page when CSS/JS resources fail to load
 
 ### Bug Fixes
 

+ 4 - 1
netbox/netbox/urls.py

@@ -5,7 +5,7 @@ from django.views.static import serve
 from drf_yasg import openapi
 from drf_yasg.views import get_schema_view
 
-from netbox.views import APIRootView, HomeView, SearchView
+from netbox.views import APIRootView, HomeView, StaticMediaFailureView, SearchView
 from users.views import LoginView, LogoutView
 from .admin import admin_site
 
@@ -63,6 +63,9 @@ _patterns = [
     path('admin/', admin_site.urls),
     path('admin/webhook-backend-status/', include('django_rq.urls')),
 
+    # Errors
+    path('media-failure/', StaticMediaFailureView.as_view(), name='media_failure'),
+
 ]
 
 if settings.DEBUG:

+ 10 - 0
netbox/netbox/views.py

@@ -300,6 +300,16 @@ class SearchView(View):
         })
 
 
+class StaticMediaFailureView(View):
+    """
+    Display a user-friendly error message with troubleshooting tips when a static media file fails to load.
+    """
+    def get(self, request):
+        return render(request, 'media_failure.html', {
+            'filename': request.GET.get('filename')
+        })
+
+
 class APIRootView(APIView):
     _ignore_model_permissions = True
     exclude_from_schema = True

+ 35 - 14
netbox/templates/_base.html

@@ -4,13 +4,27 @@
 <html lang="en">
 <head>
     <title>{% block title %}Home{% endblock %} - NetBox</title>
-    <link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
-    <link rel="stylesheet" href="{% static 'font-awesome-4.7.0/css/font-awesome.min.css' %}">
-    <link rel="stylesheet" href="{% static 'jquery-ui-1.12.1/jquery-ui.css' %}">
-    <link rel="stylesheet" href="{% static 'select2-4.0.12/dist/css/select2.min.css' %}">
-    <link rel="stylesheet" href="{% static 'select2-bootstrap-0.1.0-beta.10/select2-bootstrap.min.css' %}">
-    <link rel="stylesheet" href="{% static 'flatpickr-4.6.3/themes/light.css' %}">
-    <link rel="stylesheet" href="{% static 'css/base.css' %}?v{{ settings.VERSION }}">
+    <link rel="stylesheet"
+          href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}"
+          onerror="window.location='{% url 'media_failure' %}?filename=bootstrap-3.4.1-dist/css/bootstrap.min.css'">
+    <link rel="stylesheet"
+          href="{% static 'font-awesome-4.7.0/css/font-awesome.min.css' %}"
+          onerror="window.location='{% url 'media_failure' %}?filename=font-awesome-4.7.0/css/font-awesome.min.css'">
+    <link rel="stylesheet"
+          href="{% static 'jquery-ui-1.12.1/jquery-ui.css' %}"
+          onerror="window.location='{% url 'media_failure' %}?filename=jquery-ui-1.12.1/jquery-ui.css'">
+    <link rel="stylesheet"
+          href="{% static 'select2-4.0.12/dist/css/select2.min.css' %}"
+          onerror="window.location='{% url 'media_failure' %}?filename=select2-4.0.12/dist/css/select2.min.css'">
+    <link rel="stylesheet"
+          href="{% static 'select2-bootstrap-0.1.0-beta.10/select2-bootstrap.min.css' %}"
+          onerror="window.location='{% url 'media_failure' %}?filename=select2-bootstrap-0.1.0-beta.10/select2-bootstrap.min.css'">
+    <link rel="stylesheet"
+          href="{% static 'flatpickr-4.6.3/themes/light.css' %}"
+          onerror="window.location='{% url 'media_failure' %}?filename=flatpickr-4.6.3/themes/light.css'">
+    <link rel="stylesheet"
+          href="{% static 'css/base.css' %}?v{{ settings.VERSION }}"
+          onerror="window.location='{% url 'media_failure' %}?filename=css/base.css'">
     <link rel="icon" type="image/png" href="{% static 'img/netbox.ico' %}" />
     <meta charset="UTF-8">
     <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
@@ -66,13 +80,20 @@
             </div>
         </div>
     </footer>
-<script src="{% static 'jquery/jquery-3.4.1.min.js' %}"></script>
-<script src="{% static 'jquery-ui-1.12.1/jquery-ui.min.js' %}"></script>
-<script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
-<script src="{% static 'select2-4.0.12/dist/js/select2.min.js' %}"></script>
-<script src="{% static 'clipboard.js/clipboard-2.0.4.min.js' %}"></script>
-<script src="{% static 'flatpickr-4.6.3/flatpickr.min.js' %}"></script>
-<script src="{% static 'js/forms.js' %}?v{{ settings.VERSION }}"></script>
+<script src="{% static 'jquery/jquery-3.4.1.min.js' %}"
+        onerror="window.location='{% url 'media_failure' %}?filename=jquery/jquery-3.4.1.min.js'"></script>
+<script src="{% static 'jquery-ui-1.12.1/jquery-ui.min.js' %}"
+        onerror="window.location='{% url 'media_failure' %}?filename=jquery-ui-1.12.1/jquery-ui.min.js'"></script>
+<script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"
+        onerror="window.location='{% url 'media_failure' %}?filename=bootstrap-3.4.1-dist/js/bootstrap.min.js'"></script>
+<script src="{% static 'select2-4.0.12/dist/js/select2.min.js' %}"
+        onerror="window.location='{% url 'media_failure' %}?filename=select2-4.0.12/dist/js/select2.min.js'"></script>
+<script src="{% static 'clipboard.js/clipboard-2.0.4.min.js' %}"
+        onerror="window.location='{% url 'media_failure' %}?filename=clipboard.js/clipboard-2.0.4.min.js'"></script>
+<script src="{% static 'flatpickr-4.6.3/flatpickr.min.js' %}"
+        onerror="window.location='{% url 'media_failure' %}?filename=flatpickr-4.6.3/flatpickr.min.js'"></script>
+<script src="{% static 'js/forms.js' %}?v{{ settings.VERSION }}"
+        onerror="window.location='{% url 'media_failure' %}?filename=js/forms.js'"></script>
 <script type="text/javascript">
     var netbox_api_path = "/{{ settings.BASE_PATH }}api/";
     var loading = $(".loading");

+ 48 - 0
netbox/templates/media_failure.html

@@ -0,0 +1,48 @@
+{% load static %}
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <title>Static Media Failure - NetBox</title>
+    <meta charset="UTF-8">
+    <style type="text/css">
+        body {
+            font-family: sans-serif;
+        }
+        li.tip {
+            line-height: 150%;
+            margin-bottom: 30px;
+        }
+    </style>
+</head>
+<body>
+    <div style="margin: auto; width: 800px">
+        <h1>Static Media Failure</h1>
+        <h3>
+            The following static media file failed to load:
+            <a href="{% static filename %}"><code style="color: red">{{ filename }}</code></a>
+        </h3>
+        <p>Check the following:</p>
+        <ul>
+            <li class="tip">
+                <code><strong>manage.py collectstatic</strong></code> was run during the most recent upgrade. This installs the most recent
+                iteration of each static file into the static root path.
+            </li>
+            <li class="tip">
+                The HTTP service (e.g. nginx or Apache) is configured to serve files from the <code>STATIC_ROOT</code> path.
+                Refer to <a href="https://netbox.readthedocs.io/en/stable/installation/">the installation
+                documentation</a> for further guidance.
+                    <ul>
+                        {% if request.user.is_staff or request.user.is_superuser %}
+                            <li><code>STATIC_ROOT: <strong>{{ settings.STATIC_ROOT }}</strong></code></li>
+                        {% endif %}
+                        <li><code>STATIC_URL: <strong>{{ settings.STATIC_URL }}</strong></code></li>
+                    </ul>
+            </li>
+            <li class="tip">
+                The file <code>{{ filename }}</code> exists in the static root directory and is readable by the HTTP process.
+            </li>
+        </ul>
+        <p>Click <a href="/">here</a> to attempt loading NetBox again.</p>
+    </div>
+</body>
+</html>