mirror of
https://github.com/OMGeeky/twba.control-center.git
synced 2025-12-26 17:02:38 +01:00
add database stuff
This commit is contained in:
@@ -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"
|
||||
|
||||
62
src/main.rs
62
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<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,
|
||||
}
|
||||
|
||||
|
||||
180
src/services.rs
180
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<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("/<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);
|
||||
#[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 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(())
|
||||
} else {
|
||||
Err("task with index not found".to_string())
|
||||
}
|
||||
} else {
|
||||
Err("service with index not found".to_string())
|
||||
}
|
||||
}
|
||||
#[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}))
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
<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>
|
||||
<button id="update-button">Update Progress</button>
|
||||
</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();
|
||||
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user