From 8752dfec49535a44217f7345fc5ce3f8d6d94676 Mon Sep 17 00:00:00 2001 From: Graham McIntie Date: Sat, 14 Feb 2026 17:43:54 -0600 Subject: [PATCH] =?UTF-8?q?fix:=20netbox=20url=20field=20type,=20gaiia=20i?= =?UTF-8?q?pRange=E2=86=92block,=20remove=20unknown=20webhook=20log?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/towerops/gaiia/client.ex | 2 +- lib/towerops/gaiia/sync.ex | 2 +- lib/towerops/gaiia/webhooks.ex | 4 +- .../user_session_html/totp.html.heex | 4 +- .../user_settings_html/edit.html.heex | 8 +- .../user_sudo_html/verify.html.heex | 4 +- .../live/activity_feed_live.html.heex | 4 +- .../live/admin/dashboard_live.html.heex | 8 +- .../live/admin/org_live/index.html.heex | 6 +- .../live/admin/security_live/index.html.heex | 4 +- .../live/admin/user_live/index.html.heex | 4 +- .../live/agent_live/edit.html.heex | 5 +- .../live/agent_live/index.html.heex | 28 +- .../live/agent_live/show.html.heex | 16 +- .../live/device_live/form.html.heex | 53 +- .../live/device_live/index.html.heex | 12 +- .../live/device_live/show.html.heex | 46 +- .../live/org/gaiia_mapping_live.html.heex | 8 +- .../org/gaiia_reconciliation_live.html.heex | 4 +- .../live/org/integrations_live.html.heex | 14 +- .../live/org/preseem_devices_live.html.heex | 4 +- lib/towerops_web/live/org/settings_live.ex | 9 +- .../live/org/settings_live.html.heex | 114 +- .../live/org_live/index.html.heex | 4 +- lib/towerops_web/live/org_live/new.html.heex | 9 +- .../live/site_live/form.html.heex | 52 +- .../live/site_live/show.html.heex | 8 +- .../live/trace_live/index.html.heex | 14 +- .../live/user_settings_live.html.heex | 68 +- .../plugs/update_session_activity.ex | 3 +- priv/gettext/es/LC_MESSAGES/admin.po | 14 +- priv/gettext/es/LC_MESSAGES/auth.po | 50 +- priv/gettext/es/LC_MESSAGES/default.po | 1686 ++++++++--------- priv/gettext/es/LC_MESSAGES/equipment.po | 62 +- 34 files changed, 1296 insertions(+), 1037 deletions(-) diff --git a/lib/towerops/gaiia/client.ex b/lib/towerops/gaiia/client.ex index e5e466d7..b21440f6 100644 --- a/lib/towerops/gaiia/client.ex +++ b/lib/towerops/gaiia/client.ex @@ -74,7 +74,7 @@ defmodule Towerops.Gaiia.Client do edges { node { id - ipRange + block } } } diff --git a/lib/towerops/gaiia/sync.ex b/lib/towerops/gaiia/sync.ex index bd65be57..6e523f2c 100644 --- a/lib/towerops/gaiia/sync.ex +++ b/lib/towerops/gaiia/sync.ex @@ -99,7 +99,7 @@ defmodule Towerops.Gaiia.Sync do defp map_network_site(node) do ip_block_edges = get_in(node, ["ipBlocks", "edges"]) || [] - ip_blocks = Enum.map(ip_block_edges, & &1["node"]["ipRange"]) + ip_blocks = Enum.map(ip_block_edges, & &1["node"]["block"]) %{ gaiia_id: node["id"], diff --git a/lib/towerops/gaiia/webhooks.ex b/lib/towerops/gaiia/webhooks.ex index cbf457dd..5afe4c45 100644 --- a/lib/towerops/gaiia/webhooks.ex +++ b/lib/towerops/gaiia/webhooks.ex @@ -9,8 +9,6 @@ defmodule Towerops.Gaiia.Webhooks do alias Towerops.Gaiia - require Logger - @doc """ Verify a Gaiia webhook signature using the `timestamp.body` signed payload format. @@ -35,7 +33,7 @@ defmodule Towerops.Gaiia.Webhooks do "account." <> _ -> handle_account_event(organization_id, event, payload) "billing_subscription." <> _ -> handle_subscription_event(organization_id, event, payload) "inventory_item." <> _ -> handle_inventory_event(organization_id, event, payload) - _ -> Logger.info("Ignoring unknown Gaiia webhook event: #{event}") + _ -> :ok end :ok diff --git a/lib/towerops_web/controllers/user_session_html/totp.html.heex b/lib/towerops_web/controllers/user_session_html/totp.html.heex index 2102984f..36f9301d 100644 --- a/lib/towerops_web/controllers/user_session_html/totp.html.heex +++ b/lib/towerops_web/controllers/user_session_html/totp.html.heex @@ -23,7 +23,9 @@ />

- {t_auth("Enter the 6-digit code from your authenticator app, or use a recovery code (XXXX-XXXX format).")} + {t_auth( + "Enter the 6-digit code from your authenticator app, or use a recovery code (XXXX-XXXX format)." + )}

diff --git a/lib/towerops_web/controllers/user_settings_html/edit.html.heex b/lib/towerops_web/controllers/user_settings_html/edit.html.heex index 8011ad1e..bc373823 100644 --- a/lib/towerops_web/controllers/user_settings_html/edit.html.heex +++ b/lib/towerops_web/controllers/user_settings_html/edit.html.heex @@ -11,7 +11,9 @@ <.input field={f[:email]} type="email" label={t_auth("Email")} autocomplete="email" required /> - <.button variant="primary" phx-disable-with={t_auth("Changing...")}>{t_auth("Change Email")} + <.button variant="primary" phx-disable-with={t_auth("Changing...")}> + {t_auth("Change Email")} +
@@ -73,7 +75,9 @@ {session.device_os} • {session.app_version}

- {t_auth("Last used %{date}", date: ToweropsWeb.TimeHelpers.format_date(session.last_used_at, "UTC"))} + {t_auth("Last used %{date}", + date: ToweropsWeb.TimeHelpers.format_date(session.last_used_at, "UTC") + )}

diff --git a/lib/towerops_web/controllers/user_sudo_html/verify.html.heex b/lib/towerops_web/controllers/user_sudo_html/verify.html.heex index d79912c4..13dee43b 100644 --- a/lib/towerops_web/controllers/user_sudo_html/verify.html.heex +++ b/lib/towerops_web/controllers/user_sudo_html/verify.html.heex @@ -23,7 +23,9 @@ />

- {t_auth("Enter the 6-digit code from your authenticator app. Recovery codes are not allowed for sensitive operations.")} + {t_auth( + "Enter the 6-digit code from your authenticator app. Recovery codes are not allowed for sensitive operations." + )}

diff --git a/lib/towerops_web/live/activity_feed_live.html.heex b/lib/towerops_web/live/activity_feed_live.html.heex index 8b41e9ad..8a40d556 100644 --- a/lib/towerops_web/live/activity_feed_live.html.heex +++ b/lib/towerops_web/live/activity_feed_live.html.heex @@ -98,7 +98,9 @@ <%= if @search != "" do %> {t("Try adjusting your search terms or clearing the filters.")} <% else %> - {t("Your network operations log is empty. Events will appear here in real time as config changes, alerts, device events, and syncs happen across your organization.")} + {t( + "Your network operations log is empty. Events will appear here in real time as config changes, alerts, device events, and syncs happen across your organization." + )} <% end %>

diff --git a/lib/towerops_web/live/admin/dashboard_live.html.heex b/lib/towerops_web/live/admin/dashboard_live.html.heex index be6ec38d..2e1aeee4 100644 --- a/lib/towerops_web/live/admin/dashboard_live.html.heex +++ b/lib/towerops_web/live/admin/dashboard_live.html.heex @@ -18,7 +18,9 @@
-

{t("Organizations")}

+

+ {t("Organizations")} +

{@org_count}

<.link navigate={~p"/admin/organizations"} @@ -91,7 +93,9 @@
-

{t("Recent Audit Logs")}

+

+ {t("Recent Audit Logs")} +

<.link navigate={~p"/admin/audit"} class="text-sm text-blue-600 dark:text-blue-400 hover:underline" diff --git a/lib/towerops_web/live/admin/org_live/index.html.heex b/lib/towerops_web/live/admin/org_live/index.html.heex index e218f701..471e67f3 100644 --- a/lib/towerops_web/live/admin/org_live/index.html.heex +++ b/lib/towerops_web/live/admin/org_live/index.html.heex @@ -17,7 +17,11 @@
diff --git a/lib/towerops_web/live/agent_live/index.html.heex b/lib/towerops_web/live/agent_live/index.html.heex index bc8347dd..0eb6922f 100644 --- a/lib/towerops_web/live/agent_live/index.html.heex +++ b/lib/towerops_web/live/agent_live/index.html.heex @@ -154,7 +154,11 @@ type="button" phx-click="delete_agent" phx-value-id={agent.id} - data-confirm={t("Are you sure you want to delete this agent? All device assignments will be removed, and devices will fall back to site/organization defaults or cloud polling.")} + data-confirm={ + t( + "Are you sure you want to delete this agent? All device assignments will be removed, and devices will fall back to site/organization defaults or cloud polling." + ) + } class="text-sm font-medium text-red-600 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300" > {t("Delete")} @@ -175,7 +179,9 @@ {t("Cloud Pollers")}

- {t("Application-wide agents that can poll devices for any organization. Only visible and manageable by superadmins.")} + {t( + "Application-wide agents that can poll devices for any organization. Only visible and manageable by superadmins." + )}

<.table id="cloud-pollers-table" @@ -264,7 +270,11 @@ type="button" phx-click="delete_agent" phx-value-id={agent.id} - data-confirm={t("Are you sure you want to delete this cloud poller? This will affect all organizations using it.")} + data-confirm={ + t( + "Are you sure you want to delete this cloud poller? This will affect all organizations using it." + ) + } class="text-sm font-medium text-red-600 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300" > {t("Delete")} @@ -286,7 +296,9 @@ {t("Global Default Cloud Poller")}

- {t("Fallback agent for organizations without a default agent configured. Devices with no assignment at any level will use this agent.")} + {t( + "Fallback agent for organizations without a default agent configured. Devices with no assignment at any level will use this agent." + )}

<.form for={%{}} phx-change="update_selected_global_default" class="max-w-md"> @@ -329,7 +341,9 @@ <% else %>

<.icon name="hero-information-circle" class="h-4 w-4 inline" /> - {t("No global default configured. Devices without assignments will use direct Phoenix cluster polling.")} + {t( + "No global default configured. Devices without assignments will use direct Phoenix cluster polling." + )}

<% end %> @@ -440,7 +454,9 @@ {t("Docker Compose Setup with Auto-Updates")}

- {t("This configuration includes Watchtower for automatic agent updates. The agent will automatically update to the latest version every 12 hours.")} + {t( + "This configuration includes Watchtower for automatic agent updates. The agent will automatically update to the latest version every 12 hours." + )}

services:
   towerops-agent:
diff --git a/lib/towerops_web/live/agent_live/show.html.heex b/lib/towerops_web/live/agent_live/show.html.heex
index 39c54254..099e1e4a 100644
--- a/lib/towerops_web/live/agent_live/show.html.heex
+++ b/lib/towerops_web/live/agent_live/show.html.heex
@@ -10,7 +10,11 @@
       
diff --git a/lib/towerops_web/live/device_live/form.html.heex b/lib/towerops_web/live/device_live/form.html.heex index e7102517..31a46b78 100644 --- a/lib/towerops_web/live/device_live/form.html.heex +++ b/lib/towerops_web/live/device_live/form.html.heex @@ -153,7 +153,9 @@

Non-routable IP address detected

- {t("This IP address (private network or CGNAT range) cannot be reached from the cloud.")} + {t( + "This IP address (private network or CGNAT range) cannot be reached from the cloud." + )} {t("To monitor this device, assign it to a")} <.link navigate={~p"/agents"} class="font-medium underline"> remote agent @@ -290,7 +292,9 @@ <% end %> <% else %>

- {t("Assign this device to a remote agent for local SNMP polling. Leave empty to inherit from site or organization defaults.")} + {t( + "Assign this device to a remote agent for local SNMP polling. Leave empty to inherit from site or organization defaults." + )}

<% end %>
@@ -306,7 +310,9 @@ {t("SNMP Configuration")}

- {t("Choose monitoring mode and configure SNMP settings for detailed device information.")} + {t( + "Choose monitoring mode and configure SNMP settings for detailed device information." + )}

@@ -349,8 +355,9 @@
<%= if @monitoring_mode == "snmp_and_icmp" do %>

- {t("Monitor device availability with ICMP pings and collect detailed information via SNMP")} - (interfaces, sensors, system info). + {t( + "Monitor device availability with ICMP pings and collect detailed information via SNMP" + )} (interfaces, sensors, system info).

<% else %>

Monitor device availability with ICMP pings only (no SNMP discovery).

@@ -418,7 +425,9 @@ <% :default -> %>

<.icon name="hero-information-circle" class="h-4 w-4" /> - {t("Using default (v2c, no community set - configure at organization or site level)")} + {t( + "Using default (v2c, no community set - configure at organization or site level)" + )}

<% end %> <% end %> @@ -538,7 +547,11 @@
- <.button type="button" phx-click="test_snmp" phx-disable-with={t("Testing...")}> + <.button + type="button" + phx-click="test_snmp" + phx-disable-with={t("Testing...")} + > {t("Test SNMP Connection")} @@ -623,7 +636,9 @@ {t("MikroTik API Configuration")}

- {t("Configure MikroTik RouterOS API access. Works alongside SNMP for enhanced device management. SSH is used for device backups.")} + {t( + "Configure MikroTik RouterOS API access. Works alongside SNMP for enhanced device management. SSH is used for device backups." + )}

@@ -694,8 +709,12 @@

🚨 Critical Security Warning: - {t("Plain API (port 8728) sends credentials unencrypted over the network.")} - {t("This setting is blocked when using cloud pollers")} + {t( + "Plain API (port 8728) sends credentials unencrypted over the network." + )} + + {t("This setting is blocked when using cloud pollers")} + and should only be used with local agents on trusted networks.

@@ -726,7 +745,9 @@ <%= if @live_action == :edit do %>
-

{t("Danger Zone")}

+

+ {t("Danger Zone")} +

{t("Permanently delete this device and all its data.")}

@@ -735,11 +756,17 @@

- {t("Once you delete this device, there is no going back. All monitoring history and alerts will be permanently deleted.")} + {t( + "Once you delete this device, there is no going back. All monitoring history and alerts will be permanently deleted." + )}

<.button phx-click="delete" - data-confirm={t("Are you sure you want to delete this device? All monitoring history and alerts will be permanently deleted.")} + data-confirm={ + t( + "Are you sure you want to delete this device? All monitoring history and alerts will be permanently deleted." + ) + } variant="danger" > <.icon name="hero-trash" class="h-4 w-4" /> Delete Device diff --git a/lib/towerops_web/live/device_live/index.html.heex b/lib/towerops_web/live/device_live/index.html.heex index a6cda639..3cb7719e 100644 --- a/lib/towerops_web/live/device_live/index.html.heex +++ b/lib/towerops_web/live/device_live/index.html.heex @@ -97,7 +97,9 @@ <.button type="button" phx-click="force_rediscover_all" - data-confirm={t("This will trigger SNMP discovery for all SNMP-enabled devices. Continue?")} + data-confirm={ + t("This will trigger SNMP discovery for all SNMP-enabled devices. Continue?") + } > <.icon name="hero-magnifying-glass" class="h-4 w-4" /> Force Rediscover All @@ -156,7 +158,9 @@

- {t("Sites help you organize devices by physical location. Create a site to assign your devices and keep things organized.")} + {t( + "Sites help you organize devices by physical location. Create a site to assign your devices and keep things organized." + )}

@@ -172,7 +176,9 @@ <%= if !@has_devices do %>
<.icon name="hero-server" class="mx-auto h-12 w-12 text-gray-400 dark:text-gray-500" /> -

{t("No devices")}

+

+ {t("No devices")} +

{t("Get started by adding your first device.")}

diff --git a/lib/towerops_web/live/device_live/show.html.heex b/lib/towerops_web/live/device_live/show.html.heex index e98ad612..1b439e79 100644 --- a/lib/towerops_web/live/device_live/show.html.heex +++ b/lib/towerops_web/live/device_live/show.html.heex @@ -886,7 +886,9 @@ <%= if @voltage_sensors && length(@voltage_sensors) > 0 do %>
-

{t("Voltage")}

+

+ {t("Voltage")} +

@@ -964,7 +966,9 @@ <%= if (@general_counters && length(@general_counters) > 0) || (@firewall_counters && length(@firewall_counters) > 0) do %>
-

{t("Counters")}

+

+ {t("Counters")} +

@@ -1037,7 +1041,9 @@ <%= if @wireless_sensors && length(@wireless_sensors) > 0 do %>
-

{t("Wireless")}

+

+ {t("Wireless")} +

@@ -1406,7 +1412,9 @@ {t("No neighbors discovered")}

- {t("This device doesn't have any LLDP or CDP neighbors, or neighbor discovery hasn't run yet.")} + {t( + "This device doesn't have any LLDP or CDP neighbors, or neighbor discovery hasn't run yet." + )}

@@ -1572,7 +1580,9 @@ {t("No MAC addresses discovered")}

- {t("This device doesn't have any MAC forwarding entries, or MAC discovery hasn't run yet.")} + {t( + "This device doesn't have any MAC forwarding entries, or MAC discovery hasn't run yet." + )}

@@ -1641,7 +1651,9 @@ {t("No VLANs discovered")}

- {t("This device doesn't have any VLANs configured, or VLAN discovery hasn't run yet.")} + {t( + "This device doesn't have any VLANs configured, or VLAN discovery hasn't run yet." + )}

@@ -1757,7 +1769,9 @@ {t("No IP addresses discovered")}

- {t("This device doesn't have any IP addresses discovered, or IP discovery hasn't run yet.")} + {t( + "This device doesn't have any IP addresses discovered, or IP discovery hasn't run yet." + )}

@@ -1903,7 +1917,11 @@
<% end %> @@ -2242,7 +2262,9 @@ <% else %>

- {t("No raw discovery data available. Run discovery again to collect debug data.")} + {t( + "No raw discovery data available. Run discovery again to collect debug data." + )}

<% end %> @@ -2258,7 +2280,9 @@ {t("No checks configured")}

- {t("Run discovery to automatically detect sensors, interfaces, and other monitorable items, or add a service check manually.")} + {t( + "Run discovery to automatically detect sensors, interfaces, and other monitorable items, or add a service check manually." + )}

<%= if @device.snmp_enabled do %> diff --git a/lib/towerops_web/live/org/gaiia_mapping_live.html.heex b/lib/towerops_web/live/org/gaiia_mapping_live.html.heex index 7fa202cd..9857854f 100644 --- a/lib/towerops_web/live/org/gaiia_mapping_live.html.heex +++ b/lib/towerops_web/live/org/gaiia_mapping_live.html.heex @@ -15,7 +15,9 @@ {t("Gaiia Entity Mapping")}

- {t("Link Gaiia entities to your Towerops sites and devices. Match suggestions are shown in amber.")} + {t( + "Link Gaiia entities to your Towerops sites and devices. Match suggestions are shown in amber." + )}

@@ -120,7 +122,9 @@ <% "unmapped" -> %> {t("All entities are mapped.")} <% _ -> %> - {t("No Gaiia data has been synced yet. Enable the Gaiia integration and run a sync.")} + {t( + "No Gaiia data has been synced yet. Enable the Gaiia integration and run a sync." + )} <% end %>

diff --git a/lib/towerops_web/live/org/gaiia_reconciliation_live.html.heex b/lib/towerops_web/live/org/gaiia_reconciliation_live.html.heex index 55e4a933..55d73570 100644 --- a/lib/towerops_web/live/org/gaiia_reconciliation_live.html.heex +++ b/lib/towerops_web/live/org/gaiia_reconciliation_live.html.heex @@ -6,7 +6,9 @@ <.header> {t("Gaiia Inventory Reconciliation")} <:subtitle> - {t("Compare Gaiia inventory against Towerops device discovery. Reconciliation runs nightly.")} + {t( + "Compare Gaiia inventory against Towerops device discovery. Reconciliation runs nightly." + )} diff --git a/lib/towerops_web/live/org/integrations_live.html.heex b/lib/towerops_web/live/org/integrations_live.html.heex index b92b2486..a115789f 100644 --- a/lib/towerops_web/live/org/integrations_live.html.heex +++ b/lib/towerops_web/live/org/integrations_live.html.heex @@ -266,7 +266,9 @@ {t("Webhook Configuration")}

- {t("Receive real-time updates from Gaiia when accounts, subscriptions, or inventory items change.")} + {t( + "Receive real-time updates from Gaiia when accounts, subscriptions, or inventory items change." + )}

@@ -321,7 +323,11 @@ type="button" id="regenerate-webhook-secret" phx-click="regenerate_webhook_secret" - data-confirm={t("Are you sure? Any existing Gaiia webhook using the current secret will stop working.")} + data-confirm={ + t( + "Are you sure? Any existing Gaiia webhook using the current secret will stop working." + ) + } class="text-xs text-red-600 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300" > {t("Regenerate Secret")} @@ -342,7 +348,9 @@
  • Save the webhook
  • - {t("Once configured, Towerops will receive real-time updates when accounts, subscriptions, or inventory items change in Gaiia.")} + {t( + "Once configured, Towerops will receive real-time updates when accounts, subscriptions, or inventory items change in Gaiia." + )}

    diff --git a/lib/towerops_web/live/org/preseem_devices_live.html.heex b/lib/towerops_web/live/org/preseem_devices_live.html.heex index 16e15929..70062dfe 100644 --- a/lib/towerops_web/live/org/preseem_devices_live.html.heex +++ b/lib/towerops_web/live/org/preseem_devices_live.html.heex @@ -107,7 +107,9 @@ <% "matched" -> %> {t("No matched access points yet. Sync from Preseem and link devices.")} <% _ -> %> - {t("No Preseem access points have been synced yet. Enable the integration and run a sync.")} + {t( + "No Preseem access points have been synced yet. Enable the integration and run a sync." + )} <% end %>

    diff --git a/lib/towerops_web/live/org/settings_live.ex b/lib/towerops_web/live/org/settings_live.ex index f8ef3156..6f4e3fac 100644 --- a/lib/towerops_web/live/org/settings_live.ex +++ b/lib/towerops_web/live/org/settings_live.ex @@ -139,14 +139,16 @@ defmodule ToweropsWeb.Org.SettingsLive do def handle_event("apply_snmp_to_all", _params, socket) do {count, _} = Organizations.apply_snmp_config_to_all_equipment(socket.assigns.organization.id) - {:noreply, put_flash(socket, :info, t("Applied SNMP configuration to %{count} device records across all sites", count: count))} + {:noreply, + put_flash(socket, :info, t("Applied SNMP configuration to %{count} device records across all sites", count: count))} end @impl true def handle_event("apply_agent_to_all", _params, socket) do {count, _} = Organizations.apply_agent_to_all_equipment(socket.assigns.organization.id) - {:noreply, put_flash(socket, :info, t("Applied default agent to %{count} device records across all sites", count: count))} + {:noreply, + put_flash(socket, :info, t("Applied default agent to %{count} device records across all sites", count: count))} end # === Members tab events === @@ -176,7 +178,8 @@ defmodule ToweropsWeb.Org.SettingsLive do |> assign(:invite_form, to_form(%{"email" => "", "role" => "member"}))} {:error, changeset} -> - {:noreply, put_flash(socket, :error, t("Failed to send invitation: %{errors}", errors: error_messages(changeset)))} + {:noreply, + put_flash(socket, :error, t("Failed to send invitation: %{errors}", errors: error_messages(changeset)))} end end diff --git a/lib/towerops_web/live/org/settings_live.html.heex b/lib/towerops_web/live/org/settings_live.html.heex index ecccbba9..b31950e4 100644 --- a/lib/towerops_web/live/org/settings_live.html.heex +++ b/lib/towerops_web/live/org/settings_live.html.heex @@ -131,7 +131,9 @@ {t("Default Organization")}

    - {t("Set this organization as your default. When you log in, you'll be directed to your default organization.")} + {t( + "Set this organization as your default. When you log in, you'll be directed to your default organization." + )}

    @@ -171,7 +173,9 @@ {t("Site Organization")}

    - {t("Enable site organization to group devices into physical locations (offices, datacenters, etc.).")} + {t( + "Enable site organization to group devices into physical locations (offices, datacenters, etc.)." + )}

    @@ -182,7 +186,9 @@ label={t("Use sites to organize devices")} />

    - {t("When enabled, you can organize devices into sites. When disabled, all devices belong directly to the organization.")} + {t( + "When enabled, you can organize devices into sites. When disabled, all devices belong directly to the organization." + )}

    <%= if @organization.use_sites && @form[:use_sites].value == false do %> @@ -197,8 +203,12 @@

    - {t("All devices will be removed from their current sites and assigned directly to the organization.")} - {t("Site assignments will be lost, but devices will remain in the organization.")} + {t( + "All devices will be removed from their current sites and assigned directly to the organization." + )} + {t( + "Site assignments will be lost, but devices will remain in the organization." + )}

    @@ -217,7 +227,9 @@ {t("SNMP Configuration")}

    - {t("Set default SNMP settings for all devices in this organization. These can be overridden at the site or device level.")} + {t( + "Set default SNMP settings for all devices in this organization. These can be overridden at the site or device level." + )}

    <.icon name="hero-information-circle" class="h-4 w-4 inline" /> @@ -330,12 +342,18 @@ {t("Force Apply to All Devices")}

    - {t("This will override SNMP settings for ALL devices across all sites in this organization.")} + {t( + "This will override SNMP settings for ALL devices across all sites in this organization." + )}

    - {t("Your NetBox instance URL and API token. The token needs read permission at minimum.")} + {t( + "Your NetBox instance URL and API token. The token needs read permission at minimum." + )} {t("Write permission is required if you want to push data to NetBox.")}

    @@ -1106,7 +1140,7 @@ {t("NetBox URL")}

    - {t("Choose how data flows between TowerOps and NetBox. You can change this later.")} + {t( + "Choose how data flows between TowerOps and NetBox. You can change this later." + )}

    - {t("NetBox is the source of truth. Import devices, sites, and IPs from NetBox into TowerOps.")} + {t( + "NetBox is the source of truth. Import devices, sites, and IPs from NetBox into TowerOps." + )}
    @@ -1191,7 +1229,9 @@ TowerOps → NetBox - {t("TowerOps is the source of truth. Push discovered devices and monitoring data to NetBox.")} + {t( + "TowerOps is the source of truth. Push discovered devices and monitoring data to NetBox." + )} @@ -1217,7 +1257,9 @@ /> Bidirectional - {t("Merge data from both systems. Conflicts resolved by most-recently-updated. Requires write token.")} + {t( + "Merge data from both systems. Conflicts resolved by most-recently-updated. Requires write token." + )} @@ -1233,7 +1275,9 @@

    - {t("Choose which NetBox objects participate in sync. Devices and sites are recommended at minimum.")} + {t( + "Choose which NetBox objects participate in sync. Devices and sites are recommended at minimum." + )}

    - {t("Narrow the sync scope. Leave blank to sync everything. Comma-separated for multiple values.")} + {t( + "Narrow the sync scope. Leave blank to sync everything. Comma-separated for multiple values." + )}

    @@ -1397,7 +1443,9 @@ } />

    - {t("How often TowerOps checks NetBox for changes. 30 minutes is recommended for most setups.")} + {t( + "How often TowerOps checks NetBox for changes. 30 minutes is recommended for most setups." + )}

    @@ -1503,10 +1551,14 @@ Click {t("Add Integration")} → select {t("Events API v2")} -
  • Copy the {t("Integration Key")} and paste it above
  • +
  • + Copy the {t("Integration Key")} and paste it above +
  • - {t("When connected, TowerOps will automatically trigger, acknowledge, and resolve PagerDuty incidents in sync with your alerts.")} + {t( + "When connected, TowerOps will automatically trigger, acknowledge, and resolve PagerDuty incidents in sync with your alerts." + )}

    @@ -1518,7 +1570,9 @@

    - {t("When someone resolves or acknowledges an incident directly in PagerDuty,")} + {t( + "When someone resolves or acknowledges an incident directly in PagerDuty," + )} {t("TowerOps will automatically update the corresponding alert.")}

    @@ -1652,7 +1706,9 @@

    - {t("Receive real-time updates from Gaiia when accounts, subscriptions, or inventory items change.")} + {t( + "Receive real-time updates from Gaiia when accounts, subscriptions, or inventory items change." + )}

    @@ -1714,7 +1770,9 @@
  • Save the webhook
  • - {t("Once configured, Towerops will receive real-time updates when accounts, subscriptions, or inventory items change in Gaiia.")} + {t( + "Once configured, Towerops will receive real-time updates when accounts, subscriptions, or inventory items change in Gaiia." + )}

    diff --git a/lib/towerops_web/live/org_live/index.html.heex b/lib/towerops_web/live/org_live/index.html.heex index abc25e43..09fa5896 100644 --- a/lib/towerops_web/live/org_live/index.html.heex +++ b/lib/towerops_web/live/org_live/index.html.heex @@ -30,7 +30,9 @@
    <.icon name="hero-building-office" class="mx-auto h-12 w-12 text-gray-400 dark:text-gray-600" /> -

    {t("No organizations")}

    +

    + {t("No organizations")} +

    {t("Get started by creating a new organization.")}

    diff --git a/lib/towerops_web/live/org_live/new.html.heex b/lib/towerops_web/live/org_live/new.html.heex index ca71ec9a..f04fbe58 100644 --- a/lib/towerops_web/live/org_live/new.html.heex +++ b/lib/towerops_web/live/org_live/new.html.heex @@ -18,8 +18,9 @@

    You already have {@free_org_count} free {if @free_org_count == 1, do: "organization", - else: "organizations"}. - {t("To create additional organizations, you'll need to upgrade your existing organization to a paid plan.")} + else: "organizations"}. {t( + "To create additional organizations, you'll need to upgrade your existing organization to a paid plan." + )}

    @@ -31,7 +32,9 @@ <.input field={@form[:name]} type="text" label={t("Organization Name")} required />
    - <.button phx-disable-with={t("Creating...")} variant="primary">Create Organization + <.button phx-disable-with={t("Creating...")} variant="primary"> + Create Organization + <.button navigate={~p"/orgs"}>{t("Cancel")}
    diff --git a/lib/towerops_web/live/site_live/form.html.heex b/lib/towerops_web/live/site_live/form.html.heex index cfaeba2d..cb98796e 100644 --- a/lib/towerops_web/live/site_live/form.html.heex +++ b/lib/towerops_web/live/site_live/form.html.heex @@ -98,7 +98,9 @@

    - {t("Enter a full address and click Geocode to automatically fill latitude/longitude")} + {t( + "Enter a full address and click Geocode to automatically fill latitude/longitude" + )}

    @@ -192,7 +194,9 @@ (optional)

    - {t("Set a default agent for SNMP polling at this site. This will override the organization default for all devices at this site. Leave blank to inherit from organization.")} + {t( + "Set a default agent for SNMP polling at this site. This will override the organization default for all devices at this site. Leave blank to inherit from organization." + )}

    <.input @@ -220,7 +224,11 @@ <.button type="button" phx-click="apply_agent_to_all" - data-confirm={t("This will replace agent assignments for ALL devices at this site. Are you sure?")} + data-confirm={ + t( + "This will replace agent assignments for ALL devices at this site. Are you sure?" + ) + } variant="danger" > <.icon name="hero-arrow-path" class="h-4 w-4" /> Apply Agent to All Device @@ -234,7 +242,9 @@

    <% else %>

    - {t("Set a default agent for all devices at this site. Device can override this setting individually.")} + {t( + "Set a default agent for all devices at this site. Device can override this setting individually." + )}

    <% end %> <% end %> @@ -247,7 +257,9 @@ (optional)

    - {t("Override organization SNMP defaults for all devices at this site. Leave blank to inherit from organization.")} + {t( + "Override organization SNMP defaults for all devices at this site. Leave blank to inherit from organization." + )}

    <.input @@ -359,7 +371,9 @@ <.button type="button" phx-click="apply_snmp_to_all" - data-confirm={t("This will replace SNMP settings for ALL devices at this site. Are you sure?")} + data-confirm={ + t("This will replace SNMP settings for ALL devices at this site. Are you sure?") + } variant="danger" > <.icon name="hero-arrow-path" class="h-4 w-4" /> Apply SNMP Config to All Device @@ -375,7 +389,9 @@ (optional)

    - {t("Override organization MikroTik API defaults for all devices at this site. Leave blank to inherit from organization. Only applies to MikroTik devices.")} + {t( + "Override organization MikroTik API defaults for all devices at this site. Leave blank to inherit from organization. Only applies to MikroTik devices." + )}

    @@ -423,7 +439,9 @@

    🚨 Critical Security Warning: - {t("Plain API (port 8728) sends credentials unencrypted over the network. This setting is")} + {t( + "Plain API (port 8728) sends credentials unencrypted over the network. This setting is" + )} blocked for devices using cloud pollers and should only be used with local agents on trusted networks.

    @@ -432,7 +450,9 @@

    ⚠️ Security Warning: - {t("Plain API (port 8728) sends credentials unencrypted. Use SSL (port 8729) whenever possible.")} + {t( + "Plain API (port 8728) sends credentials unencrypted. Use SSL (port 8729) whenever possible." + )}

    <% end %> @@ -448,13 +468,21 @@ <%= if @live_action == :edit do %>
    -

    {t("Danger Zone")}

    +

    + {t("Danger Zone")} +

    - {t("Once you delete a site, there is no going back. This will also delete all devices at this site.")} + {t( + "Once you delete a site, there is no going back. This will also delete all devices at this site." + )}

    <.button phx-click="delete" - data-confirm={t("Are you sure you want to delete this site? This will also delete all devices at this site.")} + data-confirm={ + t( + "Are you sure you want to delete this site? This will also delete all devices at this site." + ) + } variant="danger" > <.icon name="hero-trash" class="h-4 w-4" /> Delete Site diff --git a/lib/towerops_web/live/site_live/show.html.heex b/lib/towerops_web/live/site_live/show.html.heex index d1460dbf..e1d2557e 100644 --- a/lib/towerops_web/live/site_live/show.html.heex +++ b/lib/towerops_web/live/site_live/show.html.heex @@ -16,7 +16,9 @@ <.button type="button" phx-click="force_rediscover_all" - data-confirm={t("This will trigger SNMP discovery for all devices at this site. Continue?")} + data-confirm={ + t("This will trigger SNMP discovery for all devices at this site. Continue?") + } > <.icon name="hero-magnifying-glass" class="h-4 w-4" /> Force Rediscover All @@ -148,7 +150,9 @@ <%= if @site.child_sites != [] do %>
    -

    {t("Child Sites")}

    +

    + {t("Child Sites")} +

    • <.link navigate={~p"/sites/#{child.id}"} class="link link-primary text-sm"> diff --git a/lib/towerops_web/live/trace_live/index.html.heex b/lib/towerops_web/live/trace_live/index.html.heex index 31a1ce30..890859e6 100644 --- a/lib/towerops_web/live/trace_live/index.html.heex +++ b/lib/towerops_web/live/trace_live/index.html.heex @@ -18,7 +18,11 @@ type="text" name="query" value={@query} - placeholder={t("Search by customer name, IP address, account ID, serial number, or device name...")} + placeholder={ + t( + "Search by customer name, IP address, account ID, serial number, or device name..." + ) + } class="input input-bordered w-full dark:bg-gray-800 dark:border-white/10 dark:text-white" phx-debounce="300" autofocus @@ -534,9 +538,13 @@ name="hero-magnifying-glass-circle" class="h-16 w-16 mx-auto mb-4 text-gray-300 dark:text-gray-600" /> -

      {t("Subscriber Trace")}

      +

      + {t("Subscriber Trace")} +

      - {t("Search for a customer by name, IP address, account ID, or device name to see their full network status at a glance.")} + {t( + "Search for a customer by name, IP address, account ID, or device name to see their full network status at a glance." + )}

    diff --git a/lib/towerops_web/live/user_settings_live.html.heex b/lib/towerops_web/live/user_settings_live.html.heex index e44ccb0a..201c5f4c 100644 --- a/lib/towerops_web/live/user_settings_live.html.heex +++ b/lib/towerops_web/live/user_settings_live.html.heex @@ -332,7 +332,9 @@ {t("Default Organization")}

    - {t("Choose which organization you see by default when logging in or navigating to pages without an organization context.")} + {t( + "Choose which organization you see by default when logging in or navigating to pages without an organization context." + )}