refactor: flatten POST handler with Result pipeline, simplify IP lookup

This commit is contained in:
2026-03-01 10:24:47 +01:00
parent 891107c442
commit c60900efb5

View File

@@ -10,14 +10,9 @@ import storage
import wisp import wisp
fn get_client_ip(request: wisp.Request) -> String { fn get_client_ip(request: wisp.Request) -> String {
case list.key_find(request.headers, "x-forwarded-for") { list.key_find(request.headers, "x-forwarded-for")
Ok(ip) -> ip |> result.try_recover(fn(_) { list.key_find(request.headers, "x-real-ip") })
Error(_) -> |> result.unwrap("unknown")
case list.key_find(request.headers, "x-real-ip") {
Ok(ip) -> ip
Error(_) -> "unknown"
}
}
} }
pub fn handle( pub fn handle(
@@ -57,21 +52,61 @@ fn handle_home(
} }
http.Post -> { http.Post -> {
use form <- wisp.require_form(request) use form <- wisp.require_form(request)
let result = {
use _ <- result.try(verify_csrf(request, form))
use _ <- result.try(check_rate_limit(storage, get_client_ip(request)))
use key <- result.try(create_paste(storage, form))
Ok(key)
}
case result {
Ok(paste_key) -> {
wisp.ok()
|> wisp.html_body(html.created(paste_key))
}
Error(response) -> response
}
}
_ -> wisp.method_not_allowed([http.Get, http.Post])
}
}
fn verify_csrf(
request: wisp.Request,
form: wisp.FormData,
) -> Result(Nil, wisp.Response) {
let csrf_cookie = wisp.get_cookie(request, "csrf_token", wisp.Signed) let csrf_cookie = wisp.get_cookie(request, "csrf_token", wisp.Signed)
let csrf_form = list.key_find(form.values, "csrf_token") let csrf_form = list.key_find(form.values, "csrf_token")
case csrf_cookie, csrf_form { case csrf_cookie, csrf_form {
Ok(cookie_token), Ok(form_token) if cookie_token == form_token -> { Ok(cookie_token), Ok(form_token) if cookie_token == form_token -> Ok(Nil)
let ip = get_client_ip(request) _, _ -> Error(wisp.bad_request("Invalid CSRF token"))
}
}
fn check_rate_limit(storage, ip: String) -> Result(Nil, wisp.Response) {
let rate_reply = process.new_subject() let rate_reply = process.new_subject()
process.send(storage, storage.CheckRateLimit(ip, rate_reply)) process.send(storage, storage.CheckRateLimit(ip, rate_reply))
case process.receive(rate_reply, 1000) { case process.receive(rate_reply, 1000) {
Ok(True) -> { Ok(True) -> Ok(Nil)
_ ->
Error(
wisp.response(429)
|> wisp.html_body("Rate limit exceeded"),
)
}
}
fn create_paste(storage, form: wisp.FormData) -> Result(String, wisp.Response) {
let encrypted_content = let encrypted_content =
list.key_find(form.values, "encrypted_content") list.key_find(form.values, "encrypted_content")
|> result.unwrap("") |> result.unwrap("")
case string.length(encrypted_content) { case string.length(encrypted_content) {
0 -> wisp.bad_request("Missing content") 0 -> Error(wisp.bad_request("Missing content"))
n if n > 10_000_000 -> wisp.bad_request("Content too large") n if n > 10_000_000 -> Error(wisp.bad_request("Content too large"))
_ -> { _ -> {
let new_key = key.generate() let new_key = key.generate()
let paste_reply = process.new_subject() let paste_reply = process.new_subject()
@@ -79,30 +114,17 @@ fn handle_home(
storage, storage,
storage.CreatePaste(new_key, encrypted_content, paste_reply), storage.CreatePaste(new_key, encrypted_content, paste_reply),
) )
case process.receive(paste_reply, 1000) { case process.receive(paste_reply, 1000) {
Ok(True) -> { Ok(True) -> Ok(new_key)
wisp.ok() _ ->
|> wisp.html_body(html.created(new_key)) Error(
}
_ -> {
wisp.internal_server_error() wisp.internal_server_error()
|> wisp.html_body("Failed to create paste") |> wisp.html_body("Failed to create paste"),
)
} }
} }
} }
}
}
_ -> {
wisp.response(429)
|> wisp.html_body("Rate limit exceeded")
}
}
}
_, _ -> wisp.bad_request("Invalid CSRF token")
}
}
_ -> wisp.method_not_allowed([http.Get, http.Post])
}
} }
fn handle_paste( fn handle_paste(