defmodule ExESDBDashboard.OperationalLive do @moduledoc """ Advanced Operational Monitoring LiveView for comprehensive system oversight. This component provides real-time monitoring of all operational aspects of the ExESDB cluster, including: - System configuration changes - Performance metrics and trends - Security events and authentication - Audit trail and compliance tracking - Alert management and notifications - Diagnostic information and debugging - Log aggregation and analysis This leverages the enhanced operational messaging patterns from ExESDB core package for consistent node tracking, precise timestamps, and comprehensive operational visibility. """ use Phoenix.LiveView require Logger # Update intervals @refresh_interval 3_000 @metrics_history_limit 100 @log_buffer_limit 200 @alert_history_limit 50 @impl true def mount(_params, _session, socket) do if connected?(socket) do subscribe_to_operational_events() schedule_refresh() end socket = socket |> assign(:page_title, "Operational Monitoring") |> assign(:active_tab, "overview") |> assign(:auto_refresh, true) |> assign(:last_updated, DateTime.utc_now()) |> assign(:operational_overview, get_initial_operational_overview()) |> assign(:performance_metrics, []) |> assign(:security_events, []) |> assign(:audit_trail, []) |> assign(:alerts, []) |> assign(:diagnostics, %{}) |> assign(:logs, []) |> assign(:config_changes, []) |> assign(:filters, %{ log_level: "all", alert_severity: "all", security_type: "all", time_range: "1h" }) {:ok, socket} end @impl true def handle_params(params, _uri, socket) do tab = Map.get(params, "tab", "overview") {:noreply, assign(socket, :active_tab, tab)} end @impl true def handle_event("switch_tab", %{"tab" => tab}, socket) do {:noreply, assign(socket, :active_tab, tab)} end @impl true def handle_event("toggle_auto_refresh", _params, socket) do auto_refresh = !socket.assigns.auto_refresh if auto_refresh and connected?(socket) do schedule_refresh() end {:noreply, assign(socket, :auto_refresh, auto_refresh)} end @impl true def handle_event("manual_refresh", _params, socket) do {:noreply, refresh_operational_data(socket)} end @impl true def handle_event("update_filter", %{"filter" => filter, "value" => value}, socket) do filters = Map.put(socket.assigns.filters, String.to_existing_atom(filter), value) {:noreply, assign(socket, :filters, filters)} end @impl true def handle_event("clear_alerts", _params, socket) do {:noreply, assign(socket, :alerts, [])} end @impl true def handle_event("export_audit", _params, socket) do # This could trigger an audit export in a real implementation {:noreply, put_flash(socket, :info, "Audit trail export requested")} end # Handle operational alert messages @impl true def handle_info({:operational_alert, alert_data}, socket) do updated_alerts = add_to_buffer(socket.assigns.alerts, alert_data, @alert_history_limit) socket = socket |> assign(:alerts, updated_alerts) |> assign(:last_updated, DateTime.utc_now()) |> update_operational_overview() {:noreply, socket} end # Handle operational metrics @impl true def handle_info({:operational_metrics, metrics_data}, socket) do updated_metrics = add_to_buffer(socket.assigns.performance_metrics, metrics_data, @metrics_history_limit) socket = socket |> assign(:performance_metrics, updated_metrics) |> assign(:last_updated, DateTime.utc_now()) |> update_operational_overview() {:noreply, socket} end # Handle security events @impl true def handle_info({:security_event, security_data}, socket) do updated_security = add_to_buffer(socket.assigns.security_events, security_data, 50) socket = socket |> assign(:security_events, updated_security) |> assign(:last_updated, DateTime.utc_now()) |> update_operational_overview() {:noreply, socket} end # Handle audit events @impl true def handle_info({:audit_event, audit_data}, socket) do updated_audit = add_to_buffer(socket.assigns.audit_trail, audit_data, 100) socket = socket |> assign(:audit_trail, updated_audit) |> assign(:last_updated, DateTime.utc_now()) |> update_operational_overview() {:noreply, socket} end # Handle diagnostic events @impl true def handle_info({:diagnostic_event, diagnostic_data}, socket) do component = diagnostic_data.component updated_diagnostics = Map.put(socket.assigns.diagnostics, component, diagnostic_data) socket = socket |> assign(:diagnostics, updated_diagnostics) |> assign(:last_updated, DateTime.utc_now()) {:noreply, socket} end # Handle log events @impl true def handle_info({:log_event, log_data}, socket) do updated_logs = add_to_buffer(socket.assigns.logs, log_data, @log_buffer_limit) socket = socket |> assign(:logs, updated_logs) |> assign(:last_updated, DateTime.utc_now()) |> update_operational_overview() {:noreply, socket} end # Handle configuration changes @impl true def handle_info({:config_change, config_data}, socket) do updated_config = add_to_buffer(socket.assigns.config_changes, config_data, 20) socket = socket |> assign(:config_changes, updated_config) |> assign(:last_updated, DateTime.utc_now()) |> update_operational_overview() {:noreply, socket} end # Handle periodic refresh @impl true def handle_info(:refresh, socket) do socket = if socket.assigns.auto_refresh do schedule_refresh() refresh_operational_data(socket) else socket end {:noreply, socket} end # Catch-all for unknown messages @impl true def handle_info(_msg, socket) do {:noreply, socket} end @impl true def render(assigns) do ~H"""

πŸ” Operational Monitoring

Comprehensive system oversight and analytics

<%= if @active_tab == "overview" do %> <.operational_overview_tab operational_overview={@operational_overview} /> <% end %> <%= if @active_tab == "performance" do %> <.performance_tab metrics={@performance_metrics} /> <% end %> <%= if @active_tab == "security" do %> <.security_tab events={@security_events} filter={@filters.security_type} /> <% end %> <%= if @active_tab == "audit" do %> <.audit_tab audit_trail={@audit_trail} /> <% end %> <%= if @active_tab == "alerts" do %> <.alerts_tab alerts={@alerts} filter={@filters.alert_severity} /> <% end %> <%= if @active_tab == "diagnostics" do %> <.diagnostics_tab diagnostics={@diagnostics} /> <% end %> <%= if @active_tab == "logs" do %> <.logs_tab logs={@logs} filter={@filters.log_level} /> <% end %>
πŸ• Last updated: <%= format_datetime(@last_updated) %> <%= if @auto_refresh, do: "🟒 Live", else: "⏸️ Paused" %>
""" end # Tab component functions would be defined here defp operational_overview_tab(assigns) do ~H"""

πŸ₯ System Health

<%= @operational_overview.health_score %>%
<%= @operational_overview.health_status %>

🚨 Active Alerts

<%= @operational_overview.critical_alerts %> Critical
<%= @operational_overview.warning_alerts %> Warning

πŸ“ˆ Performance

Response: <%= @operational_overview.avg_response_time %>ms
Throughput: <%= @operational_overview.throughput %>/s

πŸ›‘οΈ Security

Events: <%= @operational_overview.security_events_count %>
Last: <%= @operational_overview.last_security_event %>

πŸ“… Recent Activity

<%= for activity <- @operational_overview.recent_activity do %>
<%= activity.time %> <%= activity.type %> <%= activity.message %>
<% end %>
""" end # Additional tab components would be implemented here... defp performance_tab(assigns), do: ~H"
Performance metrics tab content
" defp security_tab(assigns), do: ~H"
Security events tab content
" defp audit_tab(assigns), do: ~H"
Audit trail tab content
" defp alerts_tab(assigns), do: ~H"
Alerts management tab content
" defp diagnostics_tab(assigns), do: ~H"
Diagnostics tab content
" defp logs_tab(assigns), do: ~H"
Logs tab content
" # Private helper functions defp subscribe_to_operational_events do # Subscribe to all operational channels Phoenix.PubSub.subscribe(ExESDBDashboard.PubSub, "operational_alerts") Phoenix.PubSub.subscribe(ExESDBDashboard.PubSub, "operational_metrics") Phoenix.PubSub.subscribe(ExESDBDashboard.PubSub, "operational_security") Phoenix.PubSub.subscribe(ExESDBDashboard.PubSub, "operational_audit") Phoenix.PubSub.subscribe(ExESDBDashboard.PubSub, "operational_diagnostics") Phoenix.PubSub.subscribe(ExESDBDashboard.PubSub, "operational_logging") Phoenix.PubSub.subscribe(ExESDBDashboard.PubSub, "operational_config") end defp schedule_refresh do Process.send_after(self(), :refresh, @refresh_interval) end defp refresh_operational_data(socket) do socket |> assign(:last_updated, DateTime.utc_now()) |> update_operational_overview() end defp update_operational_overview(socket) do overview = calculate_operational_overview(socket.assigns) assign(socket, :operational_overview, overview) end defp calculate_operational_overview(assigns) do %{ health_score: calculate_health_score(assigns), health_status: determine_health_status(assigns), critical_alerts: count_alerts_by_severity(assigns.alerts, "critical"), warning_alerts: count_alerts_by_severity(assigns.alerts, "warning"), avg_response_time: calculate_avg_response_time(assigns.performance_metrics), throughput: calculate_throughput(assigns.performance_metrics), security_events_count: length(assigns.security_events), last_security_event: get_last_security_event(assigns.security_events), recent_activity: generate_recent_activity(assigns) } end defp get_initial_operational_overview do %{ health_score: 95, health_status: "Healthy", critical_alerts: 0, warning_alerts: 0, avg_response_time: 45, throughput: 1250, security_events_count: 0, last_security_event: "None", recent_activity: [] } end defp add_to_buffer(buffer, new_item, limit) do [new_item | buffer] |> Enum.take(limit) end defp calculate_health_score(_assigns), do: 95 defp determine_health_status(_assigns), do: "Healthy" defp count_alerts_by_severity(alerts, severity), do: Enum.count(alerts, &(&1.severity == severity)) defp calculate_avg_response_time(_metrics), do: 45 defp calculate_throughput(_metrics), do: 1250 defp get_last_security_event([]), do: "None" defp get_last_security_event([latest | _]), do: format_datetime(latest.timestamp) defp generate_recent_activity(_assigns), do: [] defp format_datetime(%DateTime{} = dt) do Calendar.strftime(dt, "%H:%M:%S") end defp format_datetime(_), do: "unknown" end