From b53a53b199fd8f42ddb2a8ade4f4ae0041b03071 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Sat, 17 Jan 2026 10:52:02 -0600 Subject: [PATCH] Add comprehensive Dialyzer type specifications MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add @type definitions to all schema modules: - User, UserCredential, Membership, Invitation - Organization, Site, AgentAssignment - InterfaceStat, SensorReading, Alert - Fix all compilation warnings with stronger Elixir types: - Remove unused require Logger in log_filter.ex - Remove unused parse_float(nil) clause - Add pin operators (^) for variables in binary pattern matches - Fix Dialyzer errors (25 → 0): - Remove unreachable pattern match clauses - Fix unmatched return values with _ = prefix - Update @spec for deliver_alert_notification/1 - Properly handle all Phoenix.PubSub.subscribe and Task.start return values - Explicitly ignore if statement return values where needed All files now pass mix compile --warnings-as-errors and mix dialyzer. --- .tool-versions | 2 +- lib/towerops/accounts/user.ex | 23 +++++++++++++++-- lib/towerops/accounts/user_credential.ex | 22 +++++++++++++++- lib/towerops/accounts/webauthn.ex | 8 ++---- lib/towerops/agents/agent_assignment.ex | 19 ++++++++++++-- lib/towerops/alerts/alert.ex | 24 +++++++++++++++-- lib/towerops/alerts/alert_notifier.ex | 3 ++- lib/towerops/application.ex | 7 ++--- lib/towerops/equipment/event_logger.ex | 2 +- lib/towerops/log_filter.ex | 2 -- lib/towerops/organizations/invitation.ex | 21 ++++++++++++++- lib/towerops/organizations/membership.ex | 19 ++++++++++++-- lib/towerops/organizations/organization.ex | 24 ++++++++++++++--- lib/towerops/sites/site.ex | 27 +++++++++++++++++--- lib/towerops/snmp/discovery.ex | 17 ++++++------ lib/towerops/snmp/interface_stat.ex | 18 ++++++++++++- lib/towerops/snmp/poller_worker.ex | 14 +++++++--- lib/towerops/snmp/sensor_reading.ex | 14 +++++++++- lib/towerops_web/channels/agent_channel.ex | 1 - lib/towerops_web/live/equipment_live/form.ex | 11 ++++---- lib/towerops_web/live/equipment_live/show.ex | 9 ++++--- lib/towerops_web/live/graph_live/show.ex | 7 ++--- lib/towerops_web/live/mobile_qr_live.ex | 7 ++--- lib/towerops_web/plugs/mobile_auth.ex | 3 +-- lib/towerops_web/telemetry.ex | 26 ++++++++++--------- 25 files changed, 256 insertions(+), 74 deletions(-) diff --git a/.tool-versions b/.tool-versions index 1b175d46..f7be69da 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ erlang 28.3 -elixir 1.19.5-otp-28 +elixir 1.20.0-rc.1-otp-28 diff --git a/lib/towerops/accounts/user.ex b/lib/towerops/accounts/user.ex index 27c3d431..87f4df60 100644 --- a/lib/towerops/accounts/user.ex +++ b/lib/towerops/accounts/user.ex @@ -4,6 +4,10 @@ defmodule Towerops.Accounts.User do import Ecto.Changeset + alias Ecto.Association.NotLoaded + alias Towerops.Accounts.UserCredential + alias Towerops.Organizations.Membership + @primary_key {:id, :binary_id, autogenerate: true} @foreign_key_type :binary_id schema "users" do @@ -14,13 +18,28 @@ defmodule Towerops.Accounts.User do field :authenticated_at, :utc_datetime, virtual: true field :is_superuser, :boolean, default: false - has_many :memberships, Towerops.Organizations.Membership + has_many :memberships, Membership has_many :organizations, through: [:memberships, :organization] - has_many :credentials, Towerops.Accounts.UserCredential + has_many :credentials, UserCredential timestamps(type: :utc_datetime) end + @type t :: %__MODULE__{ + id: Ecto.UUID.t(), + email: String.t(), + password: String.t() | nil, + hashed_password: String.t(), + confirmed_at: DateTime.t() | nil, + authenticated_at: DateTime.t() | nil, + is_superuser: boolean(), + memberships: NotLoaded.t() | [Membership.t()], + organizations: NotLoaded.t() | [Towerops.Organizations.Organization.t()], + credentials: NotLoaded.t() | [UserCredential.t()], + inserted_at: DateTime.t(), + updated_at: DateTime.t() + } + @doc """ A user changeset for registering or changing the email. diff --git a/lib/towerops/accounts/user_credential.ex b/lib/towerops/accounts/user_credential.ex index aba5ee68..fb544f91 100644 --- a/lib/towerops/accounts/user_credential.ex +++ b/lib/towerops/accounts/user_credential.ex @@ -9,6 +9,8 @@ defmodule Towerops.Accounts.UserCredential do import Ecto.Changeset + alias Towerops.Accounts.User + @primary_key {:id, :binary_id, autogenerate: true} @foreign_key_type :binary_id @@ -24,11 +26,29 @@ defmodule Towerops.Accounts.UserCredential do field :backup_state, :boolean, default: false field :attestation_format, :string - belongs_to :user, Towerops.Accounts.User + belongs_to :user, User timestamps(type: :utc_datetime) end + @type t :: %__MODULE__{ + id: Ecto.UUID.t(), + credential_id: binary(), + public_key: binary(), + sign_count: integer(), + name: String.t(), + last_used_at: DateTime.t() | nil, + aaguid: binary(), + transports: [String.t()], + backup_eligible: boolean(), + backup_state: boolean(), + attestation_format: String.t() | nil, + user_id: Ecto.UUID.t(), + user: Ecto.Association.NotLoaded.t() | User.t(), + inserted_at: DateTime.t(), + updated_at: DateTime.t() + } + @doc """ Changeset for creating or updating a credential. """ diff --git a/lib/towerops/accounts/webauthn.ex b/lib/towerops/accounts/webauthn.ex index 2264199e..0fbee704 100644 --- a/lib/towerops/accounts/webauthn.ex +++ b/lib/towerops/accounts/webauthn.ex @@ -79,10 +79,6 @@ defmodule Towerops.Accounts.WebAuthn do {:error, reason} = error -> Logger.error("Failed to decode client data: #{inspect(reason)}") error - - error -> - Logger.error("Failed to decode client data JSON: #{inspect(error)}") - {:error, :invalid_client_data} end end @@ -241,11 +237,11 @@ defmodule Towerops.Accounts.WebAuthn do with <> <- data, true <- byte_size(rest) >= cred_id_length, - <> <- rest, + <> <- rest, {:ok, cose_key, remaining} <- CBOR.decode(public_key_bytes), # Calculate the actual length of the COSE key CBOR data public_key_cbor_length = byte_size(public_key_bytes) - byte_size(remaining), - <> <- + <> <- public_key_bytes do {:ok, %{ diff --git a/lib/towerops/agents/agent_assignment.ex b/lib/towerops/agents/agent_assignment.ex index a1c2b56c..6dbac746 100644 --- a/lib/towerops/agents/agent_assignment.ex +++ b/lib/towerops/agents/agent_assignment.ex @@ -9,17 +9,32 @@ defmodule Towerops.Agents.AgentAssignment do import Ecto.Changeset + alias Ecto.Association.NotLoaded + alias Towerops.Agents.AgentToken + alias Towerops.Equipment.Equipment + @primary_key {:id, :binary_id, autogenerate: true} @foreign_key_type :binary_id schema "agent_assignments" do field :enabled, :boolean, default: true - belongs_to :agent_token, Towerops.Agents.AgentToken - belongs_to :equipment, Towerops.Equipment.Equipment + belongs_to :agent_token, AgentToken + belongs_to :equipment, Equipment timestamps(type: :utc_datetime) end + @type t :: %__MODULE__{ + id: Ecto.UUID.t(), + enabled: boolean(), + agent_token_id: Ecto.UUID.t(), + agent_token: NotLoaded.t() | AgentToken.t(), + equipment_id: Ecto.UUID.t(), + equipment: NotLoaded.t() | Equipment.t(), + inserted_at: DateTime.t(), + updated_at: DateTime.t() + } + @doc """ Changeset for creating and updating agent assignments. diff --git a/lib/towerops/alerts/alert.ex b/lib/towerops/alerts/alert.ex index 0bf1cf95..bc9d4365 100644 --- a/lib/towerops/alerts/alert.ex +++ b/lib/towerops/alerts/alert.ex @@ -4,6 +4,10 @@ defmodule Towerops.Alerts.Alert do import Ecto.Changeset + alias Ecto.Association.NotLoaded + alias Towerops.Accounts.User + alias Towerops.Equipment.Equipment + @primary_key {:id, :binary_id, autogenerate: true} @foreign_key_type :binary_id schema "alerts" do @@ -14,12 +18,28 @@ defmodule Towerops.Alerts.Alert do field :email_sent_at, :utc_datetime field :message, :string - belongs_to :equipment, Towerops.Equipment.Equipment - belongs_to :acknowledged_by, Towerops.Accounts.User + belongs_to :equipment, Equipment + belongs_to :acknowledged_by, User timestamps(type: :utc_datetime) end + @type t :: %__MODULE__{ + id: Ecto.UUID.t(), + alert_type: :equipment_down | :equipment_up, + triggered_at: DateTime.t(), + acknowledged_at: DateTime.t() | nil, + resolved_at: DateTime.t() | nil, + email_sent_at: DateTime.t() | nil, + message: String.t() | nil, + equipment_id: Ecto.UUID.t(), + equipment: NotLoaded.t() | Equipment.t(), + acknowledged_by_id: Ecto.UUID.t() | nil, + acknowledged_by: NotLoaded.t() | User.t() | nil, + inserted_at: DateTime.t(), + updated_at: DateTime.t() + } + @doc false def changeset(alert, attrs) do alert diff --git a/lib/towerops/alerts/alert_notifier.ex b/lib/towerops/alerts/alert_notifier.ex index 0460bc5d..3441fca9 100644 --- a/lib/towerops/alerts/alert_notifier.ex +++ b/lib/towerops/alerts/alert_notifier.ex @@ -17,7 +17,8 @@ defmodule Towerops.Alerts.AlertNotifier do Returns list of sent emails for tracking. """ - @spec deliver_alert_notification(Alert) :: {:ok, [{:ok, Swoosh.Email.t()} | {:error, term()}]} + @spec deliver_alert_notification(Alert.t()) :: + {:ok, [{:ok, Swoosh.Email.t()} | {:error, term()}]} def deliver_alert_notification(%Alert{} = alert) do equipment = alert.equipment_id diff --git a/lib/towerops/application.ex b/lib/towerops/application.ex index 166a0289..ec1d6bc3 100644 --- a/lib/towerops/application.ex +++ b/lib/towerops/application.ex @@ -8,9 +8,10 @@ defmodule Towerops.Application do @impl true def start(_type, _args) do # Run migrations on startup (Ecto handles locking for concurrent runs) - if Application.get_env(:towerops, Towerops.Repo)[:database] != "towerops_test" do - Towerops.Release.migrate() - end + _ = + if Application.get_env(:towerops, Towerops.Repo)[:database] != "towerops_test" do + Towerops.Release.migrate() + end topologies = Application.get_env(:libcluster, :topologies, []) diff --git a/lib/towerops/equipment/event_logger.ex b/lib/towerops/equipment/event_logger.ex index 67a24d39..de460daf 100644 --- a/lib/towerops/equipment/event_logger.ex +++ b/lib/towerops/equipment/event_logger.ex @@ -24,7 +24,7 @@ defmodule Towerops.Equipment.EventLogger do @impl true def init(state) do # Subscribe to equipment events topic - Phoenix.PubSub.subscribe(Towerops.PubSub, "equipment:events") + _ = Phoenix.PubSub.subscribe(Towerops.PubSub, "equipment:events") Logger.info("EventLogger started and subscribed to equipment events") {:ok, state} end diff --git a/lib/towerops/log_filter.ex b/lib/towerops/log_filter.ex index 0dfe238b..c7e08a66 100644 --- a/lib/towerops/log_filter.ex +++ b/lib/towerops/log_filter.ex @@ -3,8 +3,6 @@ defmodule Towerops.LogFilter do Filters out noisy log messages from external sources like port scanners and bots. """ - require Logger - @doc """ Filters out Bandit HTTP errors from invalid requests. diff --git a/lib/towerops/organizations/invitation.ex b/lib/towerops/organizations/invitation.ex index 03b6ca06..3e19943c 100644 --- a/lib/towerops/organizations/invitation.ex +++ b/lib/towerops/organizations/invitation.ex @@ -4,7 +4,9 @@ defmodule Towerops.Organizations.Invitation do import Ecto.Changeset + alias Ecto.Association.NotLoaded alias Towerops.Accounts.User + alias Towerops.Organizations.Organization @primary_key {:id, :binary_id, autogenerate: true} @foreign_key_type :binary_id @@ -15,13 +17,30 @@ defmodule Towerops.Organizations.Invitation do field :accepted_at, :utc_datetime field :expires_at, :utc_datetime - belongs_to :organization, Towerops.Organizations.Organization + belongs_to :organization, Organization belongs_to :invited_by, User belongs_to :accepted_by, User timestamps(type: :utc_datetime) end + @type t :: %__MODULE__{ + id: Ecto.UUID.t(), + email: String.t(), + role: :admin | :member | :viewer, + token: String.t(), + accepted_at: DateTime.t() | nil, + expires_at: DateTime.t(), + organization_id: Ecto.UUID.t(), + organization: NotLoaded.t() | Organization.t(), + invited_by_id: Ecto.UUID.t(), + invited_by: NotLoaded.t() | User.t(), + accepted_by_id: Ecto.UUID.t() | nil, + accepted_by: NotLoaded.t() | User.t() | nil, + inserted_at: DateTime.t(), + updated_at: DateTime.t() + } + @doc false def changeset(invitation, attrs) do invitation diff --git a/lib/towerops/organizations/membership.ex b/lib/towerops/organizations/membership.ex index 5d111ecb..e2f2664b 100644 --- a/lib/towerops/organizations/membership.ex +++ b/lib/towerops/organizations/membership.ex @@ -4,17 +4,32 @@ defmodule Towerops.Organizations.Membership do import Ecto.Changeset + alias Ecto.Association.NotLoaded + alias Towerops.Accounts.User + alias Towerops.Organizations.Organization + @primary_key {:id, :binary_id, autogenerate: true} @foreign_key_type :binary_id schema "organization_memberships" do field :role, Ecto.Enum, values: [:owner, :admin, :member, :viewer] - belongs_to :organization, Towerops.Organizations.Organization - belongs_to :user, Towerops.Accounts.User + belongs_to :organization, Organization + belongs_to :user, User timestamps(type: :utc_datetime) end + @type t :: %__MODULE__{ + id: Ecto.UUID.t(), + role: :owner | :admin | :member | :viewer, + organization_id: Ecto.UUID.t(), + organization: NotLoaded.t() | Organization.t(), + user_id: Ecto.UUID.t(), + user: NotLoaded.t() | User.t(), + inserted_at: DateTime.t(), + updated_at: DateTime.t() + } + @doc false def changeset(membership, attrs) do membership diff --git a/lib/towerops/organizations/organization.ex b/lib/towerops/organizations/organization.ex index 1a032cef..7a1eb0d2 100644 --- a/lib/towerops/organizations/organization.ex +++ b/lib/towerops/organizations/organization.ex @@ -4,21 +4,39 @@ defmodule Towerops.Organizations.Organization do import Ecto.Changeset + alias Ecto.Association.NotLoaded + alias Towerops.Agents.AgentToken + alias Towerops.Organizations.Invitation + alias Towerops.Organizations.Membership + @primary_key {:id, :binary_id, autogenerate: true} @foreign_key_type :binary_id schema "organizations" do field :name, :string field :slug, :string - belongs_to :default_agent_token, Towerops.Agents.AgentToken + belongs_to :default_agent_token, AgentToken - has_many :memberships, Towerops.Organizations.Membership + has_many :memberships, Membership has_many :users, through: [:memberships, :user] - has_many :invitations, Towerops.Organizations.Invitation + has_many :invitations, Invitation timestamps(type: :utc_datetime) end + @type t :: %__MODULE__{ + id: Ecto.UUID.t(), + name: String.t(), + slug: String.t(), + default_agent_token_id: Ecto.UUID.t() | nil, + default_agent_token: NotLoaded.t() | AgentToken.t() | nil, + memberships: NotLoaded.t() | [Membership.t()], + users: NotLoaded.t() | [Towerops.Accounts.User.t()], + invitations: NotLoaded.t() | [Invitation.t()], + inserted_at: DateTime.t(), + updated_at: DateTime.t() + } + @doc false def changeset(organization, attrs) do organization diff --git a/lib/towerops/sites/site.ex b/lib/towerops/sites/site.ex index db386f1e..3c8a2f7a 100644 --- a/lib/towerops/sites/site.ex +++ b/lib/towerops/sites/site.ex @@ -4,6 +4,10 @@ defmodule Towerops.Sites.Site do import Ecto.Changeset + alias Ecto.Association.NotLoaded + alias Towerops.Agents.AgentToken + alias Towerops.Equipment.Equipment + alias Towerops.Organizations.Organization alias Towerops.Sites.Site @primary_key {:id, :binary_id, autogenerate: true} @@ -13,15 +17,32 @@ defmodule Towerops.Sites.Site do field :description, :string field :location, :string - belongs_to :organization, Towerops.Organizations.Organization - belongs_to :agent_token, Towerops.Agents.AgentToken + belongs_to :organization, Organization + belongs_to :agent_token, AgentToken belongs_to :parent_site, Site has_many :child_sites, Site, foreign_key: :parent_site_id - has_many :equipment, Towerops.Equipment.Equipment + has_many :equipment, Equipment timestamps(type: :utc_datetime) end + @type t :: %__MODULE__{ + id: Ecto.UUID.t(), + name: String.t(), + description: String.t() | nil, + location: String.t() | nil, + organization_id: Ecto.UUID.t(), + organization: NotLoaded.t() | Organization.t(), + agent_token_id: Ecto.UUID.t() | nil, + agent_token: NotLoaded.t() | AgentToken.t() | nil, + parent_site_id: Ecto.UUID.t() | nil, + parent_site: NotLoaded.t() | t() | nil, + child_sites: NotLoaded.t() | [t()], + equipment: NotLoaded.t() | [Equipment.t()], + inserted_at: DateTime.t(), + updated_at: DateTime.t() + } + @doc false def changeset(site, attrs) do site diff --git a/lib/towerops/snmp/discovery.ex b/lib/towerops/snmp/discovery.ex index 031c0d90..5dd302dd 100644 --- a/lib/towerops/snmp/discovery.ex +++ b/lib/towerops/snmp/discovery.ex @@ -109,15 +109,16 @@ defmodule Towerops.Snmp.Discovery do {:ok, device} <- save_discovery_results(equipment, device_info, interfaces, sensors), {:ok, neighbors} <- NeighborDiscovery.discover_neighbors(client_opts, device.interfaces), :ok <- save_neighbors(equipment.id, neighbors) do - update_equipment_discovery_time(equipment) + _ = update_equipment_discovery_time(equipment) Logger.info("SNMP discovery completed successfully for: #{equipment.name}") # Broadcast discovery completion for real-time updates - Phoenix.PubSub.broadcast( - Towerops.PubSub, - "equipment:#{equipment.id}", - {:discovery_completed, equipment.id} - ) + _ = + Phoenix.PubSub.broadcast( + Towerops.PubSub, + "equipment:#{equipment.id}", + {:discovery_completed, equipment.id} + ) {:ok, device} else @@ -278,9 +279,9 @@ defmodule Towerops.Snmp.Discovery do device = upsert_device(equipment, device_info) # Delete old interfaces/sensors and insert new ones - delete_old_data(device) + _ = delete_old_data(device) new_interfaces = insert_interfaces(device, interfaces) - insert_sensors(device, sensors) + _ = insert_sensors(device, sensors) # Return device with interfaces loaded and equipment_id added %{device | interfaces: Enum.map(new_interfaces, &Map.put(&1, :equipment_id, equipment.id))} diff --git a/lib/towerops/snmp/interface_stat.ex b/lib/towerops/snmp/interface_stat.ex index 9d250f88..38df6d99 100644 --- a/lib/towerops/snmp/interface_stat.ex +++ b/lib/towerops/snmp/interface_stat.ex @@ -4,6 +4,8 @@ defmodule Towerops.Snmp.InterfaceStat do import Ecto.Changeset + alias Towerops.Snmp.Interface + @primary_key {:id, :binary_id, autogenerate: true} @foreign_key_type :binary_id schema "snmp_interface_stats" do @@ -15,11 +17,25 @@ defmodule Towerops.Snmp.InterfaceStat do field :if_out_discards, :integer field :checked_at, :utc_datetime - belongs_to :interface, Towerops.Snmp.Interface + belongs_to :interface, Interface timestamps(type: :utc_datetime, updated_at: false) end + @type t :: %__MODULE__{ + id: Ecto.UUID.t(), + if_in_octets: integer() | nil, + if_out_octets: integer() | nil, + if_in_errors: integer() | nil, + if_out_errors: integer() | nil, + if_in_discards: integer() | nil, + if_out_discards: integer() | nil, + checked_at: DateTime.t(), + interface_id: Ecto.UUID.t(), + interface: Ecto.Association.NotLoaded.t() | Interface.t(), + inserted_at: DateTime.t() + } + @doc false def changeset(stat, attrs) do stat diff --git a/lib/towerops/snmp/poller_worker.ex b/lib/towerops/snmp/poller_worker.ex index 2aaa1423..3438efc0 100644 --- a/lib/towerops/snmp/poller_worker.ex +++ b/lib/towerops/snmp/poller_worker.ex @@ -53,7 +53,7 @@ defmodule Towerops.Snmp.PollerWorker do equipment = Equipment.get_equipment!(equipment_id) # Subscribe to discovery completion events for this equipment - Phoenix.PubSub.subscribe(Towerops.PubSub, "equipment:#{equipment_id}") + _ = Phoenix.PubSub.subscribe(Towerops.PubSub, "equipment:#{equipment_id}") if equipment.snmp_enabled do # Get the device to check if it has sensors/interfaces @@ -500,7 +500,13 @@ defmodule Towerops.Snmp.PollerWorker do defp broadcast_interface_events(events) do Enum.each(events, fn {:event, event_attrs} -> - Phoenix.PubSub.broadcast(Towerops.PubSub, "equipment:events", {:equipment_event, event_attrs}) + _ = + Phoenix.PubSub.broadcast( + Towerops.PubSub, + "equipment:events", + {:equipment_event, event_attrs} + ) + Logger.debug("Broadcast event: #{event_attrs.message}") end) end @@ -590,7 +596,7 @@ defmodule Towerops.Snmp.PollerWorker do s when s > 8 -> # Large binary, try reading last 8 bytes offset = s - 8 - <<_prefix::binary-size(offset), counter::unsigned-big-integer-size(64)>> = value + <<_prefix::binary-size(^offset), counter::unsigned-big-integer-size(64)>> = value counter _ -> @@ -896,7 +902,7 @@ defmodule Towerops.Snmp.PollerWorker do defp broadcast_sensor_events(events) do Enum.each(events, fn event -> - Phoenix.PubSub.broadcast(Towerops.PubSub, "equipment:events", {:equipment_event, event}) + _ = Phoenix.PubSub.broadcast(Towerops.PubSub, "equipment:events", {:equipment_event, event}) Logger.debug("Sensor event: #{event.message}") end) end diff --git a/lib/towerops/snmp/sensor_reading.ex b/lib/towerops/snmp/sensor_reading.ex index c4bcd966..289c4d3f 100644 --- a/lib/towerops/snmp/sensor_reading.ex +++ b/lib/towerops/snmp/sensor_reading.ex @@ -4,6 +4,8 @@ defmodule Towerops.Snmp.SensorReading do import Ecto.Changeset + alias Towerops.Snmp.Sensor + @primary_key {:id, :binary_id, autogenerate: true} @foreign_key_type :binary_id schema "snmp_sensor_readings" do @@ -11,11 +13,21 @@ defmodule Towerops.Snmp.SensorReading do field :status, :string field :checked_at, :utc_datetime - belongs_to :sensor, Towerops.Snmp.Sensor + belongs_to :sensor, Sensor timestamps(type: :utc_datetime, updated_at: false) end + @type t :: %__MODULE__{ + id: Ecto.UUID.t(), + value: float() | nil, + status: String.t(), + checked_at: DateTime.t(), + sensor_id: Ecto.UUID.t(), + sensor: Ecto.Association.NotLoaded.t() | Sensor.t(), + inserted_at: DateTime.t() + } + @doc false def changeset(reading, attrs) do reading diff --git a/lib/towerops_web/channels/agent_channel.ex b/lib/towerops_web/channels/agent_channel.ex index 9b483f2b..14be8812 100644 --- a/lib/towerops_web/channels/agent_channel.ex +++ b/lib/towerops_web/channels/agent_channel.ex @@ -356,7 +356,6 @@ defmodule ToweropsWeb.AgentChannel do defp parse_integer(_), do: nil - defp parse_float(nil), do: nil defp parse_float(value) when is_float(value), do: value defp parse_float(value) when is_integer(value), do: value / 1.0 diff --git a/lib/towerops_web/live/equipment_live/form.ex b/lib/towerops_web/live/equipment_live/form.ex index 93700a79..2147ec44 100644 --- a/lib/towerops_web/live/equipment_live/form.ex +++ b/lib/towerops_web/live/equipment_live/form.ex @@ -149,9 +149,10 @@ defmodule ToweropsWeb.EquipmentLive.Form do if equipment.snmp_enabled do # Run discovery in background - Task.start(fn -> - Snmp.discover_equipment(equipment) - end) + _ = + Task.start(fn -> + Snmp.discover_equipment(equipment) + end) {:noreply, put_flash(socket, :info, "Discovery started...")} else @@ -207,7 +208,7 @@ defmodule ToweropsWeb.EquipmentLive.Form do defp handle_equipment_creation(equipment) do if equipment.snmp_enabled do - Task.start(fn -> Snmp.discover_equipment(equipment) end) + _ = Task.start(fn -> Snmp.discover_equipment(equipment) end) "Equipment created successfully. SNMP discovery started in background." else "Equipment created successfully" @@ -216,7 +217,7 @@ defmodule ToweropsWeb.EquipmentLive.Form do defp handle_equipment_update(old_equipment, equipment) do if should_trigger_snmp_discovery?(old_equipment, equipment) do - Task.start(fn -> Snmp.discover_equipment(equipment) end) + _ = Task.start(fn -> Snmp.discover_equipment(equipment) end) "Equipment updated successfully. SNMP discovery started in background." else "Equipment updated successfully" diff --git a/lib/towerops_web/live/equipment_live/show.ex b/lib/towerops_web/live/equipment_live/show.ex index a9350cae..bcff382a 100644 --- a/lib/towerops_web/live/equipment_live/show.ex +++ b/lib/towerops_web/live/equipment_live/show.ex @@ -13,10 +13,11 @@ defmodule ToweropsWeb.EquipmentLive.Show do @impl true def handle_params(%{"id" => id} = params, _, socket) do - if connected?(socket) do - Phoenix.PubSub.subscribe(Towerops.PubSub, "equipment:#{id}") - Process.send_after(self(), :refresh_data, 10_000) - end + _ = + if connected?(socket) do + _ = Phoenix.PubSub.subscribe(Towerops.PubSub, "equipment:#{id}") + Process.send_after(self(), :refresh_data, 10_000) + end tab = Map.get(params, "tab", "overview") diff --git a/lib/towerops_web/live/graph_live/show.ex b/lib/towerops_web/live/graph_live/show.ex index bc536039..9af16cd8 100644 --- a/lib/towerops_web/live/graph_live/show.ex +++ b/lib/towerops_web/live/graph_live/show.ex @@ -12,9 +12,10 @@ defmodule ToweropsWeb.GraphLive.Show do @impl true def handle_params(%{"id" => equipment_id, "sensor_type" => sensor_type} = params, _, socket) do - if connected?(socket) do - Phoenix.PubSub.subscribe(Towerops.PubSub, "equipment:#{equipment_id}") - end + _ = + if connected?(socket) do + Phoenix.PubSub.subscribe(Towerops.PubSub, "equipment:#{equipment_id}") + end range = Map.get(params, "range", "24h") interface_id = Map.get(params, "interface_id") diff --git a/lib/towerops_web/live/mobile_qr_live.ex b/lib/towerops_web/live/mobile_qr_live.ex index 04aa80cd..08fcf7e0 100644 --- a/lib/towerops_web/live/mobile_qr_live.ex +++ b/lib/towerops_web/live/mobile_qr_live.ex @@ -17,9 +17,10 @@ defmodule ToweropsWeb.MobileQRLive do {:ok, qr_token} = MobileSessions.create_qr_login_token(user.id) # Start polling to check if token has been completed - if connected?(socket) do - schedule_check() - end + _ = + if connected?(socket) do + schedule_check() + end socket = socket diff --git a/lib/towerops_web/plugs/mobile_auth.ex b/lib/towerops_web/plugs/mobile_auth.ex index f850a8ec..65adce79 100644 --- a/lib/towerops_web/plugs/mobile_auth.ex +++ b/lib/towerops_web/plugs/mobile_auth.ex @@ -21,7 +21,7 @@ defmodule ToweropsWeb.Plugs.MobileAuth do {:ok, session} <- validate_session(token), {:ok, user} <- get_user(session.user_id) do # Touch the session to update last_used_at - Task.start(fn -> MobileSessions.touch_session(session) end) + _ = Task.start(fn -> MobileSessions.touch_session(session) end) conn |> assign(:current_user, user) @@ -59,5 +59,4 @@ defmodule ToweropsWeb.Plugs.MobileAuth do defp error_message(:missing_token), do: "Authorization header is missing or invalid" defp error_message(:invalid_token), do: "Invalid or expired authentication token" defp error_message(:user_not_found), do: "User not found" - defp error_message(_), do: "Authentication failed" end diff --git a/lib/towerops_web/telemetry.ex b/lib/towerops_web/telemetry.ex index b77de05d..04994c91 100644 --- a/lib/towerops_web/telemetry.ex +++ b/lib/towerops_web/telemetry.ex @@ -11,19 +11,21 @@ defmodule ToweropsWeb.Telemetry do @impl true def init(_arg) do # Attach telemetry handlers for logging request failures - :telemetry.attach( - "towerops-router-exception", - [:phoenix, :router_dispatch, :exception], - &__MODULE__.handle_router_exception/4, - nil - ) + _ = + :telemetry.attach( + "towerops-router-exception", + [:phoenix, :router_dispatch, :exception], + &__MODULE__.handle_router_exception/4, + nil + ) - :telemetry.attach( - "towerops-endpoint-stop", - [:phoenix, :endpoint, :stop], - &__MODULE__.handle_endpoint_stop/4, - nil - ) + _ = + :telemetry.attach( + "towerops-endpoint-stop", + [:phoenix, :endpoint, :stop], + &__MODULE__.handle_endpoint_stop/4, + nil + ) children = [ # Telemetry poller will execute the given period measurements