add database stuff

This commit is contained in:
OMGeeky
2024-10-12 01:32:23 +02:00
parent 0516ffa9bb
commit 080c1a006c
5 changed files with 170 additions and 99 deletions

View File

@@ -11,4 +11,4 @@ twba-common.workspace = true
lazy_static = "1.5.0" lazy_static = "1.5.0"
derive_more = { version = "1.0.0", features = ["full"] } derive_more = { version = "1.0.0", features = ["full"] }
serde = { version = "1.0.203", features = ["derive"] } serde = { version = "1.0.203", features = ["derive"] }
chrono = "0.4"

View File

@@ -1,9 +1,10 @@
use crate::services::init_services;
use derive_more::{FromStr, FromStrError}; use derive_more::{FromStr, FromStrError};
use rocket::State;
#[macro_use] #[macro_use]
extern crate rocket; extern crate rocket;
use rocket::request::FromParam; use rocket::request::FromParam;
use rocket::response::Responder;
use rocket::tokio::time::{sleep, Duration}; use rocket::tokio::time::{sleep, Duration};
use rocket_dyn_templates::Template; use rocket_dyn_templates::Template;
use std::sync::OnceLock; 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::twba_local_db::re_exports::sea_orm::DatabaseConnection;
use twba_common::prelude::Conf; use twba_common::prelude::Conf;
static CLIENT: OnceLock<DatabaseConnection> = OnceLock::new();
static CONF: OnceLock<Conf> = OnceLock::new(); static CONF: OnceLock<Conf> = OnceLock::new();
mod services; mod services;
@@ -28,20 +28,15 @@ fn index() -> &'static str {
trace!("Called Index"); trace!("Called Index");
"Hello, world!" "Hello, world!"
} }
#[post("/migrate")]
async fn migrate(db: &State<DatabaseConnection>) -> Result<(), ResponderError> {
twba_local_db::migrate_db(db.inner()).await?;
Ok(())
}
fn get_config<'a>() -> &'a Conf { fn get_config<'a>() -> &'a Conf {
CONF.get_or_init(twba_common::get_config) 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<DatabaseConnection, MainError> { async fn get_new_client<'a>() -> Result<DatabaseConnection, MainError> {
Ok(twba_local_db::open_database(Some(&get_config().db_url)).await?) Ok(twba_local_db::open_database(Some(&get_config().db_url)).await?)
} }
@@ -49,10 +44,10 @@ async fn get_new_client<'a>() -> Result<DatabaseConnection, MainError> {
async fn main() -> Result<(), MainError> { async fn main() -> Result<(), MainError> {
let _guard = init_tracing("twba_uploader"); let _guard = init_tracing("twba_uploader");
info!("Hello world!"); info!("Hello world!");
let services = init_services(); let db = get_new_client().await?;
let _rocket = rocket::build() let _rocket = rocket::build()
.manage(services) .manage(db)
.mount("/", routes![index, delay,]) .mount("/", routes![index, delay, migrate])
.mount( .mount(
"/services/", "/services/",
routes![ routes![
@@ -60,6 +55,8 @@ async fn main() -> Result<(), MainError> {
services::service_info, services::service_info,
services::update_progress, services::update_progress,
services::increment_progress, services::increment_progress,
services::increment_task_progress,
services::add,
], ],
) )
.attach(Template::fairing()) .attach(Template::fairing())
@@ -68,6 +65,38 @@ async fn main() -> Result<(), MainError> {
Ok(()) 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<sea_orm::DbErr> 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<sea_orm::DbErr> 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)] #[derive(Debug, derive_more::Error, derive_more::Display, derive_more::From)]
pub enum MainError { pub enum MainError {
Rocket(#[from] rocket::Error), Rocket(#[from] rocket::Error),
@@ -86,7 +115,6 @@ pub enum MainError {
} }
#[derive(Debug, derive_more::Display, derive_more::From, Clone, Copy)] #[derive(Debug, derive_more::Display, derive_more::From, Clone, Copy)]
pub enum Statics { pub enum Statics {
DbClient,
Config, Config,
} }

View File

@@ -1,103 +1,127 @@
use crate::AvailableServices; use crate::DatabaseConnection;
use rocket::fs::{relative, FileServer}; use crate::{AvailableServices, ResponderError};
use rocket::State; use rocket::State;
use rocket_dyn_templates::{context, Template}; use rocket_dyn_templates::{context, Template};
use std::sync::atomic::AtomicUsize; use twba_common::prelude::twba_local_db::prelude::{Services, Tasks};
use std::sync::atomic::Ordering; use twba_common::prelude::twba_local_db::re_exports::sea_orm::ActiveModelTrait;
use std::sync::Arc; use twba_common::prelude::twba_local_db::re_exports::sea_orm::ActiveValue;
use std::time::SystemTime; use twba_common::prelude::twba_local_db::re_exports::sea_orm::{EntityTrait, IntoActiveModel};
use std::time::UNIX_EPOCH;
async fn get_services(db: &DatabaseConnection) -> Result<Vec<Service>, 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)] #[derive(serde::Serialize)]
pub struct Service { pub struct Service {
id: i32,
name: String, name: String,
id: String,
tasks: Vec<Task>, tasks: Vec<Task>,
last_update: String,
} }
#[derive(serde::Serialize)] #[derive(serde::Serialize)]
pub struct Task { pub struct Task {
name: String, id: i32,
id: String, service_id: i32,
progress: Arc<AtomicUsize>, // Progress in percentage description: String,
progress: i32,
max_progress: i32,
} }
#[get("/<service>/info")] #[get("/<service>/info")]
pub(super) fn service_info(service: AvailableServices) -> String { pub(super) fn service_info(service: AvailableServices) -> String {
format!("Here is some info about the service: name: {service}") format!("Here is some info about the service: name: {service}")
} }
pub(super) fn init_services() -> Vec<Service> {
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("/")] #[get("/")]
pub(super) fn service(services: &State<Vec<Service>>) -> Template { pub(super) async fn service(db: &State<DatabaseConnection>) -> Result<Template, ResponderError> {
let x = services.inner(); let services = get_services(db.inner()).await?;
let last_update = SystemTime::now()
.duration_since(UNIX_EPOCH) Ok(Template::render(
.unwrap()
.as_secs();
Template::render(
"services-overview", "services-overview",
context! { services: x , last_update: last_update }, context! {services:services},
) ))
} }
#[post("/add")]
pub async fn add(db: &State<DatabaseConnection>) -> 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/<task>")]
pub async fn increment_task_progress(
task: i32,
db: &State<DatabaseConnection>,
) -> 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("/<service>/increment-progress/<task>")] #[post("/<service>/increment-progress/<task>")]
pub fn increment_progress( pub async fn increment_progress(
service: String, service: i32,
task: String, task: i32,
services: &State<Vec<Service>>, db_state: &State<DatabaseConnection>,
) -> Result<(), String> { ) -> Result<(), ResponderError> {
if let Some(service) = services.inner().iter().find(|x| x.id == service) { let db = db_state.inner();
if let Some(task) = service.tasks.iter().find(|x| x.id == task) {
task.progress.fetch_add(1, Ordering::AcqRel); let service =
Ok(()) Services::find_by_id(service)
} else { .one(db)
Err("task with index not found".to_string()) .await?
} .ok_or(ResponderError::DbEntityNotFound {
} else { table: "Services",
Err("service with index not found".to_string()) 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")] #[get("/update_progress")]
pub fn update_progress(services: &State<Vec<Service>>) -> Template { pub async fn update_progress(db: &State<DatabaseConnection>) -> Result<Template, ResponderError> {
Template::render("services", context! { services: services.inner() }) let services = get_services(db.inner()).await?;
Ok(Template::render("services", context! {services:services}))
} }

View File

@@ -9,14 +9,32 @@
<body> <body>
<div class="container"> <div class="container">
<h1>Services and Tasks</h1> <h1>Services and Tasks</h1>
<h2>thsi is a long text to check if the size of this has any influence on the size of the rest</h2>
<div class="row" id="services-container"> <div class="row" id="services-container">
</div> </div>
<div id="last-update-element" data-timestamp="0">hi</div> <button class="btn btn-primary" id="add-service">Add Service</button>
<button id="update-button">Update Progress</button> <div style="flex-direction: row; display: flex" class="container">
<button class="btn btn-primary" id="update-button">Update Progress</button>
<div id="last-update-element" data-timestamp="0">hi</div>
</div>
</div> </div>
<script> <script>
function add_service() {
$.ajax({
type: "POST",
url: "/services/add/",
success: function (data) {
console.log('success');
updateProgress();
},
error: function (error) {
console.error("Error updating progress:", error);
}
});
}
function update_task_progress(service_id, task_id) { function update_task_progress(service_id, task_id) {
console.log('updating task: ' + service_id + ' ' + task_id); console.log('updating task: ' + service_id + ' ' + task_id);
// $.post('/update_progress', { // $.post('/update_progress', {
@@ -60,6 +78,7 @@
$("#update-button").click(updateProgress); $("#update-button").click(updateProgress);
$("#add-service").click(add_service);
updateProgress(); updateProgress();

View File

@@ -6,7 +6,7 @@
<li class="list-group-item"> <li class="list-group-item">
{{ task.name }} {{ task.name }}
<div class="progress"> <div class="progress">
<div class="progress-bar" role="progressbar" style="width:{{ task.progress }}%;" aria-valuenow="{{ task.progress }}" aria-valuemin="0" aria-valuemax="100">{{ task.progress }}%</div> <div class="progress-bar" role="progressbar" style="width:{{ task.max_progress / task.progress * 100 }}%;" aria-valuenow="{{ task.progress }}" aria-valuemin="0" aria-valuemax="{{ task.max_progress }}">{{ task.progress }}%</div>
</div> </div>
<button id="increment-button" onclick="update_task_progress( '{{service.id}}','{{task.id}}' )">+</button> <button id="increment-button" onclick="update_task_progress( '{{service.id}}','{{task.id}}' )">+</button>
</li> </li>