From 0e51b7c0f1250213ffec7d7d84e04c01ede4495e Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Mon, 16 Mar 2026 03:26:02 +0100 Subject: [PATCH] feat: implement SqliteProviderConfigRepository, build_provider_config_repository factory --- k-tv-backend/infra/src/factory.rs | 17 ++++- k-tv-backend/infra/src/lib.rs | 3 + .../src/provider_config_repository/mod.rs | 4 ++ .../src/provider_config_repository/sqlite.rs | 63 +++++++++++++++++++ 4 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 k-tv-backend/infra/src/provider_config_repository/mod.rs create mode 100644 k-tv-backend/infra/src/provider_config_repository/sqlite.rs diff --git a/k-tv-backend/infra/src/factory.rs b/k-tv-backend/infra/src/factory.rs index 51cddfe..3830469 100644 --- a/k-tv-backend/infra/src/factory.rs +++ b/k-tv-backend/infra/src/factory.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use crate::db::DatabasePool; -use domain::{ActivityLogRepository, ChannelRepository, ScheduleRepository, UserRepository}; +use domain::{ActivityLogRepository, ChannelRepository, ProviderConfigRepository, ScheduleRepository, UserRepository}; #[derive(Debug, thiserror::Error)] pub enum FactoryError { @@ -70,6 +70,21 @@ pub async fn build_activity_log_repository( } } +pub async fn build_provider_config_repository( + pool: &DatabasePool, +) -> FactoryResult> { + match pool { + #[cfg(feature = "sqlite")] + DatabasePool::Sqlite(pool) => Ok(Arc::new( + crate::provider_config_repository::SqliteProviderConfigRepository::new(pool.clone()), + )), + #[allow(unreachable_patterns)] + _ => Err(FactoryError::NotImplemented( + "ProviderConfigRepository not implemented for this database".to_string(), + )), + } +} + pub async fn build_schedule_repository( pool: &DatabasePool, ) -> FactoryResult> { diff --git a/k-tv-backend/infra/src/lib.rs b/k-tv-backend/infra/src/lib.rs index 6ef5099..7562b6e 100644 --- a/k-tv-backend/infra/src/lib.rs +++ b/k-tv-backend/infra/src/lib.rs @@ -20,6 +20,7 @@ pub mod jellyfin; pub mod provider_registry; mod activity_log_repository; mod channel_repository; +mod provider_config_repository; mod schedule_repository; mod user_repository; @@ -37,6 +38,8 @@ pub use user_repository::SqliteUserRepository; #[cfg(feature = "sqlite")] pub use channel_repository::SqliteChannelRepository; #[cfg(feature = "sqlite")] +pub use provider_config_repository::SqliteProviderConfigRepository; +#[cfg(feature = "sqlite")] pub use schedule_repository::SqliteScheduleRepository; #[cfg(feature = "jellyfin")] diff --git a/k-tv-backend/infra/src/provider_config_repository/mod.rs b/k-tv-backend/infra/src/provider_config_repository/mod.rs new file mode 100644 index 0000000..e3b12ae --- /dev/null +++ b/k-tv-backend/infra/src/provider_config_repository/mod.rs @@ -0,0 +1,4 @@ +#[cfg(feature = "sqlite")] +mod sqlite; +#[cfg(feature = "sqlite")] +pub use sqlite::SqliteProviderConfigRepository; diff --git a/k-tv-backend/infra/src/provider_config_repository/sqlite.rs b/k-tv-backend/infra/src/provider_config_repository/sqlite.rs new file mode 100644 index 0000000..0294e56 --- /dev/null +++ b/k-tv-backend/infra/src/provider_config_repository/sqlite.rs @@ -0,0 +1,63 @@ +use async_trait::async_trait; +use domain::{DomainError, DomainResult, ProviderConfigRepository, ProviderConfigRow}; + +#[derive(Clone)] +pub struct SqliteProviderConfigRepository { + pool: sqlx::SqlitePool, +} + +impl SqliteProviderConfigRepository { + pub fn new(pool: sqlx::SqlitePool) -> Self { + Self { pool } + } +} + +#[async_trait] +impl ProviderConfigRepository for SqliteProviderConfigRepository { + async fn get_all(&self) -> DomainResult> { + let rows: Vec<(String, String, i64, String)> = sqlx::query_as( + "SELECT provider_type, config_json, enabled, updated_at FROM provider_configs", + ) + .fetch_all(&self.pool) + .await + .map_err(|e| DomainError::RepositoryError(e.to_string()))?; + + Ok(rows + .into_iter() + .map(|(provider_type, config_json, enabled, updated_at)| ProviderConfigRow { + provider_type, + config_json, + enabled: enabled != 0, + updated_at, + }) + .collect()) + } + + async fn upsert(&self, row: &ProviderConfigRow) -> DomainResult<()> { + sqlx::query( + r#"INSERT INTO provider_configs (provider_type, config_json, enabled, updated_at) + VALUES (?, ?, ?, ?) + ON CONFLICT(provider_type) DO UPDATE SET + config_json = excluded.config_json, + enabled = excluded.enabled, + updated_at = excluded.updated_at"#, + ) + .bind(&row.provider_type) + .bind(&row.config_json) + .bind(row.enabled as i64) + .bind(&row.updated_at) + .execute(&self.pool) + .await + .map_err(|e| DomainError::RepositoryError(e.to_string()))?; + Ok(()) + } + + async fn delete(&self, provider_type: &str) -> DomainResult<()> { + sqlx::query("DELETE FROM provider_configs WHERE provider_type = ?") + .bind(provider_type) + .execute(&self.pool) + .await + .map_err(|e| DomainError::RepositoryError(e.to_string()))?; + Ok(()) + } +}