towerops/lib/towerops/integrations/integration.ex
Graham McIntire de075ea8d9
Store billing subscriber/MRR data on integrations for dashboard
Each billing sync (Gaiia, Splynx, VISP, Sonar) already computes
subscriber counts and MRR but only stored them in sync messages.
Now persists them on the integrations table so the dashboard can
aggregate across all billing providers instead of only querying
Gaiia network sites (which were never populated).

Also fixes SnmpKit Config environment detection to use Mix.env()
as fallback when MIX_ENV env var is not set.
2026-02-15 16:51:33 -06:00

60 lines
1.7 KiB
Elixir

defmodule Towerops.Integrations.Integration do
@moduledoc """
Schema for per-organization third-party integrations.
Each organization can have one integration per provider. Credentials
are encrypted at rest using Cloak AES-256-GCM.
"""
use Ecto.Schema
import Ecto.Changeset
alias Towerops.Encrypted
@primary_key {:id, :binary_id, autogenerate: true}
@foreign_key_type :binary_id
@valid_providers ~w(preseem gaiia pagerduty netbox sonar splynx visp)
@valid_sync_statuses ~w(never success partial failed)
schema "integrations" do
field :provider, :string
field :enabled, :boolean, default: false
field :credentials, Encrypted.Map
field :sync_interval_minutes, :integer, default: 10
field :last_synced_at, :utc_datetime
field :last_sync_status, :string, default: "never"
field :last_sync_message, :string
field :subscriber_count, :integer, default: 0
field :total_mrr, :decimal
belongs_to :organization, Towerops.Organizations.Organization
timestamps(type: :utc_datetime)
end
def changeset(integration, attrs) do
integration
|> cast(attrs, [
:organization_id,
:provider,
:enabled,
:credentials,
:sync_interval_minutes,
:last_synced_at,
:last_sync_status,
:last_sync_message,
:subscriber_count,
:total_mrr
])
|> validate_required([:organization_id, :provider])
|> validate_inclusion(:provider, @valid_providers)
|> validate_inclusion(:last_sync_status, @valid_sync_statuses)
|> validate_number(:sync_interval_minutes, greater_than: 0)
|> unique_constraint([:organization_id, :provider],
error_key: :provider,
message: "has already been taken"
)
|> foreign_key_constraint(:organization_id)
end
end