use crate::db::UDB; use diesel::SaveChangesDsl; use hyper::Body; use serde::Serialize; use u_lib::{ messaging::{AsMsg, BaseMessage, Reportable}, models::*, utils::{OneOrVec, Stripped}, ULocalError, }; use uuid::Uuid; use warp::{ http::{Response, StatusCode}, Rejection, Reply, }; pub fn build_response(code: StatusCode, body: impl Into<Body>) -> Response<Body> { Response::builder().status(code).body(body.into()).unwrap() } pub fn build_ok(body: impl Into<Body>) -> Response<Body> { build_response(StatusCode::OK, body) } pub fn build_err(body: impl ToString) -> Response<Body> { build_response(StatusCode::BAD_REQUEST, body.to_string()) } pub fn build_message<M: AsMsg + Serialize>(m: M) -> Response<Body> { warp::reply::json(&m.as_message()).into_response() } pub struct Endpoints; #[cfg_attr(test, automock)] impl Endpoints { pub async fn add_agent(msg: Agent) -> Result<Response<Body>, Rejection> { info!("hnd: add_agent"); UDB::lock_db() .insert_agent(&msg) .map(|_| build_ok("")) .or_else(|e| Ok(build_err(e))) } pub async fn get_agents(uid: Option<Uuid>) -> Result<Response<Body>, Rejection> { info!("hnd: get_agents"); UDB::lock_db() .get_agents(uid) .map(build_message) .or_else(|e| Ok(build_err(e))) } pub async fn get_jobs(uid: Option<Uuid>) -> Result<Response<Body>, Rejection> { info!("hnd: get_jobs"); UDB::lock_db() .get_jobs(uid) .map(build_message) .or_else(|e| Ok(build_err(e))) } pub async fn get_agent_jobs(uid: Option<Uuid>) -> Result<Response<Body>, Rejection> { info!("hnd: get_agent_jobs"); UDB::lock_db() .get_exact_jobs(uid, false) .map(build_message) .or_else(|e| Ok(build_err(e))) } pub async fn get_personal_jobs(uid: Option<Uuid>) -> Result<Response<Body>, Rejection> { info!("hnd: get_personal_jobs"); let agents = UDB::lock_db().get_agents(uid).unwrap(); if agents.is_empty() { let db = UDB::lock_db(); db.insert_agent(&Agent::with_id(uid.unwrap())).unwrap(); let job = db.find_job_by_alias("agent_hello").unwrap(); if let Err(e) = db.set_jobs_for_agent(&uid.unwrap(), &[job.id]) { return Ok(build_err(e)); } } let result = UDB::lock_db().get_exact_jobs(uid, true); match result { Ok(r) => { let db = UDB::lock_db(); for j in r.iter() { db.update_job_status(j.id, JobState::Running).unwrap(); } Ok(build_message(r)) } Err(e) => Ok(build_err(e)), } } pub async fn upload_jobs( msg: BaseMessage<'static, Vec<JobMeta>>, ) -> Result<Response<Body>, Rejection> { info!("hnd: upload_jobs"); UDB::lock_db() .insert_jobs(&msg.into_inner()) .map(|_| build_ok("")) .or_else(|e| Ok(build_err(e))) } pub async fn del(uid: Uuid) -> Result<Response<Body>, Rejection> { info!("hnd: del"); let db = UDB::lock_db(); let del_fns = &[UDB::del_agents, UDB::del_jobs, UDB::del_results]; for del_fn in del_fns { let affected = del_fn(&db, &[uid]).unwrap(); if affected > 0 { return Ok(build_message(affected as i32)); } } Ok(build_message(0)) } pub async fn set_jobs( agent_uid: Uuid, msg: BaseMessage<'static, Vec<String>>, ) -> Result<Response<Body>, Rejection> { info!("hnd: set_jobs_by_alias, agent: {}", agent_uid); let jobs: Result<Vec<Uuid>, ULocalError> = msg .into_inner() .into_iter() .map(|ident| { info!("hnd: set_jobs_by_alias, job: {}", ident); Uuid::parse_str(&ident) .or_else(|_| UDB::lock_db().find_job_by_alias(&ident).map(|j| j.id)) }) .collect(); match jobs { Ok(j) => UDB::lock_db() .set_jobs_for_agent(&agent_uid, &j) .map(build_message) .or_else(|e| Ok(build_err(e))), Err(e) => Ok(build_err(e)), } } pub async fn report<Data: OneOrVec<Reportable> + AsMsg + 'static>( msg: BaseMessage<'static, Data>, ) -> Result<Response<Body>, Rejection> { info!("hnd: report"); let id = msg.id; let mut failed = vec![]; for entry in msg.into_inner().into_vec() { match entry { Reportable::Assigned(res) => { if id != res.agent_id { continue; } let db = UDB::lock_db(); if let Err(e) = res .save_changes::<AssignedJob>(&db.conn) .map_err(ULocalError::from) { failed.push(e.to_string()) } } Reportable::Agent(mut a) => { a.state = AgentState::Active; Self::add_agent(a).await?; } Reportable::Error(e) => { let err = AgentError::from_msg(e, id); warn!( "{} reported an error: {}", err.agent_id, Stripped(&err.msg.as_str()) ); UDB::lock_db().report_error(&err).unwrap(); } Reportable::Dummy => (), } } if !failed.is_empty() { let err_msg = ULocalError::ProcessingError(failed.join(", ")); return Ok(build_err(err_msg)); } Ok(build_ok("")) } }