Beacons: - Scaffolded with phx.gen.live then reworked so reads are public and mutations go through a live_session gated by the new :require_admin on_mount hook in UserAuth - Beacon schema stores frequency (MHz), callsign, grid, lat/lon, power (W), and height above ground (m); grid auto-derives from lat/lon when left blank via Maidenhead.from_latlon - Adds Maidenhead.from_latlon/3 so we can compute grids locally instead of hitting an external API Users admin page: - /users and /users/:id/edit (admin-only) for listing, editing (callsign/name/email/is_admin), and deleting other users - Adds Accounts.list_users, admin_update_user, delete_user, and a dedicated admin_changeset on the User schema - Nav gains a "Users" link for admins and a "Beacons" link for everyone
80 lines
2 KiB
Elixir
80 lines
2 KiB
Elixir
defmodule Microwaveprop.Beacons do
|
|
@moduledoc """
|
|
The Beacons context. Beacon records are public (anyone can read)
|
|
but only admin users can create/update/delete them — that gating
|
|
is enforced by the router's live_session for the form views.
|
|
"""
|
|
|
|
import Ecto.Query, warn: false
|
|
|
|
alias Microwaveprop.Accounts.User
|
|
alias Microwaveprop.Beacons.Beacon
|
|
alias Microwaveprop.Repo
|
|
|
|
@topic "beacons"
|
|
|
|
@doc """
|
|
Subscribes to beacon change notifications. Messages:
|
|
|
|
* `{:created, %Beacon{}}`
|
|
* `{:updated, %Beacon{}}`
|
|
* `{:deleted, %Beacon{}}`
|
|
"""
|
|
def subscribe_beacons do
|
|
Phoenix.PubSub.subscribe(Microwaveprop.PubSub, @topic)
|
|
end
|
|
|
|
defp broadcast(message) do
|
|
Phoenix.PubSub.broadcast(Microwaveprop.PubSub, @topic, message)
|
|
end
|
|
|
|
@doc "Returns all beacons ordered by frequency."
|
|
def list_beacons do
|
|
Repo.all(from b in Beacon, order_by: [asc: b.frequency_mhz, asc: b.callsign])
|
|
end
|
|
|
|
@doc "Gets a single beacon. Raises if not found."
|
|
def get_beacon!(id), do: Repo.get!(Beacon, id)
|
|
|
|
@doc """
|
|
Creates a beacon. The user is recorded on the row as the creator.
|
|
"""
|
|
def create_beacon(%User{} = user, attrs) do
|
|
%Beacon{user_id: user.id}
|
|
|> Beacon.changeset(attrs)
|
|
|> Repo.insert()
|
|
|> broadcast_if_ok(:created)
|
|
end
|
|
|
|
@doc "Updates a beacon."
|
|
def update_beacon(%Beacon{} = beacon, attrs) do
|
|
beacon
|
|
|> Beacon.changeset(attrs)
|
|
|> Repo.update()
|
|
|> broadcast_if_ok(:updated)
|
|
end
|
|
|
|
@doc "Deletes a beacon."
|
|
def delete_beacon(%Beacon{} = beacon) do
|
|
case Repo.delete(beacon) do
|
|
{:ok, beacon} ->
|
|
broadcast({:deleted, beacon})
|
|
{:ok, beacon}
|
|
|
|
other ->
|
|
other
|
|
end
|
|
end
|
|
|
|
@doc "Returns an `%Ecto.Changeset{}` for tracking beacon changes."
|
|
def change_beacon(%Beacon{} = beacon, attrs \\ %{}) do
|
|
Beacon.changeset(beacon, attrs)
|
|
end
|
|
|
|
defp broadcast_if_ok({:ok, beacon} = result, type) do
|
|
broadcast({type, beacon})
|
|
result
|
|
end
|
|
|
|
defp broadcast_if_ok(other, _type), do: other
|
|
end
|