3
Replies
4
Boosts
Seriously, look at my child:
defmodule ThurimClientApi.Plugs.RateLimit.RegistrationController do
use ThurimClientApi, :rate_limiter
// ...
def call(conn, _options) do
conn =
conn
|> assign_scale(&get_scale/1)
|> assign_limit(&get_limit/1)
%{rate_limit_info: %RateLimitInfo{scale: scale, limit: limit}} = conn.assigns
conn.assigns.current_user.user_id
|> RateLimitKey.get_key(conn.assigns.action)
|> RateLimit.hit(scale, limit)
|> case do
{:allow, _count} ->
conn
{:deny, retry_after} ->
conn
|> put_resp_header("retry-after", Integer.to_string(div(retry_after, 1000)))
|> send_resp(429, [])
|> halt()
end
end
@impl RateLimitBehaviour
def get_scale(action) when action in [:register],
do: :timer.minutes(1)
@impl RateLimitBehaviour
def get_limit(action) when action in [:register], do: 1
end
On second thought, I don't need to assign scale and limit to conn...
ok, it gets even sexier:
defmodule ThurimClientApi.Plugs.RateLimit.RegistrationController do
use ThurimClientApi, :rate_limiter
@impl RateLimitBehaviour
def get_scale(action) when action in [:register],
do: :timer.minutes(1)
@impl RateLimitBehaviour
def get_limit(action) when action in [:register], do: 1
end
Everything else is encapsulated in the __using__ macro! Yeah, yeah, explicit is better than implicit, but this lets me define rate limits per action on each controller quickly and efficiently!