| 6 |
6 |
|
|
| 7 |
7 |
|
use axum::{
|
| 8 |
8 |
|
extract::State,
|
| 9 |
|
- |
http::{HeaderMap, StatusCode, Uri},
|
|
9 |
+ |
http::{HeaderMap, Uri},
|
| 10 |
10 |
|
response::{IntoResponse, Response},
|
| 11 |
11 |
|
};
|
| 12 |
12 |
|
use sqlx::PgPool;
|
| 95 |
95 |
|
)
|
| 96 |
96 |
|
.await
|
| 97 |
97 |
|
}
|
| 98 |
|
- |
_ => return Some(StatusCode::NOT_FOUND.into_response()),
|
|
98 |
+ |
_ => return Some(AppError::NotFound.into_response()),
|
| 99 |
99 |
|
};
|
| 100 |
100 |
|
|
| 101 |
101 |
|
Some(match result {
|
| 104 |
104 |
|
// "not found" (that made custom-domain outages look like missing pages
|
| 105 |
105 |
|
// with no signal, audit Run 13 Obs). Log it and let the AppError
|
| 106 |
106 |
|
// responder pick the real status (500 for infra errors).
|
| 107 |
|
- |
Err(AppError::NotFound) => StatusCode::NOT_FOUND.into_response(),
|
|
107 |
+ |
Err(AppError::NotFound) => AppError::NotFound.into_response(),
|
| 108 |
108 |
|
Err(e) => {
|
| 109 |
109 |
|
tracing::error!(error = ?e, host = %host, "custom domain render failed");
|
| 110 |
110 |
|
e.into_response()
|
| 133 |
133 |
|
MaybeUserVerified(maybe_user): MaybeUserVerified,
|
| 134 |
134 |
|
) -> Response {
|
| 135 |
135 |
|
let Some(host) = extract_host(&headers) else {
|
| 136 |
|
- |
return StatusCode::NOT_FOUND.into_response();
|
|
136 |
+ |
return AppError::NotFound.into_response();
|
| 137 |
137 |
|
};
|
| 138 |
138 |
|
|
| 139 |
|
- |
// MNW domains → standard 404
|
|
139 |
+ |
// MNW domains fall through to the app's own branded 404. It must carry a
|
|
140 |
+ |
// body: Caddy's `handle_errors` fires only on errors Caddy itself generates,
|
|
141 |
+ |
// never on a 4xx that a healthy upstream returned, so a bare
|
|
142 |
+ |
// `StatusCode::NOT_FOUND` here reached the visitor as an empty page.
|
|
143 |
+ |
// `AppError::NotFound` renders the branded template, and `json_error_layer`
|
|
144 |
+ |
// still swaps it for JSON on /api routes.
|
| 140 |
145 |
|
if is_mnw_domain(&host) {
|
| 141 |
|
- |
return StatusCode::NOT_FOUND.into_response();
|
|
146 |
+ |
return AppError::NotFound.into_response();
|
| 142 |
147 |
|
}
|
| 143 |
148 |
|
|
| 144 |
149 |
|
// Look up custom domain
|
| 145 |
150 |
|
let user_id = match caches.domain_cache.get(&host) {
|
| 146 |
151 |
|
Some(entry) => *entry.value(),
|
| 147 |
|
- |
None => return StatusCode::NOT_FOUND.into_response(),
|
|
152 |
+ |
None => return AppError::NotFound.into_response(),
|
| 148 |
153 |
|
};
|
| 149 |
154 |
|
|
| 150 |
155 |
|
// Route by path segments
|
| 177 |
182 |
|
)
|
| 178 |
183 |
|
.await
|
| 179 |
184 |
|
}
|
| 180 |
|
- |
_ => return StatusCode::NOT_FOUND.into_response(),
|
|
185 |
+ |
_ => return AppError::NotFound.into_response(),
|
| 181 |
186 |
|
};
|
| 182 |
187 |
|
|
| 183 |
188 |
|
match result {
|
| 184 |
189 |
|
Ok(response) => response,
|
| 185 |
190 |
|
// Genuine miss → clean 404; a DB/render error must surface (log + real
|
| 186 |
191 |
|
// status) rather than silently reading as "not found" (audit Run 13 Obs).
|
| 187 |
|
- |
Err(AppError::NotFound) => StatusCode::NOT_FOUND.into_response(),
|
|
192 |
+ |
Err(AppError::NotFound) => AppError::NotFound.into_response(),
|
| 188 |
193 |
|
Err(e) => {
|
| 189 |
194 |
|
tracing::error!(error = ?e, host = %host, "custom domain render failed");
|
| 190 |
195 |
|
e.into_response()
|