Source code for smarter.apps.dashboard.views.terminal_emulator.reactapp

# pylint: disable=W0613
"""
Backend for the terminal emulator view in the dashboard logs app.

This module provides the Django view that renders a browser-based terminal
emulation window styled after macOS Terminal.app. The terminal is backed by a
React component and is used in the web console to give users direct access to
Linux command-line tools such as ``curl`` without leaving the browser.

The rendered page connects to a WebSocket endpoint that streams log data and
accepts command input, with authentication enforced via Django's session and
CSRF mechanisms.

Classes:
    TerminalEmulatorLogView: Authenticated view that renders the React-based
        terminal emulator page.

Example:
    Wire up the view in your URL configuration::

        from smarter.apps.dashboard.views.terminal_emulator.reactapp import TerminalEmulatorLogView

        urlpatterns = [
            path("terminal/", TerminalEmulatorLogView.as_view(), name="smarter-terminal-emulator"),
        ]
"""

from django.conf import settings
from django.http import HttpRequest, HttpResponse
from django.shortcuts import render

from smarter.lib import logging
from smarter.lib.django.shortcuts import reverse
from smarter.lib.django.views import (
    SmarterAuthenticatedNeverCachedWebView,
)

logger = logging.getLogger(__name__)


# pylint: disable=C0415
[docs] class TerminalEmulatorLogView(SmarterAuthenticatedNeverCachedWebView): """ Authenticated view that renders the React-based terminal emulator page. Extends :class:`~smarter.lib.django.views.SmarterAuthenticatedNeverCachedWebView` to ensure that only authenticated users can access the terminal and that responses are never served from cache. On a ``GET`` request the view builds a context dictionary containing the WebSocket API URL and the relevant cookie/session names required by the React frontend, then renders ``react/terminal-emulator.html``. Attributes: template_path (str): Set at request time to ``"react/terminal-emulator.html"``. Context keys passed to the template: .. code-block:: python { "terminal": { "root_id": str, # DOM element id for the React root "csrf_cookie_name": str, # Django CSRF cookie name "django_session_cookie_name": str,# Django session cookie name "cookie_domain": str, # Session cookie domain "api_url": str, # WebSocket endpoint URL } } """ @property def formatted_class_name(self) -> str: """Returns a formatted string of the class name for logging purposes.""" class_name = f"{__name__}.{TerminalEmulatorLogView.__name__}[{id(self)}]" return self.formatted_text(class_name)
[docs] def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: """ Render the terminal emulator page for the authenticated user. Builds a context dictionary that wires the React frontend to the correct WebSocket API endpoint and supplies the cookie/session names needed for CSRF and session authentication. The context is then passed to ``react/terminal-emulator.html``. :param request: The incoming HTTP GET request from the client. :type request: django.http.HttpRequest :param args: Additional positional arguments forwarded by the URL dispatcher. :param kwargs: Additional keyword arguments forwarded by the URL dispatcher. :returns: An HTTP 200 response rendering ``react/terminal-emulator.html`` with the terminal context dictionary. :rtype: django.http.HttpResponse """ from smarter.apps.dashboard.views.terminal_emulator.api.urls import ( DashboardLogsApiReverseNames, ) from smarter.apps.dashboard.views.views.urls import DashboardReverseNames from .names import DashboardLogsReverseNames context = { "terminal": { "root_id": "smarter-terminal-emulator-root", "csrf_cookie_name": settings.CSRF_COOKIE_NAME, # this is the CSRF token cookie that should be included in the # header of the POST request from the frontend. "django_session_cookie_name": settings.SESSION_COOKIE_NAME, # this is the Django session. "cookie_domain": settings.SESSION_COOKIE_DOMAIN, "api_url": reverse( DashboardReverseNames.namespace, DashboardLogsReverseNames.namespace, DashboardLogsApiReverseNames.namespace, DashboardLogsApiReverseNames.stream, ), # the WebSocket endpoint with the log data stream. } } self.template_path = "react/terminal-emulator.html" logger.debug( "%s.get() rendering terminal emulator with context: %s", self.formatted_class_name, logging.formatted_json(context), ) return render(request, self.template_path, context=context)