defmodule Prymn.Servers do @moduledoc """ The Servers context. """ import Ecto.Query, warn: false alias Prymn.Repo alias Prymn.Servers.Server @doc """ Returns the list of servers. ## Examples iex> list_servers() [%Server{}, ...] """ def list_servers do Repo.all(Server |> order_by(desc: :inserted_at)) end def list_registered_servers() do query = from s in Server, select: s, where: s.status == :registered Repo.all(query) end @doc """ Gets a single server. Raises `Ecto.NoResultsError` if the Server does not exist. ## Examples iex> get_server!(123) %Server{} iex> get_server!(456) ** (Ecto.NoResultsError) """ def get_server!(id), do: Repo.get!(Server, id) @doc """ Get a single server by its IP. """ @spec get_server_by_ip!(String.t()) :: Server.t() def get_server_by_ip!(ip), do: Repo.get_by!(Server, public_ip: ip) @doc """ Start a new server connection with the app. """ def create_server(attrs \\ %{}) do # Create a unique registration token %Server{registration_token: :crypto.strong_rand_bytes(16)} |> Server.changeset(attrs) |> Repo.insert() end @doc """ Registers a server using a registration token. """ def register_server(token, public_ip) do with true <- :inet.is_ip_address(public_ip), {:ok, token} <- Base.decode64(token) do public_ip_string = public_ip |> :inet.ntoa() |> to_string() from(s in Server, where: s.registration_token == ^token, select: s) |> Repo.one!() |> update_server(%{public_ip: public_ip_string, status: :registered}) else false -> {:error, :invalid_ip} :error -> {:error, :bad_token} end end @doc """ Updates a server. ## Examples iex> update_server(server, %{field: new_value}) {:ok, %Server{}} iex> update_server(server, %{field: bad_value}) {:error, %Ecto.Changeset{}} """ def update_server(%Server{} = server, attrs) do server |> Server.changeset(attrs) |> Repo.update() end @doc """ Deletes a server. ## Examples iex> delete_server(server) {:ok, %Server{}} iex> delete_server(server) {:error, %Ecto.Changeset{}} """ def delete_server(%Server{} = server) do Repo.delete(server) end @doc """ Returns an `%Ecto.Changeset{}` for tracking server changes. ## Examples iex> change_server(server) %Ecto.Changeset{data: %Server{}} """ def change_server(%Server{} = server, attrs \\ %{}) do Server.changeset(server, attrs) end @doc """ Returns a string containing the command that needs to be executed to the remote server in order to register it to the backend. """ @spec create_setup_command(Server.t()) :: String.t() def create_setup_command(%Server{registration_token: token}) do token |> Base.encode64() |> then(fn token -> "wget -O- " <> PrymnWeb.Endpoint.url() <> "/install | sudo sh -s " <> token end) end end