Compare commits
3 Commits
main
...
2b26ac23be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2b26ac23be | ||
| 5c1d8876be | |||
| e89c5c7439 |
1345
Cargo.lock
generated
1345
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -23,11 +23,11 @@ tera = { version = "1.20", features = ["date-locale"], optional = true}
|
|||||||
ics = "0.5"
|
ics = "0.5"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
lettre = "0.11"
|
lettre = "0.11"
|
||||||
csv = "1.3"
|
csv = "1.4"
|
||||||
itertools = "0.14"
|
itertools = "0.14"
|
||||||
job_scheduler_ng = "2.2"
|
job_scheduler_ng = "2.4"
|
||||||
ureq = { version = "3.0", features = ["json"] }
|
ureq = { version = "3.1", features = ["json"] }
|
||||||
regex = "1.11"
|
regex = "1.12"
|
||||||
urlencoding = "2.1"
|
urlencoding = "2.1"
|
||||||
|
|
||||||
[target.'cfg(not(windows))'.dependencies]
|
[target.'cfg(not(windows))'.dependencies]
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
mod waterlevel;
|
mod waterlevel;
|
||||||
mod weather;
|
mod weather;
|
||||||
mod yearly_role_cleanup;
|
|
||||||
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
@@ -14,7 +13,7 @@ pub fn schedule(db: &SqlitePool, config: &Config) {
|
|||||||
let db = db.clone();
|
let db = db.clone();
|
||||||
let openweathermap_key = config.openweathermap_key.clone();
|
let openweathermap_key = config.openweathermap_key.clone();
|
||||||
|
|
||||||
tokio::task::spawn(async move {
|
tokio::task::spawn(async {
|
||||||
if let Err(e) = waterlevel::update(&db).await {
|
if let Err(e) = waterlevel::update(&db).await {
|
||||||
log::error!("Water level update error: {e}, trying again next time");
|
log::error!("Water level update error: {e}, trying again next time");
|
||||||
}
|
}
|
||||||
@@ -25,9 +24,8 @@ pub fn schedule(db: &SqlitePool, config: &Config) {
|
|||||||
let mut sched = JobScheduler::new();
|
let mut sched = JobScheduler::new();
|
||||||
|
|
||||||
// Every hour
|
// Every hour
|
||||||
let db_for_hourly = db.clone();
|
|
||||||
sched.add(Job::new("0 0 * * * * *".parse().unwrap(), move || {
|
sched.add(Job::new("0 0 * * * * *".parse().unwrap(), move || {
|
||||||
let db_clone = db_for_hourly.clone();
|
let db_clone = db.clone();
|
||||||
// Use block_in_place to run async code in the synchronous function; TODO: Make it
|
// Use block_in_place to run async code in the synchronous function; TODO: Make it
|
||||||
// nicer one's rust (stable) support async closures
|
// nicer one's rust (stable) support async closures
|
||||||
task::block_in_place(|| {
|
task::block_in_place(|| {
|
||||||
@@ -42,19 +40,6 @@ pub fn schedule(db: &SqlitePool, config: &Config) {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// January 1st at midnight - yearly role cleanup
|
|
||||||
let db_for_yearly = db.clone();
|
|
||||||
sched.add(Job::new("0 0 0 1 1 * *".parse().unwrap(), move || {
|
|
||||||
let db_clone = db_for_yearly.clone();
|
|
||||||
task::block_in_place(|| {
|
|
||||||
tokio::runtime::Handle::current().block_on(async {
|
|
||||||
if let Err(e) = yearly_role_cleanup::cleanup_roles(&db_clone).await {
|
|
||||||
log::error!("Yearly role cleanup error: {e}");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
let mut interval = time::interval(Duration::from_secs(60));
|
let mut interval = time::interval(Duration::from_secs(60));
|
||||||
loop {
|
loop {
|
||||||
sched.tick();
|
sched.tick();
|
||||||
|
|||||||
@@ -1,158 +0,0 @@
|
|||||||
use crate::model::{notification::Notification, role::Role};
|
|
||||||
use sqlx::SqlitePool;
|
|
||||||
|
|
||||||
pub async fn cleanup_roles(db: &SqlitePool) -> Result<(), String> {
|
|
||||||
log::info!("Starting yearly role cleanup...");
|
|
||||||
|
|
||||||
let mut tx = db.begin().await.map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
// Find all roles to remove
|
|
||||||
let paid_role = Role::find_by_name_tx(&mut tx, "paid")
|
|
||||||
.await
|
|
||||||
.ok_or("Role 'paid' not found")?;
|
|
||||||
let schueler_role = Role::find_by_name_tx(&mut tx, "Schüler")
|
|
||||||
.await
|
|
||||||
.ok_or("Role 'Schüler' not found")?;
|
|
||||||
let student_role = Role::find_by_name_tx(&mut tx, "Student")
|
|
||||||
.await
|
|
||||||
.ok_or("Role 'Student' not found")?;
|
|
||||||
let no_einschreibgebuehr_role = Role::find_by_name_tx(&mut tx, "no-einschreibgebuehr")
|
|
||||||
.await
|
|
||||||
.ok_or("Role 'no-einschreibgebuehr' not found")?;
|
|
||||||
let half_rennrudern_role = Role::find_by_name_tx(&mut tx, "half-rennrudern")
|
|
||||||
.await
|
|
||||||
.ok_or("Role 'half-rennrudern' not found")?;
|
|
||||||
let participated_schnupperkurs_role =
|
|
||||||
Role::find_by_name_tx(&mut tx, "participated_schnupperkurs")
|
|
||||||
.await
|
|
||||||
.ok_or("Role 'participated_schnupperkurs' not found")?;
|
|
||||||
|
|
||||||
// Find scheckbuch role (needed to exclude users from "paid" removal -> they have still paid
|
|
||||||
// for the scheckbuch)
|
|
||||||
let scheckbuch_role = Role::find_by_name_tx(&mut tx, "scheckbuch")
|
|
||||||
.await
|
|
||||||
.ok_or("Role 'scheckbuch' not found")?;
|
|
||||||
|
|
||||||
// Remove "paid" role from all users EXCEPT those with scheckbuch role
|
|
||||||
let paid_removed = sqlx::query!(
|
|
||||||
"DELETE FROM user_role
|
|
||||||
WHERE role_id = ?
|
|
||||||
AND user_id NOT IN (
|
|
||||||
SELECT user_id FROM user_role WHERE role_id = ?
|
|
||||||
)",
|
|
||||||
paid_role.id,
|
|
||||||
scheckbuch_role.id
|
|
||||||
)
|
|
||||||
.execute(&mut *tx)
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.to_string())?
|
|
||||||
.rows_affected();
|
|
||||||
|
|
||||||
// Remove other roles from all users
|
|
||||||
let schueler_removed =
|
|
||||||
sqlx::query!("DELETE FROM user_role WHERE role_id = ?", schueler_role.id)
|
|
||||||
.execute(&mut *tx)
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.to_string())?
|
|
||||||
.rows_affected();
|
|
||||||
|
|
||||||
let student_removed = sqlx::query!("DELETE FROM user_role WHERE role_id = ?", student_role.id)
|
|
||||||
.execute(&mut *tx)
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.to_string())?
|
|
||||||
.rows_affected();
|
|
||||||
|
|
||||||
let no_einschreibgebuehr_removed = sqlx::query!(
|
|
||||||
"DELETE FROM user_role WHERE role_id = ?",
|
|
||||||
no_einschreibgebuehr_role.id
|
|
||||||
)
|
|
||||||
.execute(&mut *tx)
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.to_string())?
|
|
||||||
.rows_affected();
|
|
||||||
|
|
||||||
let half_rennrudern_removed = sqlx::query!(
|
|
||||||
"DELETE FROM user_role WHERE role_id = ?",
|
|
||||||
half_rennrudern_role.id
|
|
||||||
)
|
|
||||||
.execute(&mut *tx)
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.to_string())?
|
|
||||||
.rows_affected();
|
|
||||||
|
|
||||||
let participated_schnupperkurs_removed = sqlx::query!(
|
|
||||||
"DELETE FROM user_role WHERE role_id = ?",
|
|
||||||
participated_schnupperkurs_role.id
|
|
||||||
)
|
|
||||||
.execute(&mut *tx)
|
|
||||||
.await
|
|
||||||
.map_err(|e| e.to_string())?
|
|
||||||
.rows_affected();
|
|
||||||
|
|
||||||
// Send notifications to admins and Vorstand
|
|
||||||
let admin_role = Role::find_by_name_tx(&mut tx, "admin")
|
|
||||||
.await
|
|
||||||
.ok_or("Role 'admin' not found")?;
|
|
||||||
let vorstand_role = Role::find_by_name_tx(&mut tx, "Vorstand")
|
|
||||||
.await
|
|
||||||
.ok_or("Role 'Vorstand' not found")?;
|
|
||||||
|
|
||||||
let notification_message_admin = format!(
|
|
||||||
"Jährliche Rollenbereinigung abgeschlossen. Die folgenden Rollen wurden entfernt: \
|
|
||||||
paid ({} Benutzer, außer Scheckbuch-Mitglieder), \
|
|
||||||
Schüler/Student ({}/{} Benutzer), \
|
|
||||||
no-einschreibgebuehr ({} Benutzer), \
|
|
||||||
half-rennrudern ({} Benutzer), \
|
|
||||||
participated_schnupperkurs ({} Benutzer). \
|
|
||||||
Die aktualisierten Gebühren können unter https://app.rudernlinz.at/admin/user/fees eingesehen werden.",
|
|
||||||
paid_removed,
|
|
||||||
schueler_removed,
|
|
||||||
student_removed,
|
|
||||||
no_einschreibgebuehr_removed,
|
|
||||||
half_rennrudern_removed,
|
|
||||||
participated_schnupperkurs_removed
|
|
||||||
);
|
|
||||||
let notification_message_vorstand = format!(
|
|
||||||
"Jährliche Rollenbereinigung abgeschlossen. \
|
|
||||||
Die aktualisierten Gebühren können unter https://app.rudernlinz.at/admin/user/fees eingesehen werden.",
|
|
||||||
);
|
|
||||||
|
|
||||||
// Notify admins
|
|
||||||
Notification::create_for_role_tx(
|
|
||||||
&mut tx,
|
|
||||||
&admin_role,
|
|
||||||
¬ification_message_admin,
|
|
||||||
"Systembenachrichtigung",
|
|
||||||
Some("https://app.rudernlinz.at/admin/user/fees"),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
// Notify Vorstand
|
|
||||||
Notification::create_for_role_tx(
|
|
||||||
&mut tx,
|
|
||||||
&vorstand_role,
|
|
||||||
¬ification_message_vorstand,
|
|
||||||
"Systembenachrichtigung",
|
|
||||||
Some("https://app.rudernlinz.at/admin/user/fees"),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
// Commit transaction
|
|
||||||
tx.commit().await.map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
log::info!(
|
|
||||||
"Yearly role cleanup completed successfully: \
|
|
||||||
paid={}, Schüler={}, Student={}, no-einschreibgebuehr={}, \
|
|
||||||
half-rennrudern={}, participated_schnupperkurs={} removals",
|
|
||||||
paid_removed,
|
|
||||||
schueler_removed,
|
|
||||||
student_removed,
|
|
||||||
no_einschreibgebuehr_removed,
|
|
||||||
half_rennrudern_removed,
|
|
||||||
participated_schnupperkurs_removed
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user