mirror of
https://github.com/OMGeeky/twba.control-center.git
synced 2026-02-23 15:49:56 +01:00
add database stuff
This commit is contained in:
@@ -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"
|
||||||
|
|||||||
62
src/main.rs
62
src/main.rs
@@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
180
src/services.rs
180
src/services.rs
@@ -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}))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user