towerops/lib/towerops/sites.ex
Graham McIntire 1bd72dac78
1. Sites Management
- Sites schema with hierarchical parent-child relationships
  - CRUD operations for sites
  - Sites context with helper functions
  - LiveView pages:
    - /orgs/:slug/sites - List all sites
    - /orgs/:slug/sites/new - Create new site
    - /orgs/:slug/sites/:id - View site details
    - /orgs/:slug/sites/:id/edit - Edit site
  - Support for site locations and descriptions
  - Site tree builder for hierarchy visualization

  2. Equipment Management
  - Equipment schema with monitoring fields
  - IP address validation (IPv4 & IPv6)
  - Equipment status tracking (up/down/unknown)
  - Customizable check intervals per equipment
  - Equipment context with CRUD operations
  - LiveView pages:
    - /orgs/:slug/equipment - List all equipment
    - /orgs/:slug/equipment/new - Add equipment
    - /orgs/:slug/equipment/:id - View equipment details
    - /orgs/:slug/equipment/:id/edit - Edit equipment
  - Equipment can be added from site pages
  - Status badges and last checked timestamps

  3. Database Schema
  - sites table with self-referencing parent_site_id
  - equipment table with status tracking
  - All migrations run successfully
  - Proper indexes on foreign keys and status

  4. Features Implemented
  -  IP address validation using :inet.parse_address
  -  Site hierarchy with parent-child relationships
  -  Equipment linked to sites
  -  Monitoring enabled/disabled per equipment
  -  Customizable check intervals (30s - 3600s)
  -  Status tracking (up/down/unknown)
  -  Timestamps for last check and last status change
  -  Organization-scoped data (users only see their org's data)
2025-12-21 13:39:19 -06:00

114 lines
2.5 KiB
Elixir

defmodule Towerops.Sites do
@moduledoc """
The Sites context.
"""
import Ecto.Query
alias Towerops.Repo
alias Towerops.Sites.Site
@doc """
Returns the list of sites for an organization.
"""
def list_organization_sites(organization_id) do
Repo.all(
from(s in Site, where: s.organization_id == ^organization_id, order_by: [asc: s.name], preload: [:parent_site])
)
end
@doc """
Returns the list of root sites (sites without a parent) for an organization.
"""
def list_root_sites(organization_id) do
Repo.all(
from(s in Site,
where: s.organization_id == ^organization_id,
where: is_nil(s.parent_site_id),
order_by: [asc: s.name]
)
)
end
@doc """
Returns the list of child sites for a parent site.
"""
def list_child_sites(parent_site_id) do
Repo.all(from(s in Site, where: s.parent_site_id == ^parent_site_id, order_by: [asc: s.name]))
end
@doc """
Gets a single site.
"""
def get_site!(id) do
Site
|> Repo.get!(id)
|> Repo.preload([:parent_site, :child_sites, :equipment])
end
@doc """
Gets a single site belonging to an organization.
"""
def get_organization_site!(organization_id, site_id) do
Repo.one!(
from(s in Site,
where: s.id == ^site_id,
where: s.organization_id == ^organization_id,
preload: [:parent_site, :child_sites, :equipment]
)
)
end
@doc """
Creates a site.
"""
def create_site(attrs) do
%Site{}
|> Site.changeset(attrs)
|> Repo.insert()
end
@doc """
Updates a site.
"""
def update_site(%Site{} = site, attrs) do
site
|> Site.changeset(attrs)
|> Repo.update()
end
@doc """
Deletes a site.
"""
def delete_site(%Site{} = site) do
Repo.delete(site)
end
@doc """
Returns an `%Ecto.Changeset{}` for tracking site changes.
"""
def change_site(%Site{} = site, attrs \\ %{}) do
Site.changeset(site, attrs)
end
@doc """
Builds a hierarchical tree structure of sites.
"""
def build_site_tree(organization_id) do
sites = list_organization_sites(organization_id)
root_sites = Enum.filter(sites, &is_nil(&1.parent_site_id))
Enum.map(root_sites, fn root ->
build_tree_node(root, sites)
end)
end
defp build_tree_node(site, all_sites) do
children =
all_sites
|> Enum.filter(&(&1.parent_site_id == site.id))
|> Enum.map(&build_tree_node(&1, all_sites))
%{site: site, children: children}
end
end