web: new server form
This commit is contained in:
		
							parent
							
								
									b5cab545e6
								
							
						
					
					
						commit
						52b892bb4f
					
				
					 12 changed files with 139 additions and 185 deletions
				
			
		| 
						 | 
				
			
			@ -50,7 +50,8 @@ defmodule Prymn.Servers do
 | 
			
		|||
 | 
			
		||||
  """
 | 
			
		||||
  def create_server(attrs \\ %{}) do
 | 
			
		||||
    %Server{}
 | 
			
		||||
    # FIXME: Maybe use a cryptographically secure token (if UUID v4 is not one)?
 | 
			
		||||
    %Server{connection_token: Ecto.UUID.generate()}
 | 
			
		||||
    |> Server.changeset(attrs)
 | 
			
		||||
    |> Repo.insert()
 | 
			
		||||
  end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,6 +6,8 @@ defmodule Prymn.Servers.Server do
 | 
			
		|||
    field :name, :string
 | 
			
		||||
    field :ipv4, :string
 | 
			
		||||
    field :ipv6, :string
 | 
			
		||||
    field :provider, Ecto.Enum, values: [:Hetzner, :Custom]
 | 
			
		||||
    field :connection_token, Ecto.UUID, redact: true
 | 
			
		||||
 | 
			
		||||
    timestamps()
 | 
			
		||||
  end
 | 
			
		||||
| 
						 | 
				
			
			@ -13,7 +15,8 @@ defmodule Prymn.Servers.Server do
 | 
			
		|||
  @doc false
 | 
			
		||||
  def changeset(server, attrs) do
 | 
			
		||||
    server
 | 
			
		||||
    |> cast(attrs, [:name])
 | 
			
		||||
    |> validate_required([:name])
 | 
			
		||||
    |> cast(attrs, [:name, :provider])
 | 
			
		||||
    |> validate_required([:name, :provider, :connection_token])
 | 
			
		||||
    |> validate_inclusion(:provider, [:Custom], message: "Provider not available (yet)")
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -436,6 +436,8 @@ defmodule PrymnWeb.CoreComponents do
 | 
			
		|||
  attr :rows, :list, required: true
 | 
			
		||||
  attr :row_id, :any, default: nil, doc: "the function for generating the row id"
 | 
			
		||||
  attr :row_click, :any, default: nil, doc: "the function for handling phx-click on each row"
 | 
			
		||||
  attr :row_indicator, :any, default: nil
 | 
			
		||||
  attr :indicator_label, :string, default: "Indicator"
 | 
			
		||||
 | 
			
		||||
  attr :row_item, :any,
 | 
			
		||||
    default: &Function.identity/1,
 | 
			
		||||
| 
						 | 
				
			
			@ -458,6 +460,9 @@ defmodule PrymnWeb.CoreComponents do
 | 
			
		|||
      <table class="w-[40rem] mt-11 sm:w-full">
 | 
			
		||||
        <thead class="text-left text-sm leading-6 text-zinc-500">
 | 
			
		||||
          <tr>
 | 
			
		||||
            <th class="p-0 pb-4 font-normal">
 | 
			
		||||
              <div class="w-min"><%= @indicator_label %></div>
 | 
			
		||||
            </th>
 | 
			
		||||
            <th :for={col <- @col} class="p-0 pr-6 pb-4 font-normal"><%= col[:label] %></th>
 | 
			
		||||
            <th class="relative p-0 pb-4"><span class="sr-only"><%= gettext("Actions") %></span></th>
 | 
			
		||||
          </tr>
 | 
			
		||||
| 
						 | 
				
			
			@ -468,13 +473,21 @@ defmodule PrymnWeb.CoreComponents do
 | 
			
		|||
          class="relative divide-y divide-zinc-100 border-t border-zinc-200 text-sm leading-6 text-zinc-700"
 | 
			
		||||
        >
 | 
			
		||||
          <tr :for={row <- @rows} id={@row_id && @row_id.(row)} class="group hover:bg-zinc-50">
 | 
			
		||||
            <td
 | 
			
		||||
              class="relative w-min p-0 hover:cursor-pointer"
 | 
			
		||||
              phx-click={@row_click && @row_click.(row)}
 | 
			
		||||
            >
 | 
			
		||||
              <div class="block w-min py-4 pr-6">
 | 
			
		||||
                <span class="absolute -inset-y-px right-0 -left-4 group-hover:bg-zinc-50 sm:rounded-l-xl" />
 | 
			
		||||
                <span class="relative hover:cursor-pointer"><%= @row_indicator.(row) %></span>
 | 
			
		||||
              </div>
 | 
			
		||||
            </td>
 | 
			
		||||
            <td
 | 
			
		||||
              :for={{col, i} <- Enum.with_index(@col)}
 | 
			
		||||
              phx-click={@row_click && @row_click.(row)}
 | 
			
		||||
              class={["relative p-0", @row_click && "hover:cursor-pointer"]}
 | 
			
		||||
            >
 | 
			
		||||
              <div class="block py-4 pr-6">
 | 
			
		||||
                <span class="absolute -inset-y-px right-0 -left-4 group-hover:bg-zinc-50 sm:rounded-l-xl" />
 | 
			
		||||
                <span class={["relative", i == 0 && "font-semibold text-zinc-900"]}>
 | 
			
		||||
                  <%= render_slot(col, @row_item.(row)) %>
 | 
			
		||||
                </span>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,25 +0,0 @@
 | 
			
		|||
defmodule PrymnWeb.ServerLive.Edit do
 | 
			
		||||
  use PrymnWeb, :live_view
 | 
			
		||||
 | 
			
		||||
  alias Prymn.Servers
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def mount(_params, _session, socket) do
 | 
			
		||||
    {:ok, socket}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def handle_params(%{"id" => id}, _, socket) do
 | 
			
		||||
    server = Servers.get_server!(id)
 | 
			
		||||
 | 
			
		||||
    {:noreply,
 | 
			
		||||
     socket
 | 
			
		||||
     |> assign(:page_title, "Editing #{server.name}")
 | 
			
		||||
     |> assign(:server, server)}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def handle_info({PrymnWeb.ServerLive.FormComponent, {:saved, server}}, socket) do
 | 
			
		||||
    {:noreply, assign(socket, :server, server)}
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			@ -1,11 +0,0 @@
 | 
			
		|||
<.header>Editing server <%= @server.name %></.header>
 | 
			
		||||
 | 
			
		||||
<.live_component
 | 
			
		||||
  module={PrymnWeb.ServerLive.FormComponent}
 | 
			
		||||
  title="Test"
 | 
			
		||||
  id={@server.id}
 | 
			
		||||
  action={@live_action}
 | 
			
		||||
  server={@server}
 | 
			
		||||
/>
 | 
			
		||||
 | 
			
		||||
<.back navigate={~p"/servers/#{@server}"}>Go back to server <%= @server.name %></.back>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,88 +0,0 @@
 | 
			
		|||
defmodule PrymnWeb.ServerLive.FormComponent do
 | 
			
		||||
  use PrymnWeb, :live_component
 | 
			
		||||
 | 
			
		||||
  alias Prymn.Servers
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def render(assigns) do
 | 
			
		||||
    ~H"""
 | 
			
		||||
    <div>
 | 
			
		||||
      <.simple_form
 | 
			
		||||
        for={@form}
 | 
			
		||||
        id="server-form"
 | 
			
		||||
        phx-target={@myself}
 | 
			
		||||
        phx-change="validate"
 | 
			
		||||
        phx-submit="save"
 | 
			
		||||
      >
 | 
			
		||||
        <.input field={@form[:name]} type="text" label="Name" />
 | 
			
		||||
        <:actions>
 | 
			
		||||
          <.button phx-disable-with="Saving...">Save Server</.button>
 | 
			
		||||
        </:actions>
 | 
			
		||||
      </.simple_form>
 | 
			
		||||
    </div>
 | 
			
		||||
    """
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def update(%{server: server} = assigns, socket) do
 | 
			
		||||
    changeset = Servers.change_server(server)
 | 
			
		||||
 | 
			
		||||
    {:ok,
 | 
			
		||||
     socket
 | 
			
		||||
     |> assign(assigns)
 | 
			
		||||
     |> assign_form(changeset)}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def handle_event("validate", %{"server" => server_params}, socket) do
 | 
			
		||||
    changeset =
 | 
			
		||||
      socket.assigns.server
 | 
			
		||||
      |> Servers.change_server(server_params)
 | 
			
		||||
      |> Map.put(:action, :validate)
 | 
			
		||||
 | 
			
		||||
    {:noreply, assign_form(socket, changeset)}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def handle_event("save", %{"server" => server_params}, socket) do
 | 
			
		||||
    save_server(socket, socket.assigns.action, server_params)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp save_server(socket, :edit, server_params) do
 | 
			
		||||
    case Servers.update_server(socket.assigns.server, server_params) do
 | 
			
		||||
      {:ok, server} ->
 | 
			
		||||
        notify_parent({:saved, server})
 | 
			
		||||
 | 
			
		||||
        socket = socket |> put_flash(:info, "Server updated successfully")
 | 
			
		||||
 | 
			
		||||
        if Map.has_key?(socket.assigns, :patch) do
 | 
			
		||||
          socket = push_patch(socket, to: socket.assigns.patch)
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        {:noreply, socket}
 | 
			
		||||
 | 
			
		||||
      {:error, %Ecto.Changeset{} = changeset} ->
 | 
			
		||||
        {:noreply, assign_form(socket, changeset)}
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp save_server(socket, :new, server_params) do
 | 
			
		||||
    case Servers.create_server(server_params) do
 | 
			
		||||
      {:ok, server} ->
 | 
			
		||||
        notify_parent({:saved, server})
 | 
			
		||||
 | 
			
		||||
        {:noreply,
 | 
			
		||||
         socket
 | 
			
		||||
         |> put_flash(:info, "Server created successfully")
 | 
			
		||||
         |> push_patch(to: socket.assigns.patch || false)}
 | 
			
		||||
 | 
			
		||||
      {:error, %Ecto.Changeset{} = changeset} ->
 | 
			
		||||
        {:noreply, assign_form(socket, changeset)}
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp assign_form(socket, %Ecto.Changeset{} = changeset) do
 | 
			
		||||
    assign(socket, :form, to_form(changeset))
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp notify_parent(msg), do: send(self(), {__MODULE__, msg})
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			@ -2,7 +2,6 @@ defmodule PrymnWeb.ServerLive.Index do
 | 
			
		|||
  use PrymnWeb, :live_view
 | 
			
		||||
 | 
			
		||||
  alias Prymn.Servers
 | 
			
		||||
  alias Prymn.Servers.Server
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def mount(_params, _session, socket) do
 | 
			
		||||
| 
						 | 
				
			
			@ -14,26 +13,21 @@ defmodule PrymnWeb.ServerLive.Index do
 | 
			
		|||
    {:noreply, apply_action(socket, socket.assigns.live_action, params)}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp apply_action(socket, :edit, %{"id" => id}) do
 | 
			
		||||
    socket
 | 
			
		||||
    |> assign(:page_title, "Edit Server")
 | 
			
		||||
    |> assign(:server, Servers.get_server!(id))
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp apply_action(socket, :new, _params) do
 | 
			
		||||
    socket
 | 
			
		||||
    |> assign(:page_title, "New Server")
 | 
			
		||||
    |> assign(:server, %Server{})
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp apply_action(socket, :index, _params) do
 | 
			
		||||
    socket
 | 
			
		||||
    |> assign(:page_title, "Listing Servers")
 | 
			
		||||
    |> assign(:server, nil)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def handle_info({PrymnWeb.ServerLive.FormComponent, {:saved, server}}, socket) do
 | 
			
		||||
  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)}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,6 +11,12 @@
 | 
			
		|||
  id="servers"
 | 
			
		||||
  rows={@streams.servers}
 | 
			
		||||
  row_click={fn {_id, server} -> JS.navigate(~p"/servers/#{server}") end}
 | 
			
		||||
  row_indicator={
 | 
			
		||||
    fn {_id, _server} ->
 | 
			
		||||
      ~H(<span class="text-indigo-600">Connecting</span>)
 | 
			
		||||
    end
 | 
			
		||||
  }
 | 
			
		||||
  indicator_label="Status"
 | 
			
		||||
>
 | 
			
		||||
  <:col :let={{_id, server}} label="Name"><%= server.name %></:col>
 | 
			
		||||
  <:col :let={{_id, server}} label="IPv4"><%= server.ipv4 || "N/A" %></:col>
 | 
			
		||||
| 
						 | 
				
			
			@ -25,22 +31,6 @@
 | 
			
		|||
  </:action>
 | 
			
		||||
</.table>
 | 
			
		||||
 | 
			
		||||
<.modal
 | 
			
		||||
  :if={@live_action in [:new, :edit]}
 | 
			
		||||
  id="server-modal"
 | 
			
		||||
  show
 | 
			
		||||
  on_cancel={JS.patch(~p"/servers")}
 | 
			
		||||
>
 | 
			
		||||
  <.header>
 | 
			
		||||
    Add a new server
 | 
			
		||||
    <:subtitle>Connect your server to Prymn!</:subtitle>
 | 
			
		||||
  </.header>
 | 
			
		||||
  <.live_component
 | 
			
		||||
    module={PrymnWeb.ServerLive.FormComponent}
 | 
			
		||||
    id={:new}
 | 
			
		||||
    title={@page_title}
 | 
			
		||||
    action={@live_action}
 | 
			
		||||
    server={@server}
 | 
			
		||||
    patch={~p"/servers"}
 | 
			
		||||
  />
 | 
			
		||||
<.modal :if={@live_action == :new} id="server-modal" show on_cancel={JS.patch(~p"/servers")}>
 | 
			
		||||
  <.live_component module={PrymnWeb.ServerLive.NewServer} id={:new} patch={~p"/servers"} />
 | 
			
		||||
</.modal>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										92
									
								
								backend/lib/prymn_web/live/server_live/new_server.ex
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								backend/lib/prymn_web/live/server_live/new_server.ex
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,92 @@
 | 
			
		|||
defmodule PrymnWeb.ServerLive.NewServer do
 | 
			
		||||
  use PrymnWeb, :live_component
 | 
			
		||||
 | 
			
		||||
  alias Prymn.Servers
 | 
			
		||||
  require Logger
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def render(assigns) do
 | 
			
		||||
    ~H"""
 | 
			
		||||
    <div>
 | 
			
		||||
      <.header>
 | 
			
		||||
        Add a new server
 | 
			
		||||
        <:subtitle>Connect your server to Prymn!</:subtitle>
 | 
			
		||||
      </.header>
 | 
			
		||||
 | 
			
		||||
      <.error :if={assigns[:error]}>There were some errors.</.error>
 | 
			
		||||
 | 
			
		||||
      <.simple_form for={@form} phx-change="validate" phx-submit="connect" phx-target={@myself}>
 | 
			
		||||
        <.input field={@form[:name]} label="Server Name" phx-debounce={1000} />
 | 
			
		||||
        <.input
 | 
			
		||||
          field={@form[:provider]}
 | 
			
		||||
          label="Provider"
 | 
			
		||||
          type="select"
 | 
			
		||||
          prompt="Select a provider"
 | 
			
		||||
          options={Ecto.Enum.mappings(Servers.Server, :provider)}
 | 
			
		||||
        />
 | 
			
		||||
        <.provider id="provider" provider={@form[:provider]} />
 | 
			
		||||
        <:actions>
 | 
			
		||||
          <.button>Connect</.button>
 | 
			
		||||
        </:actions>
 | 
			
		||||
      </.simple_form>
 | 
			
		||||
    </div>
 | 
			
		||||
    """
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def update(assigns, socket) do
 | 
			
		||||
    changeset = Servers.change_server(%Servers.Server{})
 | 
			
		||||
 | 
			
		||||
    socket =
 | 
			
		||||
      socket
 | 
			
		||||
      |> assign(assigns)
 | 
			
		||||
      |> assign(:form, to_form(changeset))
 | 
			
		||||
 | 
			
		||||
    {:ok, socket}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def handle_event("validate", %{"server" => params}, socket) do
 | 
			
		||||
    form =
 | 
			
		||||
      %Servers.Server{}
 | 
			
		||||
      |> Servers.change_server(params)
 | 
			
		||||
      |> Map.put(:action, :validate)
 | 
			
		||||
      |> to_form()
 | 
			
		||||
 | 
			
		||||
    {:noreply, assign(socket, form: form)}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  @impl true
 | 
			
		||||
  def handle_event("connect", %{"server" => params}, socket) do
 | 
			
		||||
    socket =
 | 
			
		||||
      case Servers.create_server(params) do
 | 
			
		||||
        {:ok, server} ->
 | 
			
		||||
          # Notify parent
 | 
			
		||||
          send(self(), {:connect, server})
 | 
			
		||||
 | 
			
		||||
          socket
 | 
			
		||||
          |> put_flash(:info, "Starting new server connection...")
 | 
			
		||||
          |> push_patch(to: ~p"/servers")
 | 
			
		||||
 | 
			
		||||
        {:error, %Ecto.Changeset{} = changeset} ->
 | 
			
		||||
          socket
 | 
			
		||||
          |> assign(:form, to_form(changeset))
 | 
			
		||||
          |> assign(:error, true)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
    {:noreply, socket}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp provider(%{provider: %{value: :Custom}} = assigns) do
 | 
			
		||||
    ~H"""
 | 
			
		||||
    <p>
 | 
			
		||||
      <bold class="font-bold">Manual installation:</bold>
 | 
			
		||||
      To connect, you must have root SSH access to your server. When you click
 | 
			
		||||
      connect, you will be prompted to execute a command on your server to
 | 
			
		||||
      complete the installation.
 | 
			
		||||
    </p>
 | 
			
		||||
    """
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  defp provider(assigns), do: ~H""
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			@ -1,33 +1,18 @@
 | 
			
		|||
<.header>
 | 
			
		||||
  Server <%= @server.name %>
 | 
			
		||||
  <:subtitle>From here, you can view your server details</:subtitle>
 | 
			
		||||
  <%= @server.name %>
 | 
			
		||||
  <:subtitle>
 | 
			
		||||
    <span>IPv4: <%= @server.ipv4 || "Not available" %></span>
 | 
			
		||||
    <span :if={@server.ipv6}>IPv6: <%= @server.ipv6 %></span>
 | 
			
		||||
  </:subtitle>
 | 
			
		||||
  <:actions>
 | 
			
		||||
    <.link navigate={~p"/servers/#{@server}/edit"}>
 | 
			
		||||
      <.button>Edit server</.button>
 | 
			
		||||
    </.link>
 | 
			
		||||
    <.button>
 | 
			
		||||
      New site
 | 
			
		||||
    </.button>
 | 
			
		||||
  </:actions>
 | 
			
		||||
</.header>
 | 
			
		||||
 | 
			
		||||
<.list>
 | 
			
		||||
  <:item title="Name"><%= @server.name %></:item>
 | 
			
		||||
  <:item title="IPv4"><%= @server.ipv4 || "Not available" %></:item>
 | 
			
		||||
  <:item title="IPv6"><%= @server.ipv6 || "Not available" %></:item>
 | 
			
		||||
</.list>
 | 
			
		||||
<div class="mt-10">
 | 
			
		||||
  <h2 class="text-xl font-bold">Sites</h2>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<.back navigate={~p"/servers"}>Back to servers</.back>
 | 
			
		||||
 | 
			
		||||
<.modal
 | 
			
		||||
  :if={@live_action == :edit}
 | 
			
		||||
  id="server-modal"
 | 
			
		||||
  show
 | 
			
		||||
  on_cancel={JS.patch(~p"/servers/#{@server}")}
 | 
			
		||||
>
 | 
			
		||||
  <.live_component
 | 
			
		||||
    module={PrymnWeb.ServerLive.FormComponent}
 | 
			
		||||
    id={@server.id}
 | 
			
		||||
    title={@page_title}
 | 
			
		||||
    action={@live_action}
 | 
			
		||||
    server={@server}
 | 
			
		||||
    patch={~p"/servers/#{@server}"}
 | 
			
		||||
  />
 | 
			
		||||
</.modal>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,9 +22,7 @@ defmodule PrymnWeb.Router do
 | 
			
		|||
    live "/servers", ServerLive.Index, :index
 | 
			
		||||
    live "/servers/new", ServerLive.Index, :new
 | 
			
		||||
 | 
			
		||||
    live "/servers/:id", ServerLive.Show, :show
 | 
			
		||||
 | 
			
		||||
    live "/servers/:id/edit", ServerLive.Edit, :edit
 | 
			
		||||
    live "/servers/:id", ServerLive.Show
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  # Other scopes may use custom stacks.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,6 +6,8 @@ defmodule Prymn.Repo.Migrations.CreateServers do
 | 
			
		|||
      add :name, :string
 | 
			
		||||
      add :ipv4, :string
 | 
			
		||||
      add :ipv6, :string
 | 
			
		||||
      add :provider, :string
 | 
			
		||||
      add :connection_token, :binary
 | 
			
		||||
 | 
			
		||||
      timestamps()
 | 
			
		||||
    end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue