Merge branch 'main' into show-waterlevel
Some checks failed
CI/CD Pipeline / deploy-staging (push) Blocked by required conditions
CI/CD Pipeline / deploy-main (push) Blocked by required conditions
CI/CD Pipeline / test (push) Has been cancelled

This commit is contained in:
2024-05-16 14:45:31 +02:00
24 changed files with 314 additions and 162 deletions

View File

@ -2,7 +2,7 @@ use crate::model::{
boat::{Boat, BoatToAdd, BoatToUpdate},
location::Location,
log::Log,
user::{AdminUser, User, UserWithRolesAndNotificationCount},
user::{AdminUser, User, UserWithDetails},
};
use rocket::{
form::Form,
@ -33,7 +33,7 @@ async fn index(
context.insert("users", &users);
context.insert(
"loggedin_user",
&UserWithRolesAndNotificationCount::from_user(admin.user, db).await,
&UserWithDetails::from_user(admin.user, db).await,
);
Template::render("admin/boat/index", context.into_json())

View File

@ -10,7 +10,7 @@ use crate::model::log::Log;
use crate::model::mail::Mail;
use crate::model::role::Role;
use crate::model::user::AdminUser;
use crate::model::user::UserWithRolesAndNotificationCount;
use crate::model::user::UserWithDetails;
use crate::tera::Config;
#[get("/mail")]
@ -27,7 +27,7 @@ async fn index(
context.insert(
"loggedin_user",
&UserWithRolesAndNotificationCount::from_user(admin.user, db).await,
&UserWithDetails::from_user(admin.user, db).await,
);
context.insert("roles", &roles);

View File

@ -2,7 +2,7 @@ use crate::model::{
log::Log,
notification::Notification,
role::Role,
user::{AdminUser, User, UserWithRolesAndNotificationCount},
user::{AdminUser, User, UserWithDetails},
};
use rocket::{
form::Form,
@ -26,7 +26,7 @@ async fn index(
}
context.insert(
"loggedin_user",
&UserWithRolesAndNotificationCount::from_user(user.user, db).await,
&UserWithDetails::from_user(user.user, db).await,
);
context.insert("roles", &Role::all(db).await);

View File

@ -1,32 +1,12 @@
use crate::model::{
role::Role,
user::{SchnupperBetreuerUser, User, UserWithRolesAndNotificationCount},
user::{SchnupperBetreuerUser, User, UserWithDetails},
};
use futures::future::join_all;
use rocket::{
get,
http::Status,
request::{FlashMessage, FromRequest, Outcome},
routes, Request, Route, State,
};
use rocket::{get, request::FlashMessage, routes, Route, State};
use rocket_dyn_templates::{tera::Context, Template};
use sqlx::SqlitePool;
// Custom request guard to extract the Referer header
struct Referer(String);
#[rocket::async_trait]
impl<'r> FromRequest<'r> for Referer {
type Error = ();
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
match request.headers().get_one("Referer") {
Some(referer) => Outcome::Success(Referer(referer.to_string())),
None => Outcome::Error((Status::BadRequest, ())),
}
}
}
#[get("/schnupper")]
async fn index(
db: &State<SqlitePool>,
@ -38,9 +18,9 @@ async fn index(
let user_futures: Vec<_> = User::all_with_role(db, &schnupperant)
.await
.into_iter()
.map(|u| async move { UserWithRolesAndNotificationCount::from_user(u, db).await })
.map(|u| async move { UserWithDetails::from_user(u, db).await })
.collect();
let users: Vec<UserWithRolesAndNotificationCount> = join_all(user_futures).await;
let users: Vec<UserWithDetails> = join_all(user_futures).await;
let mut context = Context::new();
if let Some(msg) = flash {
@ -49,7 +29,7 @@ async fn index(
context.insert("schnupperanten", &users);
context.insert(
"loggedin_user",
&UserWithRolesAndNotificationCount::from_user(user.into(), db).await,
&UserWithDetails::from_user(user.into(), db).await,
);
Template::render("admin/schnupper/index", context.into_json())

View File

@ -1,14 +1,17 @@
use std::collections::HashMap;
use crate::model::{
family::Family,
log::Log,
logbook::Logbook,
role::Role,
user::{
AdminUser, User, UserWithMembershipPdf, UserWithRolesAndMembershipPdf,
UserWithRolesAndNotificationCount, VorstandUser,
use crate::{
model::{
family::Family,
log::Log,
logbook::Logbook,
role::Role,
user::{
AdminUser, User, UserWithDetails, UserWithMembershipPdf, UserWithRolesAndMembershipPdf,
VorstandUser,
},
},
tera::Config,
};
use futures::future::join_all;
use rocket::{
@ -67,10 +70,7 @@ async fn index(
context.insert("users", &users);
context.insert("roles", &roles);
context.insert("families", &families);
context.insert(
"loggedin_user",
&UserWithRolesAndNotificationCount::from_user(user, db).await,
);
context.insert("loggedin_user", &UserWithDetails::from_user(user, db).await);
Template::render("admin/user/index", context.into_json())
}
@ -102,10 +102,7 @@ async fn index_admin(
context.insert("users", &users);
context.insert("roles", &roles);
context.insert("families", &families);
context.insert(
"loggedin_user",
&UserWithRolesAndNotificationCount::from_user(user, db).await,
);
context.insert("loggedin_user", &UserWithDetails::from_user(user, db).await);
Template::render("admin/user/index", context.into_json())
}
@ -133,7 +130,7 @@ async fn fees(
}
context.insert(
"loggedin_user",
&UserWithRolesAndNotificationCount::from_user(admin.into(), db).await,
&UserWithDetails::from_user(admin.into(), db).await,
);
Template::render("admin/user/fees", context.into_json())
@ -153,7 +150,7 @@ async fn scheckbuch(
for s in scheckbooks {
scheckbooks_with_roles.push((
Logbook::completed_with_user(db, &s).await,
UserWithRolesAndNotificationCount::from_user(s, db).await,
UserWithDetails::from_user(s, db).await,
))
}
@ -164,7 +161,7 @@ async fn scheckbuch(
}
context.insert(
"loggedin_user",
&UserWithRolesAndNotificationCount::from_user(user.into(), db).await,
&UserWithDetails::from_user(user.into(), db).await,
);
Template::render("admin/user/scheckbuch", context.into_json())
@ -208,6 +205,26 @@ async fn fees_paid(
)
}
#[get("/user/<user>/send-welcome-mail")]
async fn send_welcome_mail(
db: &State<SqlitePool>,
_admin: AdminUser,
config: &State<Config>,
user: i32,
) -> Flash<Redirect> {
let Some(user) = User::find_by_id(db, user).await else {
return Flash::error(Redirect::to("/admin/user"), "User does not exist");
};
match user.send_welcome_email(db, &config.smtp_pw).await {
Ok(()) => Flash::success(
Redirect::to("/admin/user"),
format!("Willkommens-Email wurde an {} versandt.", user.name),
),
Err(e) => Flash::error(Redirect::to("/admin/user"), e),
}
}
#[get("/user/<user>/reset-pw")]
async fn resetpw(db: &State<SqlitePool>, _admin: AdminUser, user: i32) -> Flash<Redirect> {
let user = User::find_by_id(db, user).await;
@ -338,6 +355,7 @@ pub fn routes() -> Vec<Route> {
fees,
fees_paid,
scheckbuch,
download_membership_pdf
download_membership_pdf,
send_welcome_mail
]
}

View File

@ -1,7 +1,7 @@
use crate::model::{
boat::Boat,
boathouse::Boathouse,
user::{AdminUser, UserWithRolesAndNotificationCount, VorstandUser},
user::{AdminUser, UserWithDetails, VorstandUser},
};
use rocket::{
form::Form,
@ -39,7 +39,7 @@ async fn index(
context.insert(
"loggedin_user",
&UserWithRolesAndNotificationCount::from_user(admin.into(), db).await,
&UserWithDetails::from_user(admin.into(), db).await,
);
Template::render("board/boathouse", context.into_json())

View File

@ -13,7 +13,7 @@ use crate::{
model::{
boat::Boat,
boatdamage::{BoatDamage, BoatDamageFixed, BoatDamageToAdd, BoatDamageVerified},
user::{CoxUser, DonauLinzUser, TechUser, User, UserWithRolesAndNotificationCount},
user::{CoxUser, DonauLinzUser, TechUser, User, UserWithDetails},
},
tera::log::KioskCookie,
};
@ -59,7 +59,7 @@ async fn index(
context.insert("boats", &boats);
context.insert(
"loggedin_user",
&UserWithRolesAndNotificationCount::from_user(user.into(), db).await,
&UserWithDetails::from_user(user.into(), db).await,
);
Template::render("boatdamages", context.into_json())

View File

@ -15,7 +15,7 @@ use crate::{
boat::Boat,
boatreservation::{BoatReservation, BoatReservationToAdd},
log::Log,
user::{DonauLinzUser, User, UserWithRolesAndNotificationCount},
user::{DonauLinzUser, User, UserWithDetails},
},
tera::log::KioskCookie,
};
@ -75,7 +75,7 @@ async fn index(
context.insert("user", &User::all(db).await);
context.insert(
"loggedin_user",
&UserWithRolesAndNotificationCount::from_user(user.into(), db).await,
&UserWithDetails::from_user(user.into(), db).await,
);
Template::render("boatreservations", context.into_json())
@ -166,7 +166,7 @@ async fn update(
if user.id != reservation.user_id_applicant && !user.has_role(db, "admin").await {
return Flash::error(
Redirect::to("/boatreservation"),
format!("Not allowed to update reservation (only admins + creator do so)."),
"Not allowed to update reservation (only admins + creator do so).".to_string(),
);
}

View File

@ -18,7 +18,7 @@ use tera::Context;
use crate::model::{
log::Log,
user::{AdminUser, User, UserWithRolesAndNotificationCount},
user::{AdminUser, User, UserWithDetails},
};
#[derive(Serialize)]
@ -51,7 +51,7 @@ async fn send(db: &State<SqlitePool>, _user: AdminUser) -> Template {
Template::render(
"ergo.final",
context!(loggedin_user: &UserWithRolesAndNotificationCount::from_user(_user.user, db).await, thirty, dozen),
context!(loggedin_user: &UserWithDetails::from_user(_user.user, db).await, thirty, dozen),
)
}
@ -120,10 +120,7 @@ async fn index(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage<'_
if let Some(msg) = flash {
context.insert("flash", &msg.into_inner());
}
context.insert(
"loggedin_user",
&UserWithRolesAndNotificationCount::from_user(user, db).await,
);
context.insert("loggedin_user", &UserWithDetails::from_user(user, db).await);
context.insert("users", &users);
context.insert("thirty", &thirty);
context.insert("dozen", &dozen);

View File

@ -24,9 +24,7 @@ use crate::model::{
LogbookUpdateError,
},
logtype::LogType,
user::{
AdminUser, DonauLinzUser, User, UserWithRolesAndNotificationCount, UserWithWaterStatus,
},
user::{AdminUser, DonauLinzUser, User, UserWithDetails},
};
pub struct KioskCookie(String);
@ -51,23 +49,27 @@ async fn index(
) -> Template {
let boats = Boat::for_user(db, &user).await;
let mut coxes: Vec<UserWithWaterStatus> = futures::future::join_all(
let mut coxes: Vec<UserWithDetails> = futures::future::join_all(
User::cox(db)
.await
.into_iter()
.map(|user| UserWithWaterStatus::from_user(user, db)),
.map(|user| UserWithDetails::from_user(user, db)),
)
.await;
coxes.retain(|u| u.roles.contains(&"Donau Linz".into()));
coxes.retain(|u| {
u.roles.contains(&"Donau Linz".into()) || u.roles.contains(&"scheckbuch".into())
});
let mut users: Vec<UserWithWaterStatus> = futures::future::join_all(
let mut users: Vec<UserWithDetails> = futures::future::join_all(
User::all(db)
.await
.into_iter()
.map(|user| UserWithWaterStatus::from_user(user, db)),
.map(|user| UserWithDetails::from_user(user, db)),
)
.await;
users.retain(|u| u.roles.contains(&"Donau Linz".into()));
users.retain(|u| {
u.roles.contains(&"Donau Linz".into()) || u.roles.contains(&"scheckbuch".into())
});
let logtypes = LogType::all(db).await;
let distances = Logbook::distances(db).await;
@ -89,7 +91,7 @@ async fn index(
context.insert("logtypes", &logtypes);
context.insert(
"loggedin_user",
&UserWithRolesAndNotificationCount::from_user(user.into(), db).await,
&UserWithDetails::from_user(user.into(), db).await,
);
context.insert("on_water", &on_water);
context.insert("distances", &distances);
@ -103,7 +105,7 @@ async fn show(db: &State<SqlitePool>, user: DonauLinzUser) -> Template {
Template::render(
"log.completed",
context!(logs, loggedin_user: &UserWithRolesAndNotificationCount::from_user(user.into(), db).await),
context!(logs, loggedin_user: &UserWithDetails::from_user(user.into(), db).await),
)
}
@ -113,7 +115,7 @@ async fn show_for_year(db: &State<SqlitePool>, user: AdminUser, year: i32) -> Te
Template::render(
"log.completed",
context!(logs, loggedin_user: &UserWithRolesAndNotificationCount::from_user(user.user, db).await),
context!(logs, loggedin_user: &UserWithDetails::from_user(user.user, db).await),
)
}
@ -149,20 +151,30 @@ async fn kiosk(
_kiosk: KioskCookie,
) -> Template {
let boats = Boat::all(db).await;
let coxes: Vec<UserWithWaterStatus> = futures::future::join_all(
let mut coxes: Vec<UserWithDetails> = futures::future::join_all(
User::cox(db)
.await
.into_iter()
.map(|user| UserWithWaterStatus::from_user(user, db)),
.map(|user| UserWithDetails::from_user(user, db)),
)
.await;
let users: Vec<UserWithWaterStatus> = futures::future::join_all(
coxes.retain(|u| {
u.roles.contains(&"Donau Linz".into()) || u.roles.contains(&"scheckbuch".into())
});
let mut users: Vec<UserWithDetails> = futures::future::join_all(
User::all(db)
.await
.into_iter()
.map(|user| UserWithWaterStatus::from_user(user, db)),
.map(|user| UserWithDetails::from_user(user, db)),
)
.await;
users.retain(|u| {
u.roles.contains(&"Donau Linz".into()) || u.roles.contains(&"scheckbuch".into())
});
let logtypes = LogType::all(db).await;
let distances = Logbook::distances(db).await;
@ -250,7 +262,7 @@ async fn create_kiosk(
} else if let Some(shipmaster) = data.shipmaster {
User::find_by_id(db, shipmaster as i32).await.unwrap()
} else {
let Some(rower) = data.rowers.get(0) else {
let Some(rower) = data.rowers.first() else {
return Flash::error(
Redirect::to("/log"),
"Ausfahrt ohne Benutzer kann nicht angelegt werden.",

View File

@ -23,7 +23,7 @@ use tera::Context;
use crate::model::{
notification::Notification,
role::Role,
user::{User, UserWithRolesAndNotificationCount},
user::{User, UserWithDetails},
};
pub(crate) mod admin;
@ -53,10 +53,7 @@ async fn index(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage<'_
}
context.insert("notifications", &Notification::for_user(db, &user).await);
context.insert(
"loggedin_user",
&UserWithRolesAndNotificationCount::from_user(user, db).await,
);
context.insert("loggedin_user", &UserWithDetails::from_user(user, db).await);
Template::render("index", context.into_json())
}
@ -78,10 +75,7 @@ async fn steering(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage
context.insert("coxes", &coxes);
context.insert("bootskundige", &bootskundige);
context.insert(
"loggedin_user",
&UserWithRolesAndNotificationCount::from_user(user, db).await,
);
context.insert("loggedin_user", &UserWithDetails::from_user(user, db).await);
Template::render("steering", context.into_json())
}
@ -109,9 +103,7 @@ fn forbidden_error() -> Flash<Redirect> {
Flash::error(Redirect::to("/"), "Keine Berechtigung für diese Aktion. Wenn du der Meinung bist, dass du das machen darfst, melde dich bitte bei it@rudernlinz.at.")
}
struct Usage {
data: Vec<String>,
}
struct Usage {}
#[rocket::async_trait]
impl Fairing for Usage {
@ -197,7 +189,7 @@ pub fn config(rocket: Rocket<Build>) -> Rocket<Build> {
.register("/", catchers![unauthorized_error, forbidden_error])
.attach(Template::fairing())
.attach(AdHoc::config::<Config>())
.attach(Usage { data: Vec::new() })
.attach(Usage {})
}
#[cfg(test)]

View File

@ -13,7 +13,7 @@ use crate::model::{
logbook::Logbook,
tripdetails::TripDetails,
triptype::TripType,
user::{AllowedForPlannedTripsUser, User, UserWithRolesAndNotificationCount},
user::{AllowedForPlannedTripsUser, User, UserWithDetails},
usertrip::{UserTrip, UserTripDeleteError, UserTripError},
};
@ -44,10 +44,7 @@ async fn index(
}
context.insert("fee", &user.fee(db).await);
context.insert(
"loggedin_user",
&UserWithRolesAndNotificationCount::from_user(user, db).await,
);
context.insert("loggedin_user", &UserWithDetails::from_user(user, db).await);
context.insert("days", &days);
Template::render("planned", context.into_json())
}

View File

@ -4,7 +4,7 @@ use sqlx::SqlitePool;
use crate::model::{
stat::{self, BoatStat, Stat},
user::{DonauLinzUser, UserWithRolesAndNotificationCount},
user::{DonauLinzUser, UserWithDetails},
};
use super::log::KioskCookie;
@ -16,7 +16,7 @@ async fn index_boat(db: &State<SqlitePool>, user: DonauLinzUser) -> Template {
Template::render(
"stat.boats",
context!(loggedin_user: &UserWithRolesAndNotificationCount::from_user(user.into(), db).await, stat, kiosk),
context!(loggedin_user: &UserWithDetails::from_user(user.into(), db).await, stat, kiosk),
)
}
@ -38,7 +38,7 @@ async fn index(db: &State<SqlitePool>, user: DonauLinzUser, year: Option<i32>) -
Template::render(
"stat.people",
context!(loggedin_user: &UserWithRolesAndNotificationCount::from_user(user.into(), db).await, stat, personal, kiosk, guest_km, club_km),
context!(loggedin_user: &UserWithDetails::from_user(user.into(), db).await, stat, personal, kiosk, guest_km, club_km),
)
}