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,54 +52,81 @@ fn handle_home(
} }
http.Post -> { http.Post -> {
use form <- wisp.require_form(request) use form <- wisp.require_form(request)
let csrf_cookie = wisp.get_cookie(request, "csrf_token", wisp.Signed)
let csrf_form = list.key_find(form.values, "csrf_token") let result = {
case csrf_cookie, csrf_form { use _ <- result.try(verify_csrf(request, form))
Ok(cookie_token), Ok(form_token) if cookie_token == form_token -> { use _ <- result.try(check_rate_limit(storage, get_client_ip(request)))
let ip = get_client_ip(request) use key <- result.try(create_paste(storage, form))
let rate_reply = process.new_subject() Ok(key)
process.send(storage, storage.CheckRateLimit(ip, rate_reply)) }
case process.receive(rate_reply, 1000) {
Ok(True) -> { case result {
let encrypted_content = Ok(paste_key) -> {
list.key_find(form.values, "encrypted_content") wisp.ok()
|> result.unwrap("") |> wisp.html_body(html.created(paste_key))
case string.length(encrypted_content) {
0 -> wisp.bad_request("Missing content")
n if n > 10_000_000 -> wisp.bad_request("Content too large")
_ -> {
let new_key = key.generate()
let paste_reply = process.new_subject()
process.send(
storage,
storage.CreatePaste(new_key, encrypted_content, paste_reply),
)
case process.receive(paste_reply, 1000) {
Ok(True) -> {
wisp.ok()
|> wisp.html_body(html.created(new_key))
}
_ -> {
wisp.internal_server_error()
|> wisp.html_body("Failed to create paste")
}
}
}
}
}
_ -> {
wisp.response(429)
|> wisp.html_body("Rate limit exceeded")
}
}
} }
_, _ -> wisp.bad_request("Invalid CSRF token") Error(response) -> response
} }
} }
_ -> wisp.method_not_allowed([http.Get, http.Post]) _ -> 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_form = list.key_find(form.values, "csrf_token")
case csrf_cookie, csrf_form {
Ok(cookie_token), Ok(form_token) if cookie_token == form_token -> Ok(Nil)
_, _ -> Error(wisp.bad_request("Invalid CSRF token"))
}
}
fn check_rate_limit(storage, ip: String) -> Result(Nil, wisp.Response) {
let rate_reply = process.new_subject()
process.send(storage, storage.CheckRateLimit(ip, rate_reply))
case process.receive(rate_reply, 1000) {
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 =
list.key_find(form.values, "encrypted_content")
|> result.unwrap("")
case string.length(encrypted_content) {
0 -> Error(wisp.bad_request("Missing content"))
n if n > 10_000_000 -> Error(wisp.bad_request("Content too large"))
_ -> {
let new_key = key.generate()
let paste_reply = process.new_subject()
process.send(
storage,
storage.CreatePaste(new_key, encrypted_content, paste_reply),
)
case process.receive(paste_reply, 1000) {
Ok(True) -> Ok(new_key)
_ ->
Error(
wisp.internal_server_error()
|> wisp.html_body("Failed to create paste"),
)
}
}
}
}
fn handle_paste( fn handle_paste(
request: wisp.Request, request: wisp.Request,
storage: process.Subject(storage.StorageMsg), storage: process.Subject(storage.StorageMsg),