Improved error messages. Added README.md

This commit is contained in:
2025-02-08 15:29:16 +01:00
parent a5cfc96814
commit 5d80c4951e
8 changed files with 865 additions and 516 deletions
+1
View File
@@ -18,3 +18,4 @@ playwright/.cache/
/.settings/
/.vscode/
/config.toml
/html/
Generated
+778 -492
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -20,7 +20,7 @@ server_fn = { version = "0.6.13", features = ["multipart"] }
leptos_router = { version = "0.6.13" }
serde = { version = "1", features = ["derive"] }
serde_json = "1.0.117"
wasm-bindgen = "=0.2.93"
wasm-bindgen = "=0.2.100"
web-sys = { version = "0.3.69", features = ["Navigator"] }
lazy_static = "1.4.0"
chrono = { version = "0.4.38", features = ["serde"]}
@@ -35,7 +35,7 @@ toml = "0.8.12"
log = "0.4.21"
env_logger = "0.11.3"
getopts = "0.2.21"
leptos-use = "0.12.0"
leptos-use = "0.13.13"
lettre = {version = "0.11.6", features = ["tokio1-native-tls", "smtp-transport", "file-transport"], optional = true}
leptos-captcha = "0.2.0"
charts-rs = { version = "0.3.5", optional = true}
+46
View File
@@ -0,0 +1,46 @@
<picture>
<source srcset="https://demo.rezervovator.cz/rezervovator_l.svg">
<img src="https://demo.rezervovator.cz/rezervovator_l.svg" alt="Rezervovator Logo">
</picture>
# Rezervovator
Simple application for booking sport or service facilities.
## Building project
You will need cargo-leptos tool:
`cargo install cargo-leptos`
## Running project
`cargo leptos watch`
## Executing a Server on a Remote Machine Without the Toolchain
After running a `cargo leptos build --release` the minimum files needed are:
1. The server binary located in `target/server/release`
2. The `site` directory and all files within located in `target/site`
Copy these files to your remote server. The directory structure should be:
```text
leptos_start
site/
```
Set the following environment variables (updating for your project as needed):
```sh
export LEPTOS_OUTPUT_NAME="leptos_start"
export LEPTOS_SITE_ROOT="site"
export LEPTOS_SITE_PKG_DIR="pkg"
export LEPTOS_SITE_ADDR="127.0.0.1:3000"
export LEPTOS_RELOAD_PORT="3001"
```
Finally, run the server binary.
## Notes about SSG and Trunk:
Although it is not recommended, you can also run your project without server integration using the feature `csr` and `trunk serve`:
`trunk serve --open --features csr`
This may be useful for integrating external tools which require a static site, e.g. `tauri`.
+1 -1
View File
@@ -68,7 +68,7 @@ fn app_footer() -> impl IntoView {
view! {
<footer class="content-footer footer bg-footer-theme" style={move || if loc.pathname.get().starts_with("/admin") {"display: none;"} else {"display: block;"}}>
<div class="mb-2 mb-md-0" >
<div class="mb-2 mb-md-0" align="center">
<a href="https://rezervovator.cz" target="_blank"><img src="/rezervovator_l.svg" width="110"/></a> {format!(" v {}", env!("CARGO_PKG_VERSION"))}
</div>
</footer>
+26 -17
View File
@@ -105,9 +105,10 @@ cfg_if! { if #[cfg(feature = "ssr")] {
pub async fn login(username: String, password: String) -> Result<ApiResponse<()>, ServerFnError> {
use actix_session::*;
use leptos_actix::extract;
use actix_web::http::StatusCode;
use leptos_actix::ResponseOptions;
//use actix_web::http::StatusCode;
//use leptos_actix::ResponseOptions;
use crate::backend::get_pool;
use crate::locales::trl;
let pool = get_pool().await?;
let user = user_from_login(&pool, &username).await.unwrap_or(User::default());
@@ -123,10 +124,10 @@ pub async fn login(username: String, password: String) -> Result<ApiResponse<()>
}
warn!("Login failed for user {}", username);
let response = expect_context::<ResponseOptions>();
response.set_status(StatusCode::UNAUTHORIZED);
//let response = expect_context::<ResponseOptions>();
//response.set_status(StatusCode::UNAUTHORIZED);
return Ok(ApiResponse::Error("Bad username or password".to_string()))
Ok(ApiResponse::Error(trl("Bad username or password")()))
}
#[server]
@@ -173,15 +174,16 @@ pub async fn get_users() -> Result<ApiResponse<Vec<User>>, ServerFnError> {
pub async fn update_profile(user: UserProfile) -> Result<ApiResponse<()>, ServerFnError> {
use crate::user_check;
use crate::backend::get_pool;
use crate::locales::trl;
user_check!(user.login());
let usr = logged_in_user().await.unwrap_or(User::default());
if !usr.admin && user.admin() {
let response = expect_context::<ResponseOptions>();
response.set_status(StatusCode::FORBIDDEN);
//let response = expect_context::<ResponseOptions>();
//response.set_status(StatusCode::FORBIDDEN);
return Ok(ApiResponse::Error("You can't escalate your privileges".to_string()))
return Ok(ApiResponse::Error(trl("You can't escalate your privileges")()))
}
let pool = get_pool().await?;
@@ -213,6 +215,7 @@ impl ForValidation for UpdateProfile {
pub async fn change_pwd(new_pw: PwdChange) -> Result<ApiResponse<()>, ServerFnError> {
use crate::user_check;
use crate::backend::get_pool;
use crate::locales::trl;
user_check!(new_pw.login());
@@ -222,10 +225,10 @@ pub async fn change_pwd(new_pw: PwdChange) -> Result<ApiResponse<()>, ServerFnEr
if (!user.admin || user.login == new_pw.login())
&& !pwhash::bcrypt::verify(new_pw.old_password(), &usr.password) {
let response = expect_context::<ResponseOptions>();
response.set_status(StatusCode::UNAUTHORIZED);
//let response = expect_context::<ResponseOptions>();
//response.set_status(StatusCode::UNAUTHORIZED);
return Ok(ApiResponse::Error("Invalid old password".to_string()))
return Ok(ApiResponse::Error(trl("Invalid old password")()))
}
sqlx::query(r#"UPDATE "user" SET password = $1 WHERE login = $2"#)
@@ -249,6 +252,7 @@ impl ForValidation for ChangePwd {
pub async fn create_user(user: UserProfile) -> Result<ApiResponse<()>, ServerFnError> {
use crate::perm_check;
use crate::backend::get_pool;
use crate::locales::trl;
perm_check!(is_admin);
@@ -259,10 +263,10 @@ pub async fn create_user(user: UserProfile) -> Result<ApiResponse<()>, ServerFnE
.await?;
if count.0 != 0 {
let response = expect_context::<ResponseOptions>();
response.set_status(StatusCode::CONFLICT);
//let response = expect_context::<ResponseOptions>();
//response.set_status(StatusCode::CONFLICT);
return Ok(ApiResponse::Error("Username already exists".to_string()));
return Ok(ApiResponse::Error(trl("Username already exists")()));
}
let usr_pw = user.password().clone();
@@ -292,15 +296,16 @@ impl ForValidation for CreateUser {
pub async fn delete_user(id: i32) -> Result<ApiResponse<()>, ServerFnError> {
use crate::perm_check;
use crate::backend::get_pool;
use crate::locales::trl;
perm_check!(is_admin);
let user = logged_in_user().await.unwrap_or_default();
if user.id() == id {
let response = expect_context::<ResponseOptions>();
response.set_status(StatusCode::NOT_ACCEPTABLE);
//let response = expect_context::<ResponseOptions>();
//response.set_status(StatusCode::NOT_ACCEPTABLE);
return Ok(ApiResponse::Error("You can't delete yourself".to_string()))
return Ok(ApiResponse::Error(trl("You can't delete yourself")()))
}
sqlx::query(r#"DELETE FROM "user" WHERE id=$1"#)
@@ -317,5 +322,9 @@ pub async fn delete_user(id: i32) -> Result<ApiResponse<()>, ServerFnError> {
pub async fn get_pow() -> Result<String, ServerFnError> {
use leptos_captcha::spow::pow::Pow;
if !cfg!(debug_assertions) {
Ok(Pow::with_difficulty(10, 10)?.to_string())
} else {
Ok(Pow::new(10)?.to_string())
}
}
+3 -2
View File
@@ -13,11 +13,12 @@ fn about(opener: DialogOpener) -> impl IntoView {
<ModalBody>
<img src="/rezervovator_l.svg" width="180"/> <br /><br/>
<p>
{trl("Online booking application for sports facilities and service providers.")}<br/><br/>
{trl("Online booking application for sports facilities and service providers.")}<br/>
{format!(" v {}", env!("CARGO_PKG_VERSION"))}<br/><br/>
<div align="center">
<a href="https://www.rust-lang.org" target="_blank"><img src="/rust.png" height="40"/></a>" "
<a href="https://leptos.dev" target="_blank"><img src="/Leptos_logo.png" height="40"/></a> <br/><br/>
"(c) 2023 - 2024"
"(c) 2023 - 2025"
</div>
</p>
</ModalBody>
+7 -1
View File
@@ -161,7 +161,13 @@ lazy_static! {
("Closing days: ", "Zavírací dny: "),
("Closing days", "Zavírací dny"),
("From", "Od"),
("To", "do")
("To", "do"),
("Delete closing days", "Smazat zavírací dny"),
("Are you sure you want to delete closing days?", "Opravdu chcete smazat zavírací dny?"),
("Bad username or password", "Špatné uživatelské jméno nebo heslo"),
("You can't escalate your privileges", "Nemůžete povýšit práva sami sobě"),
("Username already exists", "Uživatel již existuje"),
("You can't delete yourself", "Nemůžete smazat sami sebe")
])),
("sk", HashMap::from( [
("Dashboard", "Prehlad"),