aprs.me/lib/aprsme_web/plugs/rate_limiter.ex

63 lines
1.3 KiB
Elixir

defmodule AprsmeWeb.Plugs.RateLimiter do
@moduledoc """
Rate limiting plug to prevent DoS attacks
"""
import Phoenix.Controller
import Plug.Conn
def init(opts) do
# Default options
Keyword.merge(
[
# 1 minute
scale: 60_000,
# 100 requests per minute
limit: 100,
# Rate limit by IP address
key: :ip,
error_message: "Too many requests"
],
opts
)
end
def call(conn, opts) do
key = get_key(conn, opts[:key])
scale = opts[:scale]
limit = opts[:limit]
error_message = opts[:error_message]
case Hammer.check_rate("rate_limit:#{key}", scale, limit) do
{:allow, _count} ->
conn
{:deny, _count} ->
conn
|> put_status(:too_many_requests)
|> json(%{error: error_message})
|> halt()
end
end
defp get_key(conn, :ip) do
case get_req_header(conn, "x-forwarded-for") do
[ip | _] -> ip
[] -> conn.remote_ip |> :inet.ntoa() |> to_string()
end
end
defp get_key(conn, :user_agent) do
case get_req_header(conn, "user-agent") do
[ua | _] -> ua
[] -> "unknown"
end
end
defp get_key(conn, custom_key) when is_function(custom_key) do
custom_key.(conn)
end
defp get_key(_conn, key) when is_binary(key) do
key
end
end