refactor: consolidate HTTP client for TCP/Unix socket handling

Extract reqwest client creation into dedicated HttpClient abstraction that handles both TCP and Unix socket connections transparently. Simplifies proxy logic by removing duplicate URL construction and client selection throughout the codebase.
This commit is contained in:
2026-01-07 14:34:32 -06:00
parent dcc496c979
commit dd1ce186d2
13 changed files with 398 additions and 329 deletions
+31 -50
View File
@@ -1,4 +1,11 @@
use axum::{Router, extract::Request, http::Uri, response::IntoResponse, routing::any};
use axum::{
Router,
body::Body,
extract::Request,
http::{Method, Uri},
response::IntoResponse,
routing::{any, get, post},
};
use std::sync::Arc;
use crate::{assets, handlers, state::AppState};
@@ -9,32 +16,28 @@ pub fn api_routes() -> Router<Arc<AppState>> {
.route("/", any(api_root_404_handler))
.route(
"/health",
axum::routing::get(handlers::health_handler).head(handlers::health_handler),
get(handlers::health_handler).head(handlers::health_handler),
)
// Authentication endpoints (public)
.route("/login", axum::routing::post(handlers::api_login_handler))
.route("/logout", axum::routing::post(handlers::api_logout_handler))
.route(
"/session",
axum::routing::get(handlers::api_session_handler),
)
.route("/login", post(handlers::api_login_handler))
.route("/logout", post(handlers::api_logout_handler))
.route("/session", get(handlers::api_session_handler))
// Projects - GET is public (shows all for admin, only non-hidden for public)
// POST/PUT/DELETE require authentication
.route(
"/projects",
axum::routing::get(handlers::projects_handler).post(handlers::create_project_handler),
get(handlers::projects_handler).post(handlers::create_project_handler),
)
.route(
"/projects/{id}",
axum::routing::get(handlers::get_project_handler)
get(handlers::get_project_handler)
.put(handlers::update_project_handler)
.delete(handlers::delete_project_handler),
)
// Project tags - authentication checked in handlers
.route(
"/projects/{id}/tags",
axum::routing::get(handlers::get_project_tags_handler)
.post(handlers::add_project_tag_handler),
get(handlers::get_project_tags_handler).post(handlers::add_project_tag_handler),
)
.route(
"/projects/{id}/tags/{tag_id}",
@@ -43,36 +46,29 @@ pub fn api_routes() -> Router<Arc<AppState>> {
// Tags - authentication checked in handlers
.route(
"/tags",
axum::routing::get(handlers::list_tags_handler).post(handlers::create_tag_handler),
get(handlers::list_tags_handler).post(handlers::create_tag_handler),
)
.route(
"/tags/{slug}",
axum::routing::get(handlers::get_tag_handler).put(handlers::update_tag_handler),
get(handlers::get_tag_handler).put(handlers::update_tag_handler),
)
.route(
"/tags/{slug}/related",
axum::routing::get(handlers::get_related_tags_handler),
get(handlers::get_related_tags_handler),
)
.route(
"/tags/recalculate-cooccurrence",
axum::routing::post(handlers::recalculate_cooccurrence_handler),
post(handlers::recalculate_cooccurrence_handler),
)
// Admin stats - requires authentication
.route(
"/stats",
axum::routing::get(handlers::get_admin_stats_handler),
)
.route("/stats", get(handlers::get_admin_stats_handler))
// Site settings - GET is public, PUT requires authentication
.route(
"/settings",
axum::routing::get(handlers::get_settings_handler)
.put(handlers::update_settings_handler),
get(handlers::get_settings_handler).put(handlers::update_settings_handler),
)
// Icon API - proxy to SvelteKit (authentication handled by SvelteKit)
.route(
"/icons/{*path}",
axum::routing::get(handlers::proxy_icons_handler),
)
.route("/icons/{*path}", get(handlers::proxy_icons_handler))
.fallback(api_404_and_method_handler)
}
@@ -83,19 +79,13 @@ pub fn build_base_router() -> Router<Arc<AppState>> {
.route("/api/", any(api_root_404_handler))
.route(
"/_app/{*path}",
axum::routing::get(assets::serve_embedded_asset).head(assets::serve_embedded_asset),
get(assets::serve_embedded_asset).head(assets::serve_embedded_asset),
)
.route("/pgp", axum::routing::get(handlers::handle_pgp_route))
.route(
"/publickey.asc",
axum::routing::get(handlers::serve_pgp_key),
)
.route("/pgp.asc", axum::routing::get(handlers::serve_pgp_key))
.route(
"/.well-known/pgpkey.asc",
axum::routing::get(handlers::serve_pgp_key),
)
.route("/keys", axum::routing::get(handlers::redirect_to_pgp))
.route("/pgp", get(handlers::handle_pgp_route))
.route("/publickey.asc", get(handlers::serve_pgp_key))
.route("/pgp.asc", get(handlers::serve_pgp_key))
.route("/.well-known/pgpkey.asc", get(handlers::serve_pgp_key))
.route("/keys", get(handlers::redirect_to_pgp))
}
async fn api_root_404_handler(uri: Uri) -> impl IntoResponse {
@@ -109,10 +99,7 @@ async fn api_404_and_method_handler(req: Request) -> impl IntoResponse {
let uri = req.uri();
let path = uri.path();
if method != axum::http::Method::GET
&& method != axum::http::Method::HEAD
&& method != axum::http::Method::OPTIONS
{
if method != Method::GET && method != Method::HEAD && method != Method::OPTIONS {
let content_type = req
.headers()
.get(axum::http::header::CONTENT_TYPE)
@@ -129,10 +116,7 @@ async fn api_404_and_method_handler(req: Request) -> impl IntoResponse {
)
.into_response();
}
} else if method == axum::http::Method::POST
|| method == axum::http::Method::PUT
|| method == axum::http::Method::PATCH
{
} else if method == Method::POST || method == Method::PUT || method == Method::PATCH {
// POST/PUT/PATCH require Content-Type header
return (
StatusCode::BAD_REQUEST,
@@ -158,10 +142,7 @@ async fn api_404_and_method_handler(req: Request) -> impl IntoResponse {
}
async fn api_404_handler(uri: Uri) -> impl IntoResponse {
let req = Request::builder()
.uri(uri)
.body(axum::body::Body::empty())
.unwrap();
let req = Request::builder().uri(uri).body(Body::empty()).unwrap();
api_404_and_method_handler(req).await
}