diff --git a/src/api/calls.rs b/src/api/calls.rs index 016442a..4a45675 100644 --- a/src/api/calls.rs +++ b/src/api/calls.rs @@ -19,6 +19,17 @@ async fn backup_create( } } +#[get("/backup/list")] +async fn backup_list(data: web::Data) -> impl Responder { + match handlers::backup_list(&data.pool).await { + Ok(resp) => HttpResponse::Ok().json(&resp), + Err(e) => { + error!("While handling /backup/create [POST] request: {e}"); + HttpResponse::InternalServerError().finish() + } + } +} + #[post("/backup/preset")] async fn backup_preset_post( data: web::Data, diff --git a/src/api/data.rs b/src/api/data.rs index cb87cb9..ad8e039 100644 --- a/src/api/data.rs +++ b/src/api/data.rs @@ -11,6 +11,14 @@ pub struct BackupConfig { pub docker: Option, } +#[derive(Debug, Serialize)] +#[serde(rename_all = "PascalCase")] +pub struct Backup { + pub id: u64, + pub time: u64, + pub config: BackupConfig, +} + #[derive(Debug, Deserialize)] pub enum BackupCreateRequest { Preset(String), @@ -22,6 +30,12 @@ pub enum BackupCreateResponse { Success, } +#[derive(Debug, Serialize)] +#[serde(rename_all = "PascalCase")] +pub struct BackupListResponse { + pub backups: Vec, +} + #[derive(Debug, Deserialize, Serialize)] #[serde(rename_all = "PascalCase")] pub struct BackupPreset { diff --git a/src/api/handlers.rs b/src/api/handlers.rs index ed77fd5..d05a338 100644 --- a/src/api/handlers.rs +++ b/src/api/handlers.rs @@ -24,6 +24,19 @@ pub async fn backup_create( Ok(BackupCreateResponse::Success) } +pub async fn backup_list(pool: &SqlitePool) -> Result { + Ok(BackupListResponse { + backups: backup::get_all_backups(pool, |b| match b.status() { + backup::Status::Finished => Some(b), + _ => None, + }) + .await? + .iter() + .map(|preset| preset.clone().into()) + .collect(), + }) +} + pub async fn backup_preset_post( pool: &SqlitePool, request: BackupPreset, diff --git a/src/api/mod.rs b/src/api/mod.rs index 7fa3230..3af4654 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -23,6 +23,7 @@ pub async fn start( let _ = HttpServer::new(move || { App::new() .service(calls::backup_create) + .service(calls::backup_list) .service(calls::backup_preset_post) .service(calls::backup_preset_get) .service(calls::backup_preset_id_get) diff --git a/src/backend/backup/mod.rs b/src/backend/backup/mod.rs index 68ace17..e09d718 100644 --- a/src/backend/backup/mod.rs +++ b/src/backend/backup/mod.rs @@ -4,6 +4,7 @@ pub mod worker; use crate::api; use anyhow::{Context, Error, Result}; use chrono::NaiveDateTime; +use log::error; use serde::{Deserialize, Serialize}; use sqlx::SqlitePool; use std::path::PathBuf; @@ -67,6 +68,7 @@ pub enum Status { Failed, } +#[derive(Clone)] pub struct RawBackup { id: i64, time: i64, @@ -99,6 +101,16 @@ impl TryFrom for Backup { } } +impl From for api::data::Backup { + fn from(value: Backup) -> Self { + Self { + id: value.id, + time: value.time.timestamp() as u64, + config: value.config.into(), + } + } +} + impl Backup { pub async fn new(pool: &SqlitePool, tx: mpsc::Sender, config: Config) -> Result { let time = chrono::Utc::now().naive_utc(); @@ -149,6 +161,10 @@ impl Backup { } } + pub fn status(&self) -> Status { + self.status.clone() + } + pub async fn set_status(&self, pool: &SqlitePool, status: Status) -> Result<()> { let status = serde_json::to_string(&status)?; let id = self.id as i64; @@ -164,3 +180,26 @@ impl Backup { Ok(()) } } + +pub async fn get_all_backups(pool: &SqlitePool, filter: F) -> Result> +where + F: Fn(Backup) -> Option, +{ + let query_result = sqlx::query_as!(RawBackup, r#"SELECT * FROM Backups;"#) + .fetch_all(pool) + .await; + + match query_result { + Ok(raw_presets) => Ok(raw_presets + .iter() + .filter_map(|raw| match raw.clone().try_into() { + Ok(b) => filter(b), + Err(e) => { + error!("Failed to parse backup from database row: {e}"); + None + } + }) + .collect::>()), + Err(e) => Err(Error::new(e)), + } +}