From c60900efb5bd306edee9ce48f6b8e8bd65c29660 Mon Sep 17 00:00:00 2001 From: Kharec Date: Sun, 1 Mar 2026 10:24:47 +0100 Subject: [PATCH] refactor: flatten POST handler with Result pipeline, simplify IP lookup --- src/handlers.gleam | 120 +++++++++++++++++++++++++++------------------ 1 file changed, 71 insertions(+), 49 deletions(-) diff --git a/src/handlers.gleam b/src/handlers.gleam index e2a0ff5..701ca3c 100644 --- a/src/handlers.gleam +++ b/src/handlers.gleam @@ -10,14 +10,9 @@ import storage import wisp fn get_client_ip(request: wisp.Request) -> String { - case list.key_find(request.headers, "x-forwarded-for") { - Ok(ip) -> ip - Error(_) -> - case list.key_find(request.headers, "x-real-ip") { - Ok(ip) -> ip - Error(_) -> "unknown" - } - } + list.key_find(request.headers, "x-forwarded-for") + |> result.try_recover(fn(_) { list.key_find(request.headers, "x-real-ip") }) + |> result.unwrap("unknown") } pub fn handle( @@ -57,54 +52,81 @@ fn handle_home( } http.Post -> { 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") - case csrf_cookie, csrf_form { - Ok(cookie_token), Ok(form_token) if cookie_token == form_token -> { - let ip = get_client_ip(request) - let rate_reply = process.new_subject() - process.send(storage, storage.CheckRateLimit(ip, rate_reply)) - case process.receive(rate_reply, 1000) { - Ok(True) -> { - let encrypted_content = - list.key_find(form.values, "encrypted_content") - |> result.unwrap("") - 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") - } - } + + 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)) } - _, _ -> wisp.bad_request("Invalid CSRF token") + 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_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( request: wisp.Request, storage: process.Subject(storage.StorageMsg),