Deploy again
This commit is contained in:
parent
ffe080adef
commit
787428f1a5
18 changed files with 717 additions and 223 deletions
|
|
@ -1,4 +1,5 @@
|
|||
use std::sync::Arc;
|
||||
use std::sync::{Arc, LazyLock};
|
||||
use std::time::Duration;
|
||||
|
||||
use axum::body::Body;
|
||||
use axum::extract::Request;
|
||||
|
|
@ -8,6 +9,17 @@ use tracing::warn;
|
|||
|
||||
use crate::state::AppState;
|
||||
|
||||
/// Dedicated HTTP client for proxying — does not follow redirects so 3xx
|
||||
/// responses are passed through to the browser (needed for OAuth flows).
|
||||
static PROXY_CLIENT: LazyLock<reqwest::Client> = LazyLock::new(|| {
|
||||
reqwest::Client::builder()
|
||||
.redirect(reqwest::redirect::Policy::none())
|
||||
.timeout(Duration::from_secs(30))
|
||||
.connect_timeout(Duration::from_secs(5))
|
||||
.build()
|
||||
.expect("Failed to build proxy HTTP client")
|
||||
});
|
||||
|
||||
pub async fn proxy_to_pocketbase(state: Arc<AppState>, req: Request) -> impl IntoResponse {
|
||||
let pb_url = state.pocketbase_url.trim_end_matches('/');
|
||||
|
||||
|
|
@ -21,7 +33,7 @@ pub async fn proxy_to_pocketbase(state: Arc<AppState>, req: Request) -> impl Int
|
|||
let url = format!("{pb_url}{target_path}{query}");
|
||||
|
||||
let method = req.method().clone();
|
||||
let mut builder = state.http_client.request(method, &url);
|
||||
let mut builder = PROXY_CLIENT.request(method, &url);
|
||||
|
||||
// Forward only safe headers (allowlist)
|
||||
const ALLOWED_HEADERS: &[&str] = &[
|
||||
|
|
@ -37,6 +49,21 @@ pub async fn proxy_to_pocketbase(state: Arc<AppState>, req: Request) -> impl Int
|
|||
}
|
||||
}
|
||||
|
||||
// Forward client IP so PocketBase rate-limits per-user, not per-server.
|
||||
// Prefer existing X-Forwarded-For (from reverse proxy), fall back to X-Real-IP.
|
||||
if let Some(xff) = req.headers().get("x-forwarded-for") {
|
||||
builder = builder.header("X-Forwarded-For", xff.clone());
|
||||
// First IP in the chain is the original client
|
||||
if let Ok(s) = xff.to_str() {
|
||||
if let Some(client_ip) = s.split(',').next().map(str::trim) {
|
||||
builder = builder.header("X-Real-IP", client_ip);
|
||||
}
|
||||
}
|
||||
} else if let Some(real_ip) = req.headers().get("x-real-ip") {
|
||||
builder = builder.header("X-Forwarded-For", real_ip.clone());
|
||||
builder = builder.header("X-Real-IP", real_ip.clone());
|
||||
}
|
||||
|
||||
// Forward body
|
||||
let body_bytes = match axum::body::to_bytes(req.into_body(), 10 * 1024 * 1024).await {
|
||||
Ok(bytes) => bytes,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue