diff --git a/backend/lib/prymn/servers.ex b/backend/lib/prymn/servers.ex
index d6d5bef..42aeff0 100644
--- a/backend/lib/prymn/servers.ex
+++ b/backend/lib/prymn/servers.ex
@@ -38,24 +38,33 @@ defmodule Prymn.Servers do
def get_server!(id), do: Repo.get!(Server, id)
@doc """
- Creates a server.
-
- ## Examples
-
- iex> create_server(%{field: value})
- {:ok, %Server{}}
-
- iex> create_server(%{field: bad_value})
- {:error, %Ecto.Changeset{}}
+ Start a new server connection with the app.
"""
def create_server(attrs \\ %{}) do
- # FIXME: Maybe use a cryptographically secure token (if UUID v4 is not one)?
- %Server{connection_token: Ecto.UUID.generate()}
+ # 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
+ # TODO: Validate public ip
+
+ case Base.decode64(token) do
+ {:ok, token} ->
+ from(s in Server, where: s.registration_token == ^token, select: s)
+ |> Repo.one()
+ |> update_server(%{public_ip: public_ip})
+
+ :error ->
+ {:error, "token is not a valid base64 encoded string"}
+ end
+ end
+
@doc """
Updates a server.
diff --git a/backend/lib/prymn/servers/server.ex b/backend/lib/prymn/servers/server.ex
index 21b22ac..ae3dc11 100644
--- a/backend/lib/prymn/servers/server.ex
+++ b/backend/lib/prymn/servers/server.ex
@@ -4,10 +4,13 @@ defmodule Prymn.Servers.Server do
schema "servers" do
field :name, :string
- field :ipv4, :string
- field :ipv6, :string
+ field :public_ip, :string
field :provider, Ecto.Enum, values: [:Hetzner, :Custom]
- field :connection_token, Ecto.UUID, redact: true
+ field :registration_token, :binary, redact: true
+
+ field :connection_status, Ecto.Enum,
+ values: [:awaiting, :connecting, :installing, :connected, :disconnected],
+ default: :awaiting
timestamps()
end
@@ -15,8 +18,8 @@ defmodule Prymn.Servers.Server do
@doc false
def changeset(server, attrs) do
server
- |> cast(attrs, [:name, :provider])
- |> validate_required([:name, :provider, :connection_token])
+ |> cast(attrs, [:name, :provider, :registration_token])
+ |> validate_required([:name, :provider])
|> validate_inclusion(:provider, [:Custom], message: "Provider not available (yet)")
end
end
diff --git a/backend/lib/prymn_web/components/core_components.ex b/backend/lib/prymn_web/components/core_components.ex
index f9c99ca..c3830e6 100644
--- a/backend/lib/prymn_web/components/core_components.ex
+++ b/backend/lib/prymn_web/components/core_components.ex
@@ -477,7 +477,7 @@ defmodule PrymnWeb.CoreComponents do
class="relative w-min p-0 hover:cursor-pointer"
phx-click={@row_click && @row_click.(row)}
>
-
+
<%= @row_indicator.(row) %>
diff --git a/backend/lib/prymn_web/controllers/server_controller.ex b/backend/lib/prymn_web/controllers/server_controller.ex
new file mode 100644
index 0000000..e0b5e92
--- /dev/null
+++ b/backend/lib/prymn_web/controllers/server_controller.ex
@@ -0,0 +1,19 @@
+defmodule PrymnWeb.ServerController do
+ use PrymnWeb, :controller
+
+ alias Prymn.Servers
+
+ @doc """
+ Used by clients to request a new server connection to the prymn backend
+ validating their registration token.
+ """
+ def register(conn, %{"token" => token, "ip" => ip}) do
+ case Servers.register_server(token, ip) do
+ {:ok, _server} ->
+ json(conn, %{"connected" => true})
+
+ {:error, error} ->
+ raise inspect(error)
+ end
+ end
+end
diff --git a/backend/lib/prymn_web/live/server_live/index.ex b/backend/lib/prymn_web/live/server_live/index.ex
index 99ddd39..d896183 100644
--- a/backend/lib/prymn_web/live/server_live/index.ex
+++ b/backend/lib/prymn_web/live/server_live/index.ex
@@ -24,11 +24,13 @@ defmodule PrymnWeb.ServerLive.Index do
end
@impl true
- def handle_info({:connect, server}, socket) do
- # TODO: Connect the new server:
- # For custom connections: will need to generate a new prompt for the user to execute
- # For API connections: the "prompt" will be automatic, but same execution server-side
- {:noreply, stream_insert(socket, :servers, server)}
+ def handle_info({:connect, %Servers.Server{} = server}, socket) do
+ socket =
+ if server.provider == :Custom,
+ do: push_navigate(socket, to: ~p"/servers/#{server}"),
+ else: stream_insert(socket, :servers, server)
+
+ {:noreply, socket}
end
@impl true
diff --git a/backend/lib/prymn_web/live/server_live/index.html.heex b/backend/lib/prymn_web/live/server_live/index.html.heex
index 3f38d52..d10c240 100644
--- a/backend/lib/prymn_web/live/server_live/index.html.heex
+++ b/backend/lib/prymn_web/live/server_live/index.html.heex
@@ -12,15 +12,20 @@
rows={@streams.servers}
row_click={fn {_id, server} -> JS.navigate(~p"/servers/#{server}") end}
row_indicator={
- fn {_id, _server} ->
- ~H(
Connecting)
+ fn
+ {_id, %Servers.Server{connection_status: :awaiting}} ->
+ ~H(
Awaiting connection)
+
+ {_id, %Servers.Server{connection_status: :connecting}} ->
+ ~H(
Connecting)
+
+ {_id, %Servers.Server{connection_status: :connected}} ->
+ ~H(
Connected)
end
}
indicator_label="Status"
>
<:col :let={{_id, server}} label="Name"><%= server.name %>
- <:col :let={{_id, server}} label="IPv4"><%= server.ipv4 || "N/A" %>
- <:col :let={{_id, server}} label="IPv6"><%= server.ipv6 || "N/A" %>
<:action :let={{id, server}}>
<.link
phx-click={JS.push("delete", value: %{id: server.id}) |> hide("##{id}")}
diff --git a/backend/lib/prymn_web/live/server_live/show.html.heex b/backend/lib/prymn_web/live/server_live/show.html.heex
index 604a5f9..a21038a 100644
--- a/backend/lib/prymn_web/live/server_live/show.html.heex
+++ b/backend/lib/prymn_web/live/server_live/show.html.heex
@@ -1,18 +1,25 @@
<.header>
- <%= @server.name %>
- <:subtitle>
-
IPv4: <%= @server.ipv4 || "Not available" %>
-
IPv6: <%= @server.ipv6 %>
-
- <:actions>
- <.button>
- New site
-
-
+ Server <%= @server.name %>
-
-
Sites
-
+
+
+ Connect to your server using root credentials and execute the following command:
+
+
+
+ #
+
+ your command here that contains token: <%= Base.encode64(@server.registration_token) %>
+
+
+
+
+
<.back navigate={~p"/servers"}>Back to servers
diff --git a/backend/lib/prymn_web/router.ex b/backend/lib/prymn_web/router.ex
index 5cb50c4..0eaff59 100644
--- a/backend/lib/prymn_web/router.ex
+++ b/backend/lib/prymn_web/router.ex
@@ -25,10 +25,11 @@ defmodule PrymnWeb.Router do
live "/servers/:id", ServerLive.Show
end
- # Other scopes may use custom stacks.
- # scope "/api", PrymnWeb do
- # pipe_through :api
- # end
+ scope "/api/v1", PrymnWeb do
+ pipe_through :api
+
+ post "/servers/register", ServerController, :register
+ end
# Enable LiveDashboard and Swoosh mailbox preview in development
if Application.compile_env(:prymn, :dev_routes) do
diff --git a/backend/priv/repo/migrations/20230609164352_create_servers.exs b/backend/priv/repo/migrations/20230609164352_create_servers.exs
index 93e046b..b097bad 100644
--- a/backend/priv/repo/migrations/20230609164352_create_servers.exs
+++ b/backend/priv/repo/migrations/20230609164352_create_servers.exs
@@ -2,14 +2,16 @@ defmodule Prymn.Repo.Migrations.CreateServers do
use Ecto.Migration
def change do
- create table(:servers) do
+ create table("servers") do
add :name, :string
- add :ipv4, :string
- add :ipv6, :string
+ add :public_ip, :string
add :provider, :string
- add :connection_token, :binary
+ add :connection_status, :string
+ add :registration_token, :binary
timestamps()
end
+
+ create index("servers", [:registration_token], unique: true)
end
end