diff --git a/Cargo.toml b/Cargo.toml index 9dcf6f4..2d1bf12 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,4 +11,4 @@ twba-common.workspace = true lazy_static = "1.5.0" derive_more = { version = "1.0.0", features = ["full"] } serde = { version = "1.0.203", features = ["derive"] } - +chrono = "0.4" diff --git a/src/main.rs b/src/main.rs index b20cd7b..c3dc8f6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,10 @@ -use crate::services::init_services; use derive_more::{FromStr, FromStrError}; +use rocket::State; #[macro_use] extern crate rocket; use rocket::request::FromParam; +use rocket::response::Responder; use rocket::tokio::time::{sleep, Duration}; use rocket_dyn_templates::Template; use std::sync::OnceLock; @@ -13,7 +14,6 @@ use twba_common::prelude::twba_local_db::re_exports::sea_orm; use twba_common::prelude::twba_local_db::re_exports::sea_orm::DatabaseConnection; use twba_common::prelude::Conf; -static CLIENT: OnceLock = OnceLock::new(); static CONF: OnceLock = OnceLock::new(); mod services; @@ -28,20 +28,15 @@ fn index() -> &'static str { trace!("Called Index"); "Hello, world!" } +#[post("/migrate")] +async fn migrate(db: &State) -> Result<(), ResponderError> { + twba_local_db::migrate_db(db.inner()).await?; + Ok(()) +} fn get_config<'a>() -> &'a Conf { CONF.get_or_init(twba_common::get_config) } -async fn get_client<'a>() -> Result<&'a DatabaseConnection, MainError> { - match CLIENT.get() { - Some(client) => Ok(client), - None => { - CLIENT - .set(get_new_client().await?) - .expect("Failed to set client after failing to get client"); - Ok(CLIENT.get().expect("we just initialized the client")) - } - } -} + async fn get_new_client<'a>() -> Result { Ok(twba_local_db::open_database(Some(&get_config().db_url)).await?) } @@ -49,10 +44,10 @@ async fn get_new_client<'a>() -> Result { async fn main() -> Result<(), MainError> { let _guard = init_tracing("twba_uploader"); info!("Hello world!"); - let services = init_services(); + let db = get_new_client().await?; let _rocket = rocket::build() - .manage(services) - .mount("/", routes![index, delay,]) + .manage(db) + .mount("/", routes![index, delay, migrate]) .mount( "/services/", routes![ @@ -60,6 +55,8 @@ async fn main() -> Result<(), MainError> { services::service_info, services::update_progress, services::increment_progress, + services::increment_task_progress, + services::add, ], ) .attach(Template::fairing()) @@ -68,6 +65,38 @@ async fn main() -> Result<(), MainError> { Ok(()) } +#[derive(Debug, derive_more::Error, derive_more::Display, Responder)] +#[display("{e}")] +pub struct DbErr { + _dummy: String, + #[response(ignore)] + e: sea_orm::DbErr, +} +impl From for DbErr { + fn from(e: twba_common::prelude::twba_local_db::re_exports::sea_orm::DbErr) -> Self { + let _dummy = "Some DB Error".to_string(); + Self { _dummy, e } + } +} +impl From for ResponderError { + fn from(e: twba_common::prelude::twba_local_db::re_exports::sea_orm::DbErr) -> Self { + let e: DbErr = e.into(); + e.into() + } +} +#[derive(Debug, derive_more::Error, derive_more::Display, derive_more::From, Responder)] +pub enum ResponderError { + #[response(status = 404)] + Db(#[from] DbErr), + #[display("Could not find entity '{table}' with key:'{key}'")] + #[response(status = 404)] + #[from(ignore)] + DbEntityNotFound { + table: &'static str, + #[response(ignore)] + key: String, + }, +} #[derive(Debug, derive_more::Error, derive_more::Display, derive_more::From)] pub enum MainError { Rocket(#[from] rocket::Error), @@ -86,7 +115,6 @@ pub enum MainError { } #[derive(Debug, derive_more::Display, derive_more::From, Clone, Copy)] pub enum Statics { - DbClient, Config, } diff --git a/src/services.rs b/src/services.rs index 153e6d3..15bedbb 100644 --- a/src/services.rs +++ b/src/services.rs @@ -1,103 +1,127 @@ -use crate::AvailableServices; -use rocket::fs::{relative, FileServer}; +use crate::DatabaseConnection; +use crate::{AvailableServices, ResponderError}; use rocket::State; use rocket_dyn_templates::{context, Template}; -use std::sync::atomic::AtomicUsize; -use std::sync::atomic::Ordering; -use std::sync::Arc; -use std::time::SystemTime; -use std::time::UNIX_EPOCH; +use twba_common::prelude::twba_local_db::prelude::{Services, Tasks}; +use twba_common::prelude::twba_local_db::re_exports::sea_orm::ActiveModelTrait; +use twba_common::prelude::twba_local_db::re_exports::sea_orm::ActiveValue; +use twba_common::prelude::twba_local_db::re_exports::sea_orm::{EntityTrait, IntoActiveModel}; +async fn get_services(db: &DatabaseConnection) -> Result, ResponderError> { + let mut list = vec![]; + + let services = Services::find().all(db).await?; + let tasks = Tasks::find().all(db).await?; + for service in services { + let service_tasks = tasks + .iter() + .filter(|x| x.service_id == service.id) + .map(|x| Task { + description: x.description.clone().unwrap_or_default(), + id: x.id, + max_progress: x.max_progress, + progress: x.progress, + service_id: x.service_id, + }) + .collect(); + list.push(Service { + id: service.id, + name: service.name, + tasks: service_tasks, + last_update: service.last_update.unwrap_or_default(), + }); + } + Ok(list) +} #[derive(serde::Serialize)] pub struct Service { + id: i32, name: String, - id: String, tasks: Vec, + last_update: String, } #[derive(serde::Serialize)] pub struct Task { - name: String, - id: String, - progress: Arc, // Progress in percentage + id: i32, + service_id: i32, + description: String, + progress: i32, + max_progress: i32, } #[get("//info")] pub(super) fn service_info(service: AvailableServices) -> String { format!("Here is some info about the service: name: {service}") } -pub(super) fn init_services() -> Vec { - let task1_progress = Arc::new(AtomicUsize::new(60)); - let task2_progress = Arc::new(AtomicUsize::new(100)); - let task3_progress = Arc::new(AtomicUsize::new(20)); - let task4_progress = Arc::new(AtomicUsize::new(80)); - - vec![ - Service { - name: "Service A".to_string(), - id: "s1".to_string(), - tasks: vec![ - Task { - name: "Task 1".to_string(), - id: "t1".to_string(), - progress: task1_progress, - }, - Task { - name: "Task 2".to_string(), - id: "t2".to_string(), - progress: task2_progress, - }, - ], - }, - Service { - name: "Service B".to_string(), - id: "s2".to_string(), - tasks: vec![ - Task { - name: "Task 3".to_string(), - id: "t3".to_string(), - progress: task3_progress, - }, - Task { - name: "Task 4".to_string(), - id: "t4".to_string(), - progress: task4_progress, - }, - ], - }, - ] -} #[get("/")] -pub(super) fn service(services: &State>) -> Template { - let x = services.inner(); - let last_update = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_secs(); - Template::render( +pub(super) async fn service(db: &State) -> Result { + let services = get_services(db.inner()).await?; + + Ok(Template::render( "services-overview", - context! { services: x , last_update: last_update }, - ) + context! {services:services}, + )) } +#[post("/add")] +pub async fn add(db: &State) -> Result<(), ResponderError> { + let s = twba_common::prelude::twba_local_db::entities::services::ActiveModel { + id: ActiveValue::NotSet, + name: ActiveValue::Set("Test1".to_string()), + last_update: ActiveValue::NotSet, + }; + Services::insert(s).exec(db.inner()).await?; + Ok(()) +} +#[post("/increment-progress/")] +pub async fn increment_task_progress( + task: i32, + db: &State, +) -> Result<(), ResponderError> { + let db = db.inner(); + let task = Tasks::find_by_id(task) + .one(db) + .await? + .ok_or(ResponderError::DbEntityNotFound { + table: "Tasks", + key: format!("{task}"), + })?; + let progress = task.progress; + let mut task = task.into_active_model(); + task.progress = ActiveValue::Set(progress + 1); + task.save(db).await?; + Ok(()) +} #[post("//increment-progress/")] -pub fn increment_progress( - service: String, - task: String, - services: &State>, -) -> Result<(), String> { - if let Some(service) = services.inner().iter().find(|x| x.id == service) { - if let Some(task) = service.tasks.iter().find(|x| x.id == task) { - task.progress.fetch_add(1, Ordering::AcqRel); - Ok(()) - } else { - Err("task with index not found".to_string()) - } - } else { - Err("service with index not found".to_string()) - } +pub async fn increment_progress( + service: i32, + task: i32, + db_state: &State, +) -> Result<(), ResponderError> { + let db = db_state.inner(); + + let service = + Services::find_by_id(service) + .one(db) + .await? + .ok_or(ResponderError::DbEntityNotFound { + table: "Services", + key: format!("{service}"), + })?; + + let datetime = chrono::offset::Utc::now().to_rfc3339(); + + let mut service = service.into_active_model(); + + service.last_update = ActiveValue::Set(Some(datetime)); + increment_task_progress(task, db_state).await?; + + service.save(db).await?; + Ok(()) } #[get("/update_progress")] -pub fn update_progress(services: &State>) -> Template { - Template::render("services", context! { services: services.inner() }) +pub async fn update_progress(db: &State) -> Result { + let services = get_services(db.inner()).await?; + Ok(Template::render("services", context! {services:services})) } diff --git a/templates/services-overview.html.tera b/templates/services-overview.html.tera index 035d003..8c53c64 100644 --- a/templates/services-overview.html.tera +++ b/templates/services-overview.html.tera @@ -9,14 +9,32 @@

Services and Tasks

+

thsi is a long text to check if the size of this has any influence on the size of the rest

-
hi
- + +
+ +
hi
+