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"
derive_more = { version = "1.0.0", features = ["full"] }
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 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<DatabaseConnection> = OnceLock::new();
static CONF: OnceLock<Conf> = OnceLock::new();
mod services;
@@ -28,20 +28,15 @@ fn index() -> &'static str {
trace!("Called Index");
"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 {
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> {
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> {
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<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)]
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,
}

View File

@@ -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<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)]
pub struct Service {
id: i32,
name: String,
id: String,
tasks: Vec<Task>,
last_update: String,
}
#[derive(serde::Serialize)]
pub struct Task {
name: String,
id: String,
progress: Arc<AtomicUsize>, // Progress in percentage
id: i32,
service_id: i32,
description: String,
progress: i32,
max_progress: i32,
}
#[get("/<service>/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<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("/")]
pub(super) fn service(services: &State<Vec<Service>>) -> 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<DatabaseConnection>) -> Result<Template, ResponderError> {
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<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>")]
pub fn increment_progress(
service: String,
task: String,
services: &State<Vec<Service>>,
) -> 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<DatabaseConnection>,
) -> 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<Vec<Service>>) -> Template {
Template::render("services", context! { services: services.inner() })
pub async fn update_progress(db: &State<DatabaseConnection>) -> Result<Template, ResponderError> {
let services = get_services(db.inner()).await?;
Ok(Template::render("services", context! {services:services}))
}

View File

@@ -9,14 +9,32 @@
<body>
<div class="container">
<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>
<div id="last-update-element" data-timestamp="0">hi</div>
<button id="update-button">Update Progress</button>
<button class="btn btn-primary" id="add-service">Add Service</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>
<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) {
console.log('updating task: ' + service_id + ' ' + task_id);
// $.post('/update_progress', {
@@ -60,6 +78,7 @@
$("#update-button").click(updateProgress);
$("#add-service").click(add_service);
updateProgress();

View File

@@ -6,7 +6,7 @@
<li class="list-group-item">
{{ task.name }}
<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>
<button id="increment-button" onclick="update_task_progress( '{{service.id}}','{{task.id}}' )">+</button>
</li>