init
This commit is contained in:
125
lib/forum/router.ex
Normal file
125
lib/forum/router.ex
Normal file
@@ -0,0 +1,125 @@
|
||||
defmodule Forum.Router do
|
||||
use Plug.Router
|
||||
use Joken.Config
|
||||
require Logger
|
||||
|
||||
plug Plug.Logger
|
||||
plug :log_request
|
||||
plug :fetch_cookies
|
||||
plug :verify_session_host
|
||||
plug :match
|
||||
plug :dispatch
|
||||
|
||||
defp verify_session_host(conn, _opts) do
|
||||
if conn.host == "session.frm.so" do
|
||||
case authenticate(conn) do
|
||||
{:ok, claims} ->
|
||||
conn
|
||||
|> Plug.Conn.assign(:claims, claims)
|
||||
|
||||
:error ->
|
||||
conn |> send_resp(401, "unauthorized") |> halt()
|
||||
end
|
||||
else
|
||||
conn
|
||||
end
|
||||
end
|
||||
|
||||
defp authenticate(conn) do
|
||||
with token when is_binary(token) <- conn.cookies["auth_token"], {:ok, claims} <- verify_token(token) do
|
||||
{:ok, claims}
|
||||
else
|
||||
_ -> :error
|
||||
end
|
||||
end
|
||||
|
||||
defp verify_token(token) do
|
||||
secret = Application.fetch_env!(:forum, :auth)[:jwt_secret]
|
||||
signer = Joken.Signer.create("HS256", secret)
|
||||
|
||||
case Joken.verify_and_validate(%{}, token, signer) do
|
||||
{:ok, claims} -> {:ok, claims}
|
||||
{:error, _reason} = err -> err
|
||||
end
|
||||
end
|
||||
|
||||
get "/admin", host: "session.frm.so", do: handle_admin(conn)
|
||||
get "/admin/graphyellow.svg", host: "session.frm.so", do: admin_asset(conn, "ui/graphyellow.svg", "image/svg+xml")
|
||||
get "/admin/*path", host: "session.frm.so", do: handle_admin(conn)
|
||||
|
||||
get "/", host: "session.frm.so" do
|
||||
user_id = conn.assigns.claims["id"]
|
||||
email = conn.assigns.claims["email"]
|
||||
conn
|
||||
|> WebSockAdapter.upgrade(Forum.WsHandler, %{user_id: user_id, email: email}, timeout: :infinity)
|
||||
|> halt()
|
||||
end
|
||||
|
||||
match _ do
|
||||
send_resp(conn, 404, "not found")
|
||||
end
|
||||
|
||||
defp handle_admin(conn) do
|
||||
if websocket_upgrade?(conn) do
|
||||
conn
|
||||
|> WebSockAdapter.upgrade(Forum.WsHandler, %{}, timeout: :infinity)
|
||||
|> halt()
|
||||
else
|
||||
admin_page(conn)
|
||||
end
|
||||
end
|
||||
|
||||
defp websocket_upgrade?(conn) do
|
||||
connection = get_req_header(conn, "connection") |> Enum.join(",") |> String.downcase()
|
||||
upgrade = get_req_header(conn, "upgrade") |> List.first() |> to_string() |> String.downcase()
|
||||
|
||||
String.contains?(connection, "upgrade") and upgrade == "websocket"
|
||||
end
|
||||
|
||||
defp admin_page(conn) do
|
||||
html = File.read!(Forum.Assets.path("ui/admin.html"))
|
||||
|
||||
conn
|
||||
|> put_resp_content_type("text/html")
|
||||
|> send_resp(200, html)
|
||||
end
|
||||
|
||||
defp admin_asset(conn, path, content_type) do
|
||||
conn
|
||||
|> put_resp_content_type(content_type)
|
||||
|> send_file(200, Forum.Assets.path(path))
|
||||
end
|
||||
|
||||
defp log_request(conn, _opts) do
|
||||
started_at = System.monotonic_time(:microsecond)
|
||||
|
||||
Plug.Conn.register_before_send(conn, fn conn ->
|
||||
duration_us = System.monotonic_time(:microsecond) - started_at
|
||||
|
||||
Forum.LogStore.add(%{
|
||||
"source_ip" => remote_ip(conn),
|
||||
"host" => conn.host || "",
|
||||
"method" => conn.method,
|
||||
"path" => conn.request_path,
|
||||
"query_string" => conn.query_string,
|
||||
"status" => conn.status || 0,
|
||||
"duration_ms" => Float.round(duration_us / 1_000, 2)
|
||||
})
|
||||
|
||||
conn
|
||||
end)
|
||||
end
|
||||
|
||||
defp remote_ip(conn) do
|
||||
case Plug.Conn.get_req_header(conn, "x-real-ip") do
|
||||
[ip | _] -> ip
|
||||
[] -> fallback_ip(conn)
|
||||
end
|
||||
end
|
||||
|
||||
defp fallback_ip(%{remote_ip: remote_ip}) when is_tuple(remote_ip) do
|
||||
remote_ip |> :inet.ntoa() |> to_string()
|
||||
end
|
||||
|
||||
defp fallback_ip(_), do: ""
|
||||
end
|
||||
Reference in New Issue
Block a user