defmodule ToweropsWeb.HelpLive.Index do @moduledoc false use ToweropsWeb, :live_view alias Towerops.Accounts alias Towerops.Accounts.Scope alias Towerops.HTTP alias Towerops.Organizations alias ToweropsWeb.HelpLive.Sections.About alias ToweropsWeb.HelpLive.Sections.Agents alias ToweropsWeb.HelpLive.Sections.ApiTokens alias ToweropsWeb.HelpLive.Sections.CloudPollers alias ToweropsWeb.HelpLive.Sections.GettingStarted alias ToweropsWeb.HelpLive.Sections.GraphqlApi alias ToweropsWeb.HelpLive.Sections.Graphs alias ToweropsWeb.HelpLive.Sections.Insights alias ToweropsWeb.HelpLive.Sections.Integrations alias ToweropsWeb.HelpLive.Sections.Mikrotik alias ToweropsWeb.HelpLive.Sections.NetworkMap alias ToweropsWeb.HelpLive.Sections.NotFound alias ToweropsWeb.HelpLive.Sections.RestApi alias ToweropsWeb.HelpLive.Sections.Settings alias ToweropsWeb.HelpLive.Sections.Sites @impl true def mount(_params, session, socket) do # Check if user is logged in via session current_user = get_current_user(socket, session) is_authenticated = !is_nil(current_user) # Load user's default organization if authenticated current_organization = if current_user do case Organizations.list_user_organizations(current_user.id) do [first_org | _] -> first_org [] -> nil end end # Build current_scope if authenticated current_scope = if current_user do current_user |> Scope.for_user() |> Scope.put_organization(current_organization) end socket = socket |> assign(:page_title, t("Help")) |> assign(:current_user, current_user) |> assign(:is_authenticated, is_authenticated) |> assign(:current_scope, current_scope) |> assign(:generated_password, nil) |> assign(:password_generating, false) # Generate initial password if connected?(socket) do send(self(), :fetch_random_password) end {:ok, socket} end @impl true def handle_params(params, _url, socket) do section = Map.get(params, "section", "about") socket = socket |> assign(:active_section, section) |> assign(:generated_password, nil) |> assign(:password_generating, false) {:noreply, socket} end @impl true def handle_event("generate_password", _params, socket) do socket = assign(socket, :password_generating, true) send(self(), :fetch_random_password) {:noreply, socket} end @impl true def handle_info(:fetch_random_password, socket) do case generate_random_password() do {:ok, password} -> {:noreply, socket |> assign(:generated_password, password) |> assign(:password_generating, false)} {:error, reason} -> {:noreply, socket |> assign(:password_generating, false) |> put_flash(:error, t("Failed to generate password: %{reason}", reason: reason))} end end defp generate_random_password do # Generate a 24-character truly random password from random.org # Format: uppercase, lowercase, and digits url = "https://www.random.org/strings/?num=1&len=24&digits=on&upperalpha=on&loweralpha=on&unique=on&format=plain&rnd=new" case HTTP.get(__MODULE__, url) do {:ok, %{status: 200, body: body}} -> password = String.trim(body) {:ok, password} {:ok, %{status: status}} -> {:error, "random.org returned status #{status}"} {:error, error} -> {:error, format_request_error(error)} end end defp format_request_error(%_{} = error), do: Exception.message(error) defp format_request_error(error), do: inspect(error) defp get_current_user(socket, session) do # Try to get current_user from socket assigns (if already set by on_mount) with nil <- Map.get(socket.assigns, :current_user), token when not is_nil(token) <- session["user_token"], {user, _authenticated_at} <- Accounts.get_user_by_session_token(token) do user else %Towerops.Accounts.User{} = user -> user _ -> nil end end defp help_content(assigns) do ~H"""

Help & Documentation

Learn how to use Towerops to monitor your network infrastructure

{render_section(@active_section, assigns)}
""" end defp render_section("about", assigns), do: About.render(assigns) defp render_section("getting-started", assigns), do: GettingStarted.render(assigns) defp render_section("settings", assigns), do: Settings.render(assigns) defp render_section("sites", assigns), do: Sites.render(assigns) defp render_section("cloud-pollers", assigns), do: CloudPollers.render(assigns) defp render_section("agents", assigns), do: Agents.render(assigns) defp render_section("integrations", assigns), do: Integrations.render(assigns) defp render_section("graphs", assigns), do: Graphs.render(assigns) defp render_section("insights", assigns), do: Insights.render(assigns) defp render_section("network-map", assigns), do: NetworkMap.render(assigns) defp render_section("api-tokens", assigns), do: ApiTokens.render(assigns) defp render_section("rest-api", assigns), do: RestApi.render(assigns) defp render_section("graphql-api", assigns), do: GraphqlApi.render(assigns) defp render_section("mikrotik", assigns), do: Mikrotik.render(assigns) defp render_section(_section, assigns), do: NotFound.render(assigns) end