Files
frajtano/lib/agent.ex
2024-09-21 17:17:01 +01:00

80 lines
1.9 KiB
Elixir

defmodule Frajtano.Agent do
alias Frajtano.Peer
use GenServer
def start_link(args) do
GenServer.start_link(__MODULE__, nil, [{:name, __MODULE__} | args])
end
@impl true
def init(_) do
{
:ok,
%{},
{:continue, :init_peers}
}
end
@impl true
def handle_continue(:init_peers, state) do
for peer <- Application.fetch_env!(:frajtano, :initial_peers), do: {:ok, _} = Peer.start(peer)
{:noreply, state}
end
# select: list of specs, where specs are a tuple of match, guards, and outputs
# match is {key, pid, value}, :"$1" is a match variable
def peer_paths() do
Registry.select(Frajtano.Peers, [{{:"$1", :_, :_}, [], [:"$1"]}])
end
def peer_pids() do
Registry.select(Frajtano.Peers, [{{:_, :"$1", :_}, [], [:"$1"]}])
end
@impl true
def handle_call({:identities}, _from, _state) do
idents =
Task.async_stream(
peer_pids(),
&{&1, Peer.identities(&1)},
ordered: false,
on_timeout: :kill_task
)
idents = for {:ok, {peer, {:ok, idents}}} <- idents, do: {idents, peer}
{
:reply,
{:ok, Enum.flat_map(idents, &elem(&1, 0))},
for({idents, peer} <- idents, {key, _comment} <- idents, into: %{}, do: {key, peer})
}
end
@impl true
def handle_call({:sign, {key, _, _} = req}, _from, state) do
{:reply, Peer.sign(state[key], req), state}
end
@impl true
def handle_call({:add_peer, path}, _from, state) do
# TODO: deduplicate peers by socket path
case Peer.start(path) do
{:ok, _} -> {:reply, :ok, state}
{:error, error} -> {:reply, {:error, error}, state}
end
end
def identities() do
GenServer.call(__MODULE__, {:identities})
end
def sign(request) do
# Signing can take some time, as a password may need to be entered or similar
GenServer.call(__MODULE__, {:sign, request}, :infinity)
end
def add_peer(path) do
GenServer.call(__MODULE__, {:add_peer, path})
end
end