mirror of
https://github.com/mgerb/classic-wow-forums
synced 2026-01-11 09:32:51 +00:00
server/client - rate limiting for threads/replies
This commit is contained in:
@@ -80,6 +80,12 @@ export class Editor extends React.Component<Props, State> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getErrorMessage(e: any) {
|
||||||
|
return get(e, 'response.status') === 429 ?
|
||||||
|
'You are doing that too much! Please try again in a few minutes.' :
|
||||||
|
'Server error. Please try again later.';
|
||||||
|
}
|
||||||
|
|
||||||
async newReply() {
|
async newReply() {
|
||||||
const { content } = this.state;
|
const { content } = this.state;
|
||||||
|
|
||||||
@@ -99,7 +105,7 @@ export class Editor extends React.Component<Props, State> {
|
|||||||
this.props.editingReply ? await axios.put('/api/reply', data) : await axios.post('/api/reply', data);
|
this.props.editingReply ? await axios.put('/api/reply', data) : await axios.post('/api/reply', data);
|
||||||
this.props.onClose(false);
|
this.props.onClose(false);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.setState({ errorMessage: 'Server error. Please try again later.' });
|
this.setState({ errorMessage: this.getErrorMessage(e) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,7 +127,7 @@ export class Editor extends React.Component<Props, State> {
|
|||||||
await axios.post('/api/thread', data);
|
await axios.post('/api/thread', data);
|
||||||
this.props.onClose(false);
|
this.props.onClose(false);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.setState({ errorMessage: 'Server error. Please try again later.' });
|
this.setState({ errorMessage: this.getErrorMessage(e) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
18
lib/myapp/rate_limiter.ex
Normal file
18
lib/myapp/rate_limiter.ex
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
defmodule MyApp.RateLimiter do
|
||||||
|
|
||||||
|
# map keys to integers to save memory
|
||||||
|
def new_reply_key, do: 1
|
||||||
|
def new_thread_key, do: 2
|
||||||
|
|
||||||
|
@spec limit(String.t, integer, integer) :: {:ok, String.t} | {:error, String.t}
|
||||||
|
def limit(end_point, user_id, seconds) do
|
||||||
|
key = "rl#{end_point}:#{user_id}"
|
||||||
|
case Cachex.get(:myapp, key) do
|
||||||
|
{:missing, _} ->
|
||||||
|
Cachex.set(:myapp, key, true, ttl: :timer.seconds(seconds))
|
||||||
|
{:ok, "ok"}
|
||||||
|
{:ok, _} -> {:error, "limit reached"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -2,6 +2,7 @@ defmodule MyAppWeb.ReplyController do
|
|||||||
use MyAppWeb, :controller
|
use MyAppWeb, :controller
|
||||||
alias MyAppWeb.Response
|
alias MyAppWeb.Response
|
||||||
alias MyApp.Data
|
alias MyApp.Data
|
||||||
|
alias MyApp.RateLimiter
|
||||||
|
|
||||||
@spec insert(map, map) :: any
|
@spec insert(map, map) :: any
|
||||||
def insert(conn, params) do
|
def insert(conn, params) do
|
||||||
@@ -9,11 +10,15 @@ defmodule MyAppWeb.ReplyController do
|
|||||||
|> MyApp.Guardian.Plug.current_claims
|
|> MyApp.Guardian.Plug.current_claims
|
||||||
|> Map.get("id")
|
|> Map.get("id")
|
||||||
|
|
||||||
{output, status} = params
|
{output, status} = case RateLimiter.limit(RateLimiter.new_reply_key, user_id, 60) do
|
||||||
|
{:ok, _} -> params
|
||||||
|> Map.put("user_id", user_id)
|
|> Map.put("user_id", user_id)
|
||||||
|> Data.Reply.insert
|
|> Data.Reply.insert
|
||||||
|> Response.put_resp
|
|> Response.put_resp
|
||||||
|
|
||||||
|
{:error, error} -> {error, 429}
|
||||||
|
end
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_status(status)
|
|> put_status(status)
|
||||||
|> Response.json(output)
|
|> Response.json(output)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ defmodule MyAppWeb.ThreadController do
|
|||||||
use MyAppWeb, :controller
|
use MyAppWeb, :controller
|
||||||
alias MyAppWeb.Response
|
alias MyAppWeb.Response
|
||||||
alias MyApp.Data
|
alias MyApp.Data
|
||||||
|
alias MyApp.RateLimiter
|
||||||
|
|
||||||
@spec insert(map, map) :: any
|
@spec insert(map, map) :: any
|
||||||
def insert(conn, params) do
|
def insert(conn, params) do
|
||||||
@@ -9,10 +10,14 @@ defmodule MyAppWeb.ThreadController do
|
|||||||
|> MyApp.Guardian.Plug.current_claims
|
|> MyApp.Guardian.Plug.current_claims
|
||||||
|> Map.get("id")
|
|> Map.get("id")
|
||||||
|
|
||||||
{output, status} = params
|
# every 5 minutes user can submit new thread
|
||||||
|
{output, status} = case RateLimiter.limit(RateLimiter.new_thread_key, user_id, 300) do
|
||||||
|
{:ok, _} -> params
|
||||||
|> Map.put("user_id", user_id)
|
|> Map.put("user_id", user_id)
|
||||||
|> Data.Thread.insert
|
|> Data.Thread.insert
|
||||||
|> Response.put_resp
|
|> Response.put_resp
|
||||||
|
{:error, error} -> {error, 429}
|
||||||
|
end
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_status(status)
|
|> put_status(status)
|
||||||
|
|||||||
Reference in New Issue
Block a user