* Support for lang in URL as /fr/recipe/view/42
* Create a pages module in the frontend crate
This commit is contained in:
parent
b812525f4b
commit
418d31a127
12 changed files with 117 additions and 80 deletions
|
|
@ -1,10 +1,10 @@
|
|||
use std::{net::SocketAddr, path::Path};
|
||||
|
||||
use axum::{
|
||||
BoxError, Router,
|
||||
BoxError, Router, ServiceExt,
|
||||
error_handling::HandleErrorLayer,
|
||||
extract::{ConnectInfo, Extension, FromRef, Request, State},
|
||||
http::StatusCode,
|
||||
http::{StatusCode, Uri},
|
||||
middleware::{self, Next},
|
||||
response::Response,
|
||||
routing::{delete, get, patch, post, put},
|
||||
|
|
@ -14,6 +14,7 @@ use chrono::prelude::*;
|
|||
use clap::Parser;
|
||||
use config::Config;
|
||||
use itertools::Itertools;
|
||||
use tower::layer::Layer;
|
||||
use tower::{ServiceBuilder, buffer::BufferLayer, limit::RateLimitLayer};
|
||||
use tower_http::{
|
||||
services::{ServeDir, ServeFile},
|
||||
|
|
@ -290,13 +291,20 @@ async fn main() {
|
|||
.with_state(state)
|
||||
.nest_service("/favicon.ico", ServeFile::new("static/favicon.ico"))
|
||||
.nest_service("/static", ServeDir::new("static"))
|
||||
.layer(TraceLayer::new_for_http())
|
||||
.into_make_service_with_connect_info::<SocketAddr>();
|
||||
.layer(TraceLayer::new_for_http());
|
||||
|
||||
let url_rewriting_middleware = tower::util::MapRequestLayer::new(url_rewriting);
|
||||
let app_with_url_rewriting = url_rewriting_middleware.layer(app);
|
||||
|
||||
let addr = SocketAddr::from(([0, 0, 0, 0], port));
|
||||
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
|
||||
|
||||
axum::serve(listener, app).await.unwrap();
|
||||
axum::serve(
|
||||
listener,
|
||||
app_with_url_rewriting.into_make_service_with_connect_info::<SocketAddr>(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
async fn user_authentication(
|
||||
|
|
@ -312,54 +320,65 @@ async fn user_authentication(
|
|||
Ok(next.run(req).await)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Lang(Option<String>);
|
||||
|
||||
fn url_rewriting(mut req: Request) -> Request {
|
||||
// Here we are extracting the language from the url then rewriting it.
|
||||
// For example:
|
||||
// "/fr/recipe/view/1"
|
||||
// lang = "fr" and uri rewritten as = "/recipe/view/1"
|
||||
let lang_and_new_uri = 'lang_and_new_uri: {
|
||||
if let Some(path_query) = req.uri().path_and_query() {
|
||||
let mut parts = path_query.path().split('/');
|
||||
let _ = parts.next(); // Empty part due to the first '/'.
|
||||
if let Some(lang) = parts.next() {
|
||||
let available_codes = translation::available_codes();
|
||||
if available_codes.contains(&lang) {
|
||||
let mut rest: String = String::from("");
|
||||
for part in parts {
|
||||
rest.push('/');
|
||||
rest.push_str(part);
|
||||
}
|
||||
if let Some(query) = path_query.query() {
|
||||
rest.push('?');
|
||||
rest.push_str(query);
|
||||
}
|
||||
|
||||
if let Ok(new_uri) = rest.parse::<Uri>() {
|
||||
break 'lang_and_new_uri Some((lang.to_string(), new_uri));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
};
|
||||
|
||||
if let Some((lang, new_uri)) = lang_and_new_uri {
|
||||
*req.uri_mut() = new_uri;
|
||||
req.extensions_mut().insert(Lang(Some(lang)));
|
||||
} else {
|
||||
req.extensions_mut().insert(Lang(None));
|
||||
}
|
||||
|
||||
req
|
||||
}
|
||||
|
||||
/// The language of the current HTTP request is defined in the current order:
|
||||
/// - Extraction from the url: like in '/fr/recipe/view/42' (Not yet implemented).
|
||||
/// - Extraction from the url: like in '/fr/recipe/view/42'
|
||||
/// - Get from the user database record.
|
||||
/// - Get from the cookie.
|
||||
/// - Get from the HTTP header `accept-language`.
|
||||
/// - Set as `translation::DEFAULT_LANGUAGE_CODE`.
|
||||
async fn translation(
|
||||
Extension(lang): Extension<Lang>,
|
||||
Extension(user): Extension<Option<model::User>>,
|
||||
mut req: Request,
|
||||
next: Next,
|
||||
) -> Result<Response> {
|
||||
// Here we are extracting the language from the url then rewriting it.
|
||||
// For example:
|
||||
// "/fr/recipe/view/1"
|
||||
// lang = "fr" and uri rewritten as = "/recipe/view/1"
|
||||
// Disable because it doesn't work at this level, see:
|
||||
// https://docs.rs/axum/latest/axum/middleware/index.html#rewriting-request-uri-in-middleware
|
||||
|
||||
// let lang_and_new_uri = 'lang_from_uri: {
|
||||
// if let Some(path_query) = req.uri().path_and_query() {
|
||||
// event!(Level::INFO, "path: {:?}", path_query.path());
|
||||
// let mut parts = path_query.path().split('/');
|
||||
// let _ = parts.next(); // Empty part due to the first '/'.
|
||||
// if let Some(lang) = parts.next() {
|
||||
// let available_codes = translation::available_codes();
|
||||
// if available_codes.contains(&lang) {
|
||||
// let mut rest: String = String::from("");
|
||||
// for part in parts {
|
||||
// rest.push('/');
|
||||
// rest.push_str(part);
|
||||
// }
|
||||
// // let uri_builder = Uri::builder()
|
||||
// if let Ok(new_uri) = rest.parse::<Uri>() {
|
||||
// event!(Level::INFO, "path rewrite: {:?}", new_uri.path());
|
||||
// break 'lang_from_uri Some((lang.to_string(), new_uri));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// None
|
||||
// };
|
||||
// let language = if let Some((lang, uri)) = lang_and_new_uri {
|
||||
// *req.uri_mut() = uri; // Replace the URI without the language.
|
||||
// event!(Level::INFO, "URI: {:?}", req.uri());
|
||||
// lang
|
||||
// } else
|
||||
|
||||
let language = if let Some(user) = user {
|
||||
let language = if let Some(lang) = lang.0 {
|
||||
lang
|
||||
} else if let Some(user) = user {
|
||||
user.lang
|
||||
} else {
|
||||
let available_codes = translation::available_codes();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue