Add Base Path

This commit is contained in:
gabatxo1312 2026-01-31 00:05:53 +01:00
parent 4eb913e7a1
commit f6d4e7c556
No known key found for this signature in database
9 changed files with 100 additions and 23 deletions

View File

@ -1,5 +1,6 @@
database_path = "" database_path = ""
locale = "fr" locale = "fr"
base_url = ""
[listener] [listener]
port = 8000 port = 8000

View File

@ -2,11 +2,12 @@ use askama::Template;
use askama_web::WebTemplate; use askama_web::WebTemplate;
use axum::{ use axum::{
Router, Router,
extract::State,
routing::{get, post}, routing::{get, post},
}; };
use static_serve::embed_assets; use static_serve::embed_assets;
use crate::state::AppState; use crate::{routes::template_ctx::TemplateCtx, state::AppState};
mod migrations; mod migrations;
mod models; mod models;
@ -44,8 +45,14 @@ pub fn build_app(state: AppState) -> Router {
#[derive(Template, WebTemplate)] #[derive(Template, WebTemplate)]
#[template(path = "404.html")] #[template(path = "404.html")]
struct NotFoundTemplate {} struct NotFoundTemplate {
pub ctx: TemplateCtx,
pub async fn error_handler() -> impl axum::response::IntoResponse { }
NotFoundTemplate {}
pub async fn error_handler(State(state): State<AppState>) -> impl axum::response::IntoResponse {
NotFoundTemplate {
ctx: TemplateCtx {
base_path: state.config.base_path,
},
}
} }

View File

@ -13,7 +13,9 @@ use serde::Deserialize;
use serde_with::{NoneAsEmptyString, serde_as}; use serde_with::{NoneAsEmptyString, serde_as};
use snafu::prelude::*; use snafu::prelude::*;
use crate::{models::book::Model as BookModel, state::error::CSVSnafu}; use crate::{
models::book::Model as BookModel, routes::template_ctx::TemplateCtx, state::error::CSVSnafu,
};
use crate::{models::user::Model as UserModel, state::error::IOSnafu}; use crate::{models::user::Model as UserModel, state::error::IOSnafu};
use crate::{ use crate::{
@ -55,6 +57,7 @@ struct BookIndexTemplate {
current_page: u64, current_page: u64,
total_page: u64, total_page: u64,
base_query: String, base_query: String,
ctx: TemplateCtx,
} }
pub async fn index( pub async fn index(
@ -73,7 +76,7 @@ pub async fn index(
.context(UserSnafu)?; .context(UserSnafu)?;
// Get all Book filtered with query // Get all Book filtered with query
let books_paginate = BookOperator::new(state) let books_paginate = BookOperator::new(state.clone())
.all_paginate(page, Some(query.clone())) .all_paginate(page, Some(query.clone()))
.await .await
.context(BookSnafu)?; .context(BookSnafu)?;
@ -127,6 +130,9 @@ pub async fn index(
current_page: books_paginate.current_page, current_page: books_paginate.current_page,
total_page: books_paginate.total_page, total_page: books_paginate.total_page,
base_query, base_query,
ctx: TemplateCtx {
base_path: state.config.base_path,
},
}) })
} }
@ -136,6 +142,7 @@ struct ShowBookTemplate {
book: BookModel, book: BookModel,
owner: UserModel, owner: UserModel,
current_holder: Option<UserModel>, current_holder: Option<UserModel>,
ctx: TemplateCtx,
} }
pub async fn show( pub async fn show(
@ -168,6 +175,9 @@ pub async fn show(
book, book,
owner, owner,
current_holder, current_holder,
ctx: TemplateCtx {
base_path: state.config.base_path,
},
}) })
} }
@ -200,14 +210,23 @@ pub async fn create(
#[template(path = "books/new.html")] #[template(path = "books/new.html")]
struct NewBookTemplate { struct NewBookTemplate {
users: Vec<UserModel>, users: Vec<UserModel>,
ctx: TemplateCtx,
} }
pub async fn new( pub async fn new(
State(state): State<AppState>, State(state): State<AppState>,
) -> Result<impl axum::response::IntoResponse, AppStateError> { ) -> Result<impl axum::response::IntoResponse, AppStateError> {
let users = UserOperator::new(state).all().await.context(UserSnafu)?; let users = UserOperator::new(state.clone())
.all()
.await
.context(UserSnafu)?;
Ok(NewBookTemplate { users }) Ok(NewBookTemplate {
users,
ctx: TemplateCtx {
base_path: state.config.base_path,
},
})
} }
#[derive(Template, WebTemplate)] #[derive(Template, WebTemplate)]
@ -215,6 +234,7 @@ pub async fn new(
struct EditBookTemplate { struct EditBookTemplate {
users: Vec<UserModel>, users: Vec<UserModel>,
book: BookModel, book: BookModel,
ctx: TemplateCtx,
} }
pub async fn edit( pub async fn edit(
@ -225,12 +245,18 @@ pub async fn edit(
.all() .all()
.await .await
.context(UserSnafu)?; .context(UserSnafu)?;
let book = BookOperator::new(state) let book = BookOperator::new(state.clone())
.find_by_id(id) .find_by_id(id)
.await .await
.context(BookSnafu)?; .context(BookSnafu)?;
Ok(EditBookTemplate { users, book }) Ok(EditBookTemplate {
users,
book,
ctx: TemplateCtx {
base_path: state.config.base_path,
},
})
} }
pub async fn update( pub async fn update(

View File

@ -1,2 +1,3 @@
pub mod book; pub mod book;
pub mod template_ctx;
pub mod user; pub mod user;

View File

@ -0,0 +1,14 @@
#[derive(Clone)]
pub struct TemplateCtx {
pub base_path: String,
}
impl TemplateCtx {
pub fn asset(&self, path: &str) -> String {
if self.base_path.is_empty() || self.base_path == "/" {
format!("/{}", path)
} else {
format!("{}/{}", self.base_path.trim_end_matches('/'), path)
}
}
}

View File

@ -16,6 +16,7 @@ use crate::{
book::BookOperator, book::BookOperator,
user::{self, UserOperator}, user::{self, UserOperator},
}, },
routes::template_ctx::TemplateCtx,
state::{ state::{
AppState, AppState,
error::{AppStateError, BookSnafu, UserSnafu}, error::{AppStateError, BookSnafu, UserSnafu},
@ -27,6 +28,7 @@ use crate::{
struct UsersIndexTemplate { struct UsersIndexTemplate {
users_with_books_number: Vec<UserWithBookNumber>, users_with_books_number: Vec<UserWithBookNumber>,
query: IndexQuery, query: IndexQuery,
ctx: TemplateCtx,
} }
pub struct UserWithBookNumber { pub struct UserWithBookNumber {
@ -86,6 +88,9 @@ pub async fn index(
Ok(UsersIndexTemplate { Ok(UsersIndexTemplate {
users_with_books_number: result, users_with_books_number: result,
query, query,
ctx: TemplateCtx {
base_path: state.config.base_path,
},
}) })
} }
@ -135,24 +140,36 @@ pub async fn delete(
#[template(path = "users/edit.html")] #[template(path = "users/edit.html")]
struct EditTemplate { struct EditTemplate {
user: user::Model, user: user::Model,
ctx: TemplateCtx,
} }
pub async fn edit( pub async fn edit(
State(state): State<AppState>, State(state): State<AppState>,
Path(id): Path<i32>, Path(id): Path<i32>,
) -> Result<impl axum::response::IntoResponse, AppStateError> { ) -> Result<impl axum::response::IntoResponse, AppStateError> {
let user = UserOperator::new(state) let user = UserOperator::new(state.clone())
.find_by_id(id) .find_by_id(id)
.await .await
.context(UserSnafu)?; .context(UserSnafu)?;
Ok(EditTemplate { user }) Ok(EditTemplate {
user,
ctx: TemplateCtx {
base_path: state.config.base_path,
},
})
} }
#[derive(Template, WebTemplate)] #[derive(Template, WebTemplate)]
#[template(path = "users/new.html")] #[template(path = "users/new.html")]
struct NewTemplate {} struct NewTemplate {
ctx: TemplateCtx,
pub async fn new() -> impl axum::response::IntoResponse { }
NewTemplate {}
pub async fn new(State(state): State<AppState>) -> impl axum::response::IntoResponse {
NewTemplate {
ctx: TemplateCtx {
base_path: state.config.base_path,
},
}
} }

View File

@ -33,6 +33,7 @@ pub struct AppConfig {
#[serde(default = "AppConfig::default_sqlite_path")] #[serde(default = "AppConfig::default_sqlite_path")]
pub database_path: Utf8PathBuf, pub database_path: Utf8PathBuf,
pub locale: String, pub locale: String,
pub base_path: String,
pub listener: Listener, pub listener: Listener,
} }
@ -40,6 +41,7 @@ impl Default for AppConfig {
fn default() -> Self { fn default() -> Self {
AppConfig { AppConfig {
database_path: Self::default_sqlite_path(), database_path: Self::default_sqlite_path(),
base_path: Self::default_base_path(),
locale: Self::default_locale(), locale: Self::default_locale(),
listener: Listener::default(), listener: Listener::default(),
} }
@ -98,6 +100,10 @@ impl AppConfig {
"en".to_string() "en".to_string()
} }
fn default_base_path() -> String {
"".to_string()
}
pub fn default_sqlite_path() -> Utf8PathBuf { pub fn default_sqlite_path() -> Utf8PathBuf {
Self::config_path().join("db.sqlite") Self::config_path().join("db.sqlite")
} }

View File

@ -6,6 +6,7 @@ use snafu::prelude::*;
use crate::{ use crate::{
models::{book::BookError, user::UserError}, models::{book::BookError, user::UserError},
routes::template_ctx::TemplateCtx,
state::config::ConfigError, state::config::ConfigError,
}; };
@ -47,6 +48,7 @@ pub enum AppStateError {
#[template(path = "error.html")] #[template(path = "error.html")]
struct ErrorTemplate { struct ErrorTemplate {
state: AppStateErrorContext, state: AppStateErrorContext,
ctx: TemplateCtx,
} }
struct AppStateErrorContext { struct AppStateErrorContext {
@ -66,6 +68,9 @@ impl IntoResponse for AppStateError {
let error_context = AppStateErrorContext::from(self); let error_context = AppStateErrorContext::from(self);
ErrorTemplate { ErrorTemplate {
state: error_context, state: error_context,
ctx: TemplateCtx {
base_path: "".to_string(),
},
} }
.into_response() .into_response()
} }

View File

@ -4,12 +4,12 @@
<meta charset="utf-8"> <meta charset="utf-8">
<title>{% block title %}{{ t!("name") }}{% endblock %}</title> <title>{% block title %}{{ t!("name") }}{% endblock %}</title>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="/assets/css/bootstrap.css"> <link rel="stylesheet" href='{{ ctx.asset("assets/css/bootstrap.css") }}'>
<link rel="stylesheet" href="/assets/css/main.css"> <link rel="stylesheet" href='{{ ctx.asset("assets/css/main.css") }}'>
<link rel="icon" type="image/png" sizes="32x32" href="/assets/images/favicon.png"> <link rel="icon" type="image/png" sizes="32x32" href="/assets/images/favicon.png">
<link rel="stylesheet" href="/assets/css/fork-awesome.min.css"> <link rel="stylesheet" href='{{ ctx.asset("assets/css/fork-awesome.min.css") }}'>
<link rel="icon" type="image/x-icon" href="/assets/images/favicon.ico"> <link rel="icon" type="image/x-icon" href='{{ ctx.asset("assets/images/favicon.ico") }}'>
{% block extra_head %}{% endblock extra_head %} {% block extra_head %}{% endblock extra_head %}
</head> </head>
@ -26,7 +26,7 @@
{% endblock %} {% endblock %}
</footer> </footer>
<script src="/assets/js/bootstrap.min.js"></script> <script src='{{ ctx.asset("assets/js/bootstrap.min.js") }}'></script>
<script src="/assets/js/script.js"></script> <script src='{{ ctx.asset("assets/js/script.js") }}'></script>
</body> </body>
</html> </html>