Skip to content

Commit a2578fe

Browse files
committed
Support path prefix in reverse proxy scenarios.
1 parent 77a2b46 commit a2578fe

12 files changed

Lines changed: 81 additions & 54 deletions

File tree

crates/wastebin_server/src/handlers/delete/form.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pub async fn delete(
1515
async {
1616
let id = id.parse()?;
1717
db.delete_for(id, uid).await?;
18-
Ok(Redirect::to("/"))
18+
Ok(crate::redirect("/"))
1919
}
2020
.await
2121
.map_err(|err| make_error(err, page.clone(), theme))

crates/wastebin_server/src/handlers/extract.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ where
143143
.and_then(|referer| referer.to_str().ok())
144144
.map(|referer| {
145145
if referer.starts_with('/') && !referer.starts_with("//") {
146-
Redirect::to(referer)
146+
crate::redirect(referer)
147147
} else {
148148
referer
149149
.parse::<url::Url>()
@@ -155,10 +155,10 @@ where
155155
|q| Redirect::to(&format!("{path}?{q}")),
156156
)
157157
})
158-
.unwrap_or_else(|| Redirect::to("/"))
158+
.unwrap_or_else(|| crate::redirect("/"))
159159
}
160160
})
161-
.unwrap_or_else(|| Redirect::to("/"));
161+
.unwrap_or_else(|| crate::redirect("/"));
162162

163163
Ok(SafeReferer(redirect))
164164
}

crates/wastebin_server/src/handlers/insert/form.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ pub async fn post<E: std::fmt::Debug>(
8383
let mut cookie = cookie("uid", uid.to_string());
8484
cookie.set_secure(true);
8585

86-
Ok((jar.add(cookie), Redirect::to(&url)))
86+
Ok((jar.add(cookie), crate::redirect(&url)))
8787
}
8888
.await
8989
.map_err(|err| make_error(err, page, theme))

crates/wastebin_server/src/main.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use std::time::Duration;
1414
use axum::extract::{DefaultBodyLimit, FromRef, Request, State};
1515
use axum::http::{HeaderName, HeaderValue, StatusCode};
1616
use axum::middleware::{Next, from_fn, from_fn_with_state};
17-
use axum::response::{IntoResponse, Response};
17+
use axum::response::{IntoResponse, Redirect, Response};
1818
use axum::routing::{Router, get, post};
1919
use axum_extra::extract::cookie::Key;
2020
use futures::future::TryFutureExt;
@@ -23,6 +23,7 @@ use http::header::{
2323
X_XSS_PROTECTION,
2424
};
2525
use tokio::net::{TcpListener, UnixListener};
26+
use tokio::task_local;
2627
use tower::ServiceBuilder;
2728
use tower_http::compression::CompressionLayer;
2829
use tower_http::timeout::TimeoutLayer;
@@ -34,6 +35,10 @@ use crate::handlers::extract::Theme;
3435
use crate::handlers::{delete, download, html, insert, raw, robots, theme};
3536
use wastebin_core::db::Database;
3637

38+
task_local! {
39+
static PATH_PREFIX: String;
40+
}
41+
3742
/// Reference counted [`page::Page`] wrapper.
3843
pub(crate) type Page = Arc<page::Page>;
3944

@@ -79,6 +84,26 @@ impl FromRef<AppState> for Cache {
7984
}
8085
}
8186

87+
fn path_prefix() -> String {
88+
PATH_PREFIX.with(|val| val.to_string())
89+
}
90+
91+
fn redirect(uri: &str) -> Redirect {
92+
let prefixed_uri = path_prefix() + uri;
93+
Redirect::to(&prefixed_uri)
94+
}
95+
96+
async fn path_prefix_layer(req: Request, next: Next) -> Response {
97+
let prefix = req
98+
.headers()
99+
.get("x-forwarded-prefix")
100+
.and_then(|val| val.to_str().ok())
101+
.unwrap_or("")
102+
.to_string();
103+
104+
PATH_PREFIX.scope(prefix, next.run(req)).await
105+
}
106+
82107
async fn security_headers_layer(req: Request, next: Next) -> impl IntoResponse {
83108
// Rendered Markdown may embed remote images via `![](…)`; relax img-src for that route only.
84109
const CSP_STRICT: HeaderValue = HeaderValue::from_static(
@@ -234,6 +259,7 @@ fn make_app(state: AppState, timeout: Duration, max_body_size: usize) -> Router
234259
timeout,
235260
))
236261
.layer(from_fn_with_state(state.clone(), handle_service_errors))
262+
.layer(from_fn(path_prefix_layer))
237263
.layer(from_fn(security_headers_layer)),
238264
)
239265
.with_state(state)

crates/wastebin_server/templates/base.html

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{% let path_prefix = crate::path_prefix() %}
12
<!DOCTYPE html>
23
<html lang="en">
34
<head>
@@ -7,27 +8,27 @@
78
<title>{{ page.title }}{% block title_content %}{% endblock %}</title>
89
{% match theme %}
910
{% when Some with (Theme::Dark) %}
10-
<link rel="stylesheet" href="{{ page.assets.css.dark.route() }}">
11+
<link rel="stylesheet" href="{{ path_prefix }}{{ page.assets.css.dark.route() }}">
1112
{% when Some with (Theme::Light) %}
12-
<link rel="stylesheet" href="{{ page.assets.css.light.route() }}">
13+
<link rel="stylesheet" href="{{ path_prefix }}{{ page.assets.css.light.route() }}">
1314
{% when Some with (Theme::System) %}
14-
<link rel="stylesheet" href="{{ page.assets.css.dark.route() }}" media="(prefers-color-scheme: dark)">
15-
<link rel="stylesheet" href="{{ page.assets.css.light.route() }}" media="(prefers-color-scheme: light)">
15+
<link rel="stylesheet" href="{{ path_prefix }}{{ page.assets.css.dark.route() }}" media="(prefers-color-scheme: dark)">
16+
<link rel="stylesheet" href="{{ path_prefix }}{{ page.assets.css.light.route() }}" media="(prefers-color-scheme: light)">
1617
{% when None %}
17-
<link rel="stylesheet" href="{{ page.assets.css.dark.route() }}" media="(prefers-color-scheme: dark)">
18-
<link rel="stylesheet" href="{{ page.assets.css.light.route() }}" media="(prefers-color-scheme: light)">
18+
<link rel="stylesheet" href="{{ path_prefix }}{{ page.assets.css.dark.route() }}" media="(prefers-color-scheme: dark)">
19+
<link rel="stylesheet" href="{{ path_prefix }}{{ page.assets.css.light.route() }}" media="(prefers-color-scheme: light)">
1920
{% endmatch %}
20-
<link rel="stylesheet" href="{{ page.assets.css.style.route() }}">
21-
<link rel="icon" href="{{ page.assets.favicon.route() }}" type="image/png">
22-
<noscript><link rel="stylesheet" href="{{ page.assets.css.no_js.route() }}"></noscript>
21+
<link rel="stylesheet" href="{{ path_prefix }}{{ page.assets.css.style.route() }}">
22+
<link rel="icon" href="{{ path_prefix }}{{ page.assets.favicon.route() }}" type="image/png">
23+
<noscript><link rel="stylesheet" href="{{ path_prefix }}{{ page.assets.css.no_js.route() }}"></noscript>
2324
{% block head %}{% endblock %}
2425
</head>
2526
<body>
2627
{% block body %}
2728
<div id="main-container">
2829
<header>
2930
<div class="nav-group">
30-
<a href="/" class="nav-button" title="home" aria-label="home">
31+
<a href="{{ path_prefix }}/" class="nav-button" title="home" aria-label="home">
3132
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 22V12h6v10"/></svg>
3233
</a>
3334
{% block title %}{% endblock %}
@@ -37,43 +38,43 @@
3738
<div class="theme-switcher">
3839
{% match theme %}
3940
{% when None %}
40-
<a href="/theme?pref=dark" class="theme-opt" title="dark mode">
41+
<a href="{{ path_prefix }}/theme?pref=dark" class="theme-opt" title="dark mode">
4142
<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/></svg>
4243
</a>
43-
<a href="/theme?pref=light" class="theme-opt" title="light mode">
44+
<a href="{{ path_prefix }}/theme?pref=light" class="theme-opt" title="light mode">
4445
<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/></svg>
4546
</a>
46-
<a href="/theme?pref=system" class="theme-opt active" title="auto mode">
47+
<a href="{{ path_prefix }}/theme?pref=system" class="theme-opt active" title="auto mode">
4748
<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="14" x="2" y="3" rx="2"/><path d="M8 21h8"/><path d="M12 17v4"/></svg>
4849
</a>
4950
{% when Some with (Theme::System) %}
50-
<a href="/theme?pref=dark" class="theme-opt" title="dark mode">
51+
<a href="{{ path_prefix }}/theme?pref=dark" class="theme-opt" title="dark mode">
5152
<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/></svg>
5253
</a>
53-
<a href="/theme?pref=light" class="theme-opt" title="light mode">
54+
<a href="{{ path_prefix }}/theme?pref=light" class="theme-opt" title="light mode">
5455
<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/></svg>
5556
</a>
56-
<a href="/theme?pref=system" class="theme-opt active" title="auto mode">
57+
<a href="{{ path_prefix }}/theme?pref=system" class="theme-opt active" title="auto mode">
5758
<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="14" x="2" y="3" rx="2"/><path d="M8 21h8"/><path d="M12 17v4"/></svg>
5859
</a>
5960
{% when Some with (Theme::Dark) %}
60-
<a href="/theme?pref=dark" class="theme-opt active" title="dark mode">
61+
<a href="{{ path_prefix }}/theme?pref=dark" class="theme-opt active" title="dark mode">
6162
<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/></svg>
6263
</a>
63-
<a href="/theme?pref=light" class="theme-opt" title="light mode">
64+
<a href="{{ path_prefix }}/theme?pref=light" class="theme-opt" title="light mode">
6465
<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/></svg>
6566
</a>
66-
<a href="/theme?pref=system" class="theme-opt" title="auto mode">
67+
<a href="{{ path_prefix }}/theme?pref=system" class="theme-opt" title="auto mode">
6768
<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="14" x="2" y="3" rx="2"/><path d="M8 21h8"/><path d="M12 17v4"/></svg>
6869
</a>
6970
{% when Some with (Theme::Light) %}
70-
<a href="/theme?pref=dark" class="theme-opt" title="dark mode">
71+
<a href="{{ path_prefix }}/theme?pref=dark" class="theme-opt" title="dark mode">
7172
<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z"/></svg>
7273
</a>
73-
<a href="/theme?pref=light" class="theme-opt active" title="light mode">
74+
<a href="{{ path_prefix }}/theme?pref=light" class="theme-opt active" title="light mode">
7475
<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2"/><path d="M12 20v2"/><path d="m4.93 4.93 1.41 1.41"/><path d="m17.66 17.66 1.41 1.41"/><path d="M2 12h2"/><path d="M20 12h2"/><path d="m6.34 17.66-1.41 1.41"/><path d="m19.07 4.93-1.41 1.41"/></svg>
7576
</a>
76-
<a href="/theme?pref=system" class="theme-opt" title="auto mode">
77+
<a href="{{ path_prefix }}/theme?pref=system" class="theme-opt" title="auto mode">
7778
<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="14" x="2" y="3" rx="2"/><path d="M8 21h8"/><path d="M12 17v4"/></svg>
7879
</a>
7980
{% endmatch %}

crates/wastebin_server/templates/burn.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<div class="flex-center">
44
<div class="centered-label">burn after reading</div>
55
<div class="centered-body">
6-
Copy and send <a class="text-link" href="/{{ key }}">this link</a>.<br>
6+
Copy and send <a class="text-link" href="{{ path_prefix }}/{{ key }}">this link</a>.<br>
77
After opening it for the first time, it will be deleted.
88
</div>
99
<div class="qr-container">

crates/wastebin_server/templates/encrypted.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
{% block content %}
88
<div class="flex-center">
99
<div class="centered-label">encrypted paste</div>
10-
<form action="/{{ id }}" method="post">
10+
<form action="{{ path_prefix }}/{{ id }}" method="post">
1111
<div class="controls">
1212
<div class="password-input-wrap">
1313
<input type="password" id="password" name="password" placeholder="password …" autofocus>

crates/wastebin_server/templates/error.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
<div class="flex-center">
44
<div class="error-emoji">😢</div>
55
<div class="centered-body">{{ description }}</div>
6-
<a class="text-link" href="/">go back</a>
6+
<a class="text-link" href="{{ path_prefix }}/">go back</a>
77
</div>
88
{% endblock %}

crates/wastebin_server/templates/formatted.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,6 @@
5555
</div>
5656
{{ html|safe }}
5757
{% if !is_available %}
58-
<script defer src="{{ page.assets.burn_js.route()}}"></script>
58+
<script defer src="{{ path_prefix }}{{ page.assets.burn_js.route()}}"></script>
5959
{% endif %}
6060
{% endblock %}

0 commit comments

Comments
 (0)