use crate::config::{Config, SyncTaskConfig};
use crate::error::{MeiliBridgeError, Result};
use tracing::{info, warn};
pub struct StartupChecker {
config: Config,
}
impl StartupChecker {
pub fn new(config: Config) -> Self {
Self { config }
}
pub async fn check(&self) -> Result<()> {
info!("Performing startup checks...");
self.check_cdc_only_tables().await?;
self.check_meilisearch_connectivity().await?;
info!("✅ Startup checks completed successfully");
Ok(())
}
async fn check_cdc_only_tables(&self) -> Result<()> {
let mut cdc_only_tables = Vec::new();
for task in &self.config.sync_tasks {
if task.is_cdc_only() {
cdc_only_tables.push(task);
}
}
if cdc_only_tables.is_empty() {
return Ok(());
}
info!(
"Found {} CDC-only tables (no initial full sync):",
cdc_only_tables.len()
);
for task in &cdc_only_tables {
info!(" - Table '{}' -> Index '{}'", task.table, task.index);
if !self.config.meilisearch.auto_create_index {
warn!(
" ⚠️ auto_create_index is disabled. Index '{}' must exist in Meilisearch \
or the first CDC event will fail.",
task.index
);
} else {
info!(
" ✓ auto_create_index is enabled. Index '{}' will be created on first event.",
task.index
);
}
if !task.has_primary_key_configured(self.config.meilisearch.primary_key.as_ref()) {
warn!(
" ⚠️ No primary key configured for table '{}'. Delete operations will fail.",
task.table
);
}
}
if !self.config.meilisearch.auto_create_index && !cdc_only_tables.is_empty() {
warn!(
"💡 Consider enabling 'meilisearch.auto_create_index' in your configuration \
to automatically create indexes for CDC-only tables."
);
}
Ok(())
}
async fn check_meilisearch_connectivity(&self) -> Result<()> {
info!("Checking Meilisearch connectivity...");
use crate::destination::meilisearch::protected_client::ProtectedMeilisearchClient;
let client = ProtectedMeilisearchClient::new(self.config.meilisearch.clone())?;
match client.test_connection().await {
Ok(_) => {
info!("✓ Successfully connected to Meilisearch");
match client.get_version().await {
Ok(version) => {
info!(" Meilisearch version: {}", version);
}
Err(e) => {
warn!(" Could not retrieve Meilisearch version: {}", e);
}
}
}
Err(e) => {
return Err(MeiliBridgeError::Meilisearch(format!(
"Failed to connect to Meilisearch at {}: {}",
self.config.meilisearch.url, e
)));
}
}
if self.config.meilisearch.api_key.is_some() {
info!(" API key configured - permissions will be validated on first use");
} else {
warn!(" No API key configured - ensure Meilisearch allows anonymous access");
}
Ok(())
}
}
trait SyncTaskExt {
fn is_cdc_only(&self) -> bool;
fn has_primary_key_configured(&self, global_pk: Option<&String>) -> bool;
}
impl SyncTaskExt for SyncTaskConfig {
fn is_cdc_only(&self) -> bool {
!self.full_sync_on_start.unwrap_or(false)
}
fn has_primary_key_configured(&self, global_pk: Option<&String>) -> bool {
!self.primary_key.is_empty() || global_pk.map(|s| !s.is_empty()).unwrap_or(false)
}
}