From 7074a8d8688779977237ad56f798efae71e6c96d Mon Sep 17 00:00:00 2001 From: perf3ct Date: Thu, 3 Jul 2025 14:03:26 +0000 Subject: [PATCH] feat(webdav): add validation statuses to sources --- frontend/src/pages/SourcesPage.tsx | 1 - src/db/sources.rs | 15 +++++++- src/scheduling/source_scheduler.rs | 56 ++++++++++++++++++++++++------ 3 files changed, 59 insertions(+), 13 deletions(-) diff --git a/frontend/src/pages/SourcesPage.tsx b/frontend/src/pages/SourcesPage.tsx index 4da76a5..2983d04 100644 --- a/frontend/src/pages/SourcesPage.tsx +++ b/frontend/src/pages/SourcesPage.tsx @@ -68,7 +68,6 @@ import { TextSnippet as DocumentIcon, Visibility as OcrIcon, Block as BlockIcon, - FindInPage as DeepScanIcon, HealthAndSafety as HealthIcon, Warning as WarningIcon, Error as CriticalIcon, diff --git a/src/db/sources.rs b/src/db/sources.rs index 6d46f8e..ee349fc 100644 --- a/src/db/sources.rs +++ b/src/db/sources.rs @@ -76,6 +76,10 @@ impl Database { total_size_bytes: row.get("total_size_bytes"), created_at: row.get("created_at"), updated_at: row.get("updated_at"), + validation_status: row.get("validation_status"), + last_validation_at: row.get("last_validation_at"), + validation_score: row.get("validation_score"), + validation_issues: row.get("validation_issues"), })), None => Ok(None), } @@ -347,6 +351,10 @@ impl Database { total_size_bytes: row.get("total_size_bytes"), created_at: row.get("created_at"), updated_at: row.get("updated_at"), + validation_status: row.get("validation_status"), + last_validation_at: row.get("last_validation_at"), + validation_score: row.get("validation_score"), + validation_issues: row.get("validation_issues"), }; sources.push(source); @@ -359,7 +367,8 @@ impl Database { let row = sqlx::query( r#"SELECT id, user_id, name, source_type, enabled, config, status, last_sync_at, last_error, last_error_at, total_files_synced, - total_files_pending, total_size_bytes, created_at, updated_at + total_files_pending, total_size_bytes, created_at, updated_at, + validation_status, last_validation_at, validation_score, validation_issues FROM sources WHERE id = $1"# ) .bind(source_id) @@ -385,6 +394,10 @@ impl Database { total_size_bytes: row.get("total_size_bytes"), created_at: row.get("created_at"), updated_at: row.get("updated_at"), + validation_status: row.get("validation_status"), + last_validation_at: row.get("last_validation_at"), + validation_score: row.get("validation_score"), + validation_issues: row.get("validation_issues"), })) } else { Ok(None) diff --git a/src/scheduling/source_scheduler.rs b/src/scheduling/source_scheduler.rs index 04c4e70..a3b064e 100644 --- a/src/scheduling/source_scheduler.rs +++ b/src/scheduling/source_scheduler.rs @@ -61,6 +61,11 @@ impl SourceScheduler { if let Err(e) = self.check_and_sync_sources().await { error!("Error in source sync scheduler: {}", e); } + + // Run periodic validation checks for all sources + if let Err(e) = self.run_periodic_validations().await { + error!("Error in periodic validation checks: {}", e); + } } } @@ -946,27 +951,27 @@ impl SourceScheduler { .map_err(|e| format!("Config parse error: {}", e))?; let webdav_config = crate::services::webdav_service::WebDAVConfig { - server_url: config.server_url, - username: config.username, - password: config.password, - watch_folders: config.watch_folders, - file_extensions: config.file_extensions, + server_url: config.server_url.clone(), + username: config.username.clone(), + password: config.password.clone(), + watch_folders: config.watch_folders.clone(), + file_extensions: config.file_extensions.clone(), timeout_seconds: 30, // Quick connectivity test - server_type: config.server_type, + server_type: config.server_type.clone(), }; let webdav_service = crate::services::webdav_service::WebDAVService::new(webdav_config) .map_err(|e| format!("Service creation failed: {}", e))?; let test_config = crate::models::WebDAVTestConnection { - server_url: config.server_url.clone(), - username: config.username.clone(), - password: config.password.clone(), - server_type: config.server_type.clone(), + server_url: config.server_url, + username: config.username, + password: config.password, + server_type: config.server_type, }; webdav_service.test_connection(test_config).await - .map_err(|e| format!("Connection test failed: {}", e.message))?; + .map_err(|e| format!("Connection test failed: {}", e))?; Ok(()) } @@ -1073,4 +1078,33 @@ impl SourceScheduler { Ok(ErrorAnalysis { score_penalty, issues }) } + + async fn run_periodic_validations(&self) -> Result<(), Box> { + // Get all enabled sources + let sources = self.state.db.get_sources_for_sync().await?; + + for source in sources { + // Only validate if it's been more than 30 minutes since last validation + let should_validate = if let Some(last_validation) = source.last_validation_at { + chrono::Utc::now().signed_duration_since(last_validation).num_minutes() > 30 + } else { + true // Never validated before + }; + + if should_validate && source.enabled { + info!("Running periodic validation for source: {}", source.name); + + // Run validation in background to avoid blocking + let source_clone = source.clone(); + let state_clone = self.state.clone(); + tokio::spawn(async move { + if let Err(e) = Self::validate_source_health(&source_clone, &state_clone).await { + error!("Periodic validation failed for source {}: {}", source_clone.name, e); + } + }); + } + } + + Ok(()) + } } \ No newline at end of file