129 lines
3.3 KiB
Elixir
129 lines
3.3 KiB
Elixir
defmodule GeoTherminator.PumpAPI.Auth.Server do
|
|
require Logger
|
|
require GeoTherminator.PumpAPI.Auth.User
|
|
require GeoTherminator.PumpAPI.Auth.InstallationInfo
|
|
require GeoTherminator.PumpAPI.Auth.Tokens
|
|
|
|
use GenServer
|
|
import GeoTherminator.TypedStruct
|
|
alias GeoTherminator.PumpAPI.Auth
|
|
|
|
@token_check_timer 10_000
|
|
@token_check_diff 5 * 60
|
|
|
|
defmodule Options do
|
|
deftypedstruct(%{
|
|
server_name: GenServer.name(),
|
|
username: String.t(),
|
|
password: String.t()
|
|
})
|
|
end
|
|
|
|
defmodule State do
|
|
deftypedstruct(%{
|
|
username: String.t(),
|
|
password: String.t(),
|
|
authed_user: {Auth.User.t() | nil, nil},
|
|
installations_fetched: {boolean(), false},
|
|
installations: {[Auth.InstallationInfo.t()], []}
|
|
})
|
|
end
|
|
|
|
@spec start_link(Options.t()) :: GenServer.on_start()
|
|
def start_link(opts) do
|
|
GenServer.start_link(__MODULE__, opts, name: opts.server_name)
|
|
end
|
|
|
|
@impl true
|
|
def init(%Options{} = opts) do
|
|
case init_state(opts.username, opts.password) do
|
|
{:ok, state} -> {:ok, state}
|
|
:error -> {:stop, :error}
|
|
end
|
|
end
|
|
|
|
@impl true
|
|
def handle_call(msg, from, state)
|
|
|
|
def handle_call(:get_auth, _from, state) do
|
|
{:reply, state.authed_user, state}
|
|
end
|
|
|
|
def handle_call(:get_installations, _from, state) do
|
|
{:reply, state.installations, state}
|
|
end
|
|
|
|
def handle_call({:get_installation, id}, _from, state) do
|
|
{:reply, Enum.find(state.installations, &(Auth.InstallationInfo.record(&1, :id) == id)),
|
|
state}
|
|
end
|
|
|
|
@impl true
|
|
def handle_info(msg, state)
|
|
|
|
def handle_info(:token_check, state) do
|
|
now = DateTime.utc_now()
|
|
|
|
diff =
|
|
DateTime.diff(
|
|
state.authed_user
|
|
|> Auth.User.record(:tokens)
|
|
|> Auth.Tokens.record(:access_token_expiry),
|
|
now
|
|
)
|
|
|
|
schedule_token_check()
|
|
|
|
if diff < @token_check_diff do
|
|
Logger.debug("Renewing auth token since #{diff} < #{@token_check_diff}")
|
|
|
|
case init_state(state.username, state.password) do
|
|
{:ok, new_state} -> {:noreply, new_state}
|
|
:error -> {:noreply, state}
|
|
end
|
|
else
|
|
{:noreply, state}
|
|
end
|
|
end
|
|
|
|
@spec get_auth(GenServer.name()) :: Auth.User.t()
|
|
def get_auth(server) do
|
|
GenServer.call(server, :get_auth)
|
|
end
|
|
|
|
@spec get_installations(GenServer.name()) :: Auth.InstallationInfo.t()
|
|
def get_installations(server) do
|
|
GenServer.call(server, :get_installations)
|
|
end
|
|
|
|
@spec get_installation(GenServer.name(), integer()) :: Auth.InstallationInfo.t() | nil
|
|
def get_installation(server, id) do
|
|
GenServer.call(server, {:get_installation, id})
|
|
end
|
|
|
|
defp schedule_token_check() do
|
|
Process.send_after(self(), :token_check, @token_check_timer)
|
|
end
|
|
|
|
@spec init_state(String.t(), String.t()) :: {:ok, State.t()} | {:stop, :error}
|
|
defp init_state(username, password) do
|
|
with {:ok, user} <- :pump_api@auth@api.auth(username, password),
|
|
{:ok, installations} <- :pump_api@auth@api.installation_info(user) do
|
|
schedule_token_check()
|
|
|
|
state = %State{
|
|
authed_user: user,
|
|
installations: installations,
|
|
installations_fetched: true,
|
|
username: username,
|
|
password: password
|
|
}
|
|
|
|
{:ok, state}
|
|
else
|
|
err ->
|
|
Logger.error("Could not auth or fetch installations! #{inspect(err)}")
|
|
:error
|
|
end
|
|
end
|
|
end
|