summaryrefslogtreecommitdiff
path: root/lib/web/controllers/irc_auth_sse_controller.ex
blob: 01c840b8b827310e132a086fe1817a3c151c7922 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
defmodule NolaWeb.IrcAuthSseController do
  use NolaWeb, :controller
  require Logger

  @ping_interval 20_000
  @expire_delay :timer.minutes(3)

  def sse(conn, params) do
    perks = if uri = Map.get(params, "redirect_to") do
      {:redirect, uri}
    else
      nil
    end
    token = String.downcase(EntropyString.random_string(65))
    conn
    |> assign(:token, token)
    |> assign(:perks, perks)
    |> put_resp_header("X-Accel-Buffering", "no")
    |> put_resp_header("content-type", "text/event-stream")
    |> send_chunked(200)
    |> subscribe()
    |> send_sse_message("token", token)
    |> sse_loop
  end

  def subscribe(conn) do
    :timer.send_interval(@ping_interval, {:event, :ping})
    :timer.send_after(@expire_delay, {:event, :expire})
    {:ok, _} = Registry.register(Nola.PubSub, "messages:private", [])
    conn
  end

  def sse_loop(conn) do
    {type, event, exit} = receive do
      {:event, :ping} -> {"ping", "ping", false}
      {:event, :expire} -> {"expire", "expire", true}
      {:irc, :text, %{account: account, text: token} = m} ->
        if String.downcase(String.trim(token)) == conn.assigns.token do
          path = Nola.AuthToken.new_path(account.id, conn.assigns.perks)
          m.replyfun.("ok!")
          {"authenticated", path, true}
        else
          {nil, nil, false}
        end
      _ -> {nil, nil, false}
    end

    conn = if type do
      send_sse_message(conn, type, event)
    else
      conn
    end

    if exit do
      conn
    else
      sse_loop(conn)
    end
  end

  defp send_sse_message(conn, type, data) do
    {:ok, conn} = chunk(conn, "event: #{type}\ndata: #{data}\n\n")
    conn
  end

end