Подключение к сокету Phoenix с токеном и присутствием

Я пытаюсь связать модули Phoenix Channel, Token и Presence, чтобы добавить функциональность чата в мое приложение Phoenix 1.3. Я не смог заставить все 3 модуля работать вместе. Последняя ошибка была connection to websocket closed before handshake, Теперь я не получаю никаких ошибок, но он также не подключается к сокету.

Я считаю, что проблема заключается в функции "connect" в player_socket.ex. (У меня есть ресурс игрока). Вот функция:

  def connect(%{"token" => token}, socket) do
      case Phoenix.Token.verify(socket, "player auth", token, max_age: @max_age) do
        {:ok, player_id} ->
          player = Repo.get!(Player, player_id)
          {:ok, assign(socket, :current_player, player)}
          {:error, _reason} ->
           :error
      end
  end

Я подписываю токен в метатеге в app.html.eex. <%= tag :meta, name: "channel_token", content: Phoenix.Token.sign(@conn, "player auth", :player_id) %>

Затем в obby_channel.ex я пытаюсь присоединиться к каналу:

  def join("lobby:lobby", _params, socket) do
    send(self(), :after_join)
    {:ok, assign(socket, :player_id, :current_player)}
  end

  def handle_info(:after_join, socket) do
    push socket, "presence_state", Presence.list(socket)
    {:ok, _} = Presence.track(socket, socket.assigns.current_player, %{
      online_at: inspect(System.system_time(:seconds))
    })
    {:noreply, socket}
  end

Я читаю документы, но не могу понять, почему я не могу подключиться к веб-сокету с помощью current_player, чтобы я мог использовать Presence для отображения того, кто находится в сети, и имен игроков, чтобы связать их с сообщениями чата. Любое понимание очень ценится! У меня есть репо здесь: https://github.com/EssenceOfChaos/gofish

ОБНОВИТЬ

Я использую плагин "current_player" для сохранения структуры игрока в коннекте как "current_player".

%Plug.Conn{adapter: {Plug.Adapters.Cowboy.Conn, :...},
 assigns: %{current_player: %Gofish.Accounts.Player{__meta__: #Ecto.Schema.Metadata<:loaded, "players">,
    email: "example@aol.com", id: 6,

Вот мой обновленный obby_channel.ex:

  def join("lobby:lobby", _params, socket) do
    send(self(), :after_join)
    {:ok, socket}
  end

  def handle_info(:after_join, socket) do
    push socket, "presence_state", Presence.list(socket)
    {:ok, _} = Presence.track(socket, socket.assigns.current_player.id, %{
      username: socket.assigns.current_player.username,
      online_at: inspect(System.system_time(:seconds))
    })
    {:noreply, socket}
  end

1 ответ

Решение

Ваш player_socket.ex Это хорошо. У вас есть несколько проблем, хотя:

В вашем layout/app.eex шаблон:

Phoenix.Token.sign(@conn, "player auth", :player_id) буквально пишет атом :player_id вместо ID игрока. Для того, чтобы написать идентификатор игрока, вы должны использовать @player_id и добавьте плагин, который глобально присваивает значение вашему router.ex вот так:

pipeline :browser do
  [...]
  plug :fetch_current_user
end

...

def fetch_current_user(conn, _) do
  assigns(conn, :current_player, get_session(conn, :current_player)
end

Это сделает @current_player доступны во всех ваших шаблонах, которые вы затем можете использовать в app.eex:

<%= tag :meta, name: "channel_token", content: Phoenix.Token.sign(@conn, "player auth", @current_player) %>

(вы должны написать это условно, если @current_player не nil и остановите ваш клиент JS от попыток подключений через веб-сокет, если это так, кстати)

Это изменение немедленно исправит вашу неспособность подключиться к веб-сокету, если вы вошли в систему, но у вас все еще есть еще одна проблема:{:ok, assign(socket, :player_id, :current_player)} в вашем loby_channel.ex назначает атом :current_player буквально вместо того, чтобы использовать фактическое значение идентификатора текущего игрока, но вам не нужна эта строка вообще. Вместо этого в вашем :after_join, ты должен сделать

{:ok, _} = Presence.track(socket, socket.assigns.current_player.username, %{
  online_at: inspect(System.system_time(:seconds))
})

Обратите внимание, что я изменился socket.assigns.current_player в socket.assigns.current_player.username, Это потому, что вы не можете назначить структуру в качестве ключа присутствия.

В качестве альтернативы вы могли бы сделать

{:ok, _} = Presence.track(socket, socket.assigns.current_player.id, %{
  username: socket.assigns.current_player.username,
  online_at: inspect(System.system_time(:seconds))
})

и в вашем socket.js вы бы использовали first.username вместо id внутри renderOnlineUsers

Другие вопросы по тегам