diff --git a/lib/towerops/gaiia/webhooks.ex b/lib/towerops/gaiia/webhooks.ex index 495a2615..cbf457dd 100644 --- a/lib/towerops/gaiia/webhooks.ex +++ b/lib/towerops/gaiia/webhooks.ex @@ -20,7 +20,7 @@ defmodule Towerops.Gaiia.Webhooks do @spec verify_signature(binary(), binary(), binary(), binary()) :: boolean() def verify_signature(body, timestamp, signature, secret) do signed_payload = timestamp <> "." <> body - expected = :crypto.mac(:hmac, :sha256, secret, signed_payload) |> Base.encode16(case: :lower) + expected = :hmac |> :crypto.mac(:sha256, secret, signed_payload) |> Base.encode16(case: :lower) Plug.Crypto.secure_compare(expected, signature) end diff --git a/lib/towerops/geocoding.ex b/lib/towerops/geocoding.ex index ad187da2..79e667f6 100644 --- a/lib/towerops/geocoding.ex +++ b/lib/towerops/geocoding.ex @@ -1,7 +1,7 @@ defmodule Towerops.Geocoding do @moduledoc """ Service for geocoding addresses using Google Maps Geocoding API. - + Supports both system-wide API keys and organization-specific encrypted API keys. """ @@ -11,23 +11,20 @@ defmodule Towerops.Geocoding do @doc """ Geocode an address to latitude/longitude coordinates. - + ## Parameters - + - `address`: The address string to geocode - + ## Returns - + - `{:ok, %{latitude: float, longitude: float, formatted_address: string}}` on success - `{:error, reason}` on failure """ def geocode(address) when is_binary(address) do with {:ok, api_key} <- get_api_key(), - {:ok, response} <- make_geocoding_request(address, api_key), - {:ok, result} <- parse_geocoding_response(response) do - {:ok, result} - else - {:error, reason} -> {:error, reason} + {:ok, response} <- make_geocoding_request(address, api_key) do + parse_geocoding_response(response) end end @@ -69,11 +66,12 @@ defmodule Towerops.Geocoding do defp parse_geocoding_response(%{"status" => "OK", "results" => [result | _]}) do with %{"geometry" => %{"location" => %{"lat" => lat, "lng" => lng}}} <- result, %{"formatted_address" => formatted_address} <- result do - {:ok, %{ - latitude: lat, - longitude: lng, - formatted_address: formatted_address - }} + {:ok, + %{ + latitude: lat, + longitude: lng, + formatted_address: formatted_address + }} else _ -> {:error, "Invalid response format from Google Maps API"} end diff --git a/lib/towerops/workers/gaiia_insight_worker.ex b/lib/towerops/workers/gaiia_insight_worker.ex index 1392f3a2..2c18514b 100644 --- a/lib/towerops/workers/gaiia_insight_worker.ex +++ b/lib/towerops/workers/gaiia_insight_worker.ex @@ -49,7 +49,7 @@ defmodule Towerops.Workers.GaiiaInsightWorker do "dedup_key" => "untracked", "finding_type" => "untracked", "count" => count, - "device_ids" => untracked_devices |> Enum.map(& &1.device.id), + "device_ids" => Enum.map(untracked_devices, & &1.device.id), "device_names" => untracked_devices |> Enum.take(10) |> Enum.map(& &1.device.name) } }) diff --git a/lib/towerops_web/components/marketing_layouts.ex b/lib/towerops_web/components/marketing_layouts.ex index 4c58e3de..ae28d68e 100644 --- a/lib/towerops_web/components/marketing_layouts.ex +++ b/lib/towerops_web/components/marketing_layouts.ex @@ -93,8 +93,8 @@ defmodule ToweropsWeb.MarketingLayouts do - - + +
Towerops continuously analyzes data from SNMP polling, Preseem, Gaiia, and other integrations to surface actionable findings. Insights are categorized by source, urgency, and type so you can focus on what matters.
-Use the filter bar to narrow insights by status (active/dismissed), source (Preseem, Gaiia, SNMP, system), and urgency level. Each insight shows affected devices as clickable links so you can drill directly into the device detail page.
@@ -2344,13 +2362,15 @@ defmodule ToweropsWeb.HelpLive.Index do- Tip: Insights run on a nightly schedule. Connect your Gaiia and Preseem integrations in - <.link navigate={~p"/help?section=integrations"} class="underline">Organization Settings + Tip: + Insights run on a nightly schedule. Connect your Gaiia and Preseem integrations in + <.link navigate={~p"/help?section=integrations"} class="underline"> + Organization Settings + to get the most comprehensive insights.
Each site can have an address and/or latitude/longitude coordinates. You can set these in two ways:
- Address-to-coordinate conversion requires a Google Maps Geocoding API key. Your administrator sets this as the GOOGLE_MAPS_API_KEY environment variable on the server. The map display itself uses OpenStreetMap and requires no API key.
+ Address-to-coordinate conversion requires a Google Maps Geocoding API key. Your administrator sets this as the
+
+ GOOGLE_MAPS_API_KEY
+
+ environment variable on the server. The map display itself uses OpenStreetMap and requires no API key.
- Include the token in the Authorization header:
+ Include the token in the
+
+ Authorization
+
+ header:
Authorization: Bearer your-api-token-here
See also: - <.link navigate={~p"/help?section=rest-api"} class="underline">REST API and - <.link navigate={~p"/help?section=graphql-api"} class="underline">GraphQL API + <.link navigate={~p"/help?section=rest-api"} class="underline">REST API + and + <.link navigate={~p"/help?section=graphql-api"} class="underline"> + GraphQL API + for endpoint documentation.
- The Towerops REST API provides programmatic access to your monitoring data. All endpoints are under /api/v1/ and require a valid API token.
+ The Towerops REST API provides programmatic access to your monitoring data. All endpoints are under
+
+ /api/v1/
+
+ and require a valid API token.
<%= raw("curl -H \"Authorization: Bearer YOUR_TOKEN\" \\\n https://app.towerops.net/api/v1/devices") %>
- All responses use <%= raw("{\"data\": ...}") %> format. Results are scoped to the organization associated with your API token.
+ All responses use
+
+ {raw("{\"data\": ...}")}
+
+ format. Results are scoped to the organization associated with your API token.
- The GraphQL API lets you query exactly the data you need in a single request. The endpoint is POST /api/graphql and uses the same API token authentication as the REST API.
+ The GraphQL API lets you query exactly the data you need in a single request. The endpoint is
+
+ POST /api/graphql
+
+ and uses the same API token authentication as the REST API.
<%= raw("curl -X POST https://app.towerops.net/api/graphql \\\n -H \"Authorization: Bearer YOUR_TOKEN\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"query\": \"{ devices { id name ipAddress status } }\"}'") %>
Full reference: - GraphQL API Documentation → + + GraphQL API Documentation → + — complete schema reference with query examples and variable usage.
- <%= site.address %> + {site.address}
<% end %> <%= if site.description do %>- <%= site.description %> + {site.description}
<% end %>- <%= Float.round(site.latitude, 4) %>, <%= Float.round(site.longitude, 4) %> + {Float.round(site.latitude, 4)}, {Float.round(site.longitude, 4)}