clean(presentation): strip comments, kill dead code, extract constants
- remove all doc/inline comments - delete unused ApiError variants (AuthRequired, NotImplemented) and helpers (internal, not_implemented) - prefix lifetime-only AppState fields with _ to suppress dead_code warning - extract magic numbers: TICK_INTERVAL_SECS, EXPIRY_THRESHOLD_HOURS, POLL_INTERVAL_SECS, EVENT_BUS_CAPACITY, DEFAULT_ACTIVITY_LIMIT, DEFAULT_SEARCH_LIMIT, etc - replace inline serde_json::json! in sync_status with typed SyncStatusEntry - fix mid-file import in schedule handler - fix extractors: strip_prefix instead of magic offset 6 - fix unreachable pattern in wire_repositories with cfg guard - use eq_ignore_ascii_case for content-type header check
This commit is contained in:
@@ -1,8 +1,3 @@
|
||||
//! Factory — builds AppState from Config + DbPool.
|
||||
//!
|
||||
//! Connects to the database, runs migrations, creates all adapter instances,
|
||||
//! constructs Deps structs, and returns a fully-wired AppState.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use application::{
|
||||
@@ -21,31 +16,26 @@ use infra_wiring::{Config, ConfigSource, DbPool};
|
||||
|
||||
use crate::state::AppState;
|
||||
|
||||
/// Build a fully-wired AppState ready for the HTTP server.
|
||||
const EVENT_BUS_CAPACITY: usize = 64;
|
||||
const DEV_JWT_SECRET: &str = "k-template-dev-secret-not-for-production-use-only";
|
||||
|
||||
pub async fn build_app_state(config: Config) -> anyhow::Result<AppState> {
|
||||
// Connect to database
|
||||
let pool = DbPool::connect(&config.database_url).await?;
|
||||
pool.run_migrations().await?;
|
||||
|
||||
// Wire up all repositories from the database pool
|
||||
let wire_output = wire_repositories(&pool)?;
|
||||
|
||||
// Auth service
|
||||
let auth_service: Arc<dyn domain::ports::AuthService> =
|
||||
Arc::new(adapter_auth::PasswordAuthService);
|
||||
|
||||
// Event bus
|
||||
let event_bus = Arc::new(adapter_event_publisher::ChannelEventBus::new(64));
|
||||
let event_bus = Arc::new(adapter_event_publisher::ChannelEventBus::new(EVENT_BUS_CAPACITY));
|
||||
let event_publisher: Arc<dyn domain::ports::EventPublisher> = event_bus.clone();
|
||||
|
||||
// Provider registry
|
||||
let provider_registry = build_provider_registry(&config).await;
|
||||
|
||||
// Library sync adapter — uses the LibraryCommand port internally
|
||||
let library_sync: Arc<dyn domain::ports::LibrarySyncAdapter> =
|
||||
build_library_sync(wire_output.library_command.clone());
|
||||
|
||||
// Schedule engine
|
||||
let schedule_engine = Arc::new(ScheduleEngineService::new(
|
||||
provider_registry.clone(),
|
||||
wire_output.channel_query.clone(),
|
||||
@@ -53,14 +43,11 @@ pub async fn build_app_state(config: Config) -> anyhow::Result<AppState> {
|
||||
wire_output.schedule_command.clone(),
|
||||
));
|
||||
|
||||
// JWT validator
|
||||
#[cfg(feature = "auth-jwt")]
|
||||
let jwt_validator = build_jwt_validator(&config)?;
|
||||
|
||||
// Sync trigger channel
|
||||
let (sync_tx, sync_rx) = tokio::sync::watch::channel(());
|
||||
|
||||
// Build all deps structs
|
||||
let auth_deps = Arc::new(AuthDeps {
|
||||
user_command: wire_output.user_command.clone(),
|
||||
user_query: wire_output.user_query.clone(),
|
||||
@@ -120,7 +107,6 @@ pub async fn build_app_state(config: Config) -> anyhow::Result<AppState> {
|
||||
|
||||
let config_arc = Arc::new(config);
|
||||
|
||||
// Spawn background tasks
|
||||
let bg_schedule_deps = schedule_deps.clone();
|
||||
tokio::spawn(crate::background::auto_scheduler::run(bg_schedule_deps));
|
||||
|
||||
@@ -163,19 +149,14 @@ pub async fn build_app_state(config: Config) -> anyhow::Result<AppState> {
|
||||
#[cfg(feature = "auth-jwt")]
|
||||
jwt_validator,
|
||||
provider_registry,
|
||||
library_sync,
|
||||
settings_repo: wire_output.settings,
|
||||
event_bus,
|
||||
_library_sync: library_sync,
|
||||
_settings_repo: wire_output.settings,
|
||||
_event_bus: event_bus,
|
||||
config: config_arc,
|
||||
sync_trigger: sync_tx,
|
||||
})
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Repository wiring output — trait objects ready for dependency injection.
|
||||
struct WireOutput {
|
||||
user_command: Arc<dyn domain::ports::UserCommand>,
|
||||
user_query: Arc<dyn domain::ports::UserQuery>,
|
||||
@@ -229,12 +210,12 @@ fn wire_repositories(pool: &DbPool) -> anyhow::Result<WireOutput> {
|
||||
provider_config_query: w.provider_config_query,
|
||||
})
|
||||
}
|
||||
#[cfg(not(any(feature = "sqlite", feature = "postgres")))]
|
||||
_ => anyhow::bail!("database backend not compiled into this binary"),
|
||||
}
|
||||
}
|
||||
|
||||
async fn build_provider_registry(config: &Config) -> Arc<dyn IProviderRegistry> {
|
||||
// Build a concrete registry that routes to configured providers.
|
||||
let mut providers: Vec<(String, Arc<dyn IMediaProvider>)> = Vec::new();
|
||||
|
||||
match config.config_source {
|
||||
@@ -259,8 +240,6 @@ async fn build_provider_registry(config: &Config) -> Arc<dyn IProviderRegistry>
|
||||
}
|
||||
}
|
||||
ConfigSource::Db => {
|
||||
// DB-based provider configs loaded elsewhere at runtime.
|
||||
// For now, fall through to noop if nothing configured via env.
|
||||
tracing::info!("CONFIG_SOURCE=db: provider configs loaded from database at runtime");
|
||||
}
|
||||
}
|
||||
@@ -288,7 +267,7 @@ fn build_jwt_validator(config: &Config) -> anyhow::Result<Option<Arc<adapter_aut
|
||||
anyhow::bail!("JWT_SECRET is required in production");
|
||||
}
|
||||
tracing::warn!("JWT_SECRET not set — using insecure development secret");
|
||||
"k-template-dev-secret-not-for-production-use-only".to_string()
|
||||
DEV_JWT_SECRET.to_string()
|
||||
}
|
||||
};
|
||||
|
||||
@@ -305,10 +284,6 @@ fn build_jwt_validator(config: &Config) -> anyhow::Result<Option<Arc<adapter_aut
|
||||
Ok(Some(Arc::new(adapter_auth::JwtValidator::new(jwt_config))))
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// NoopMediaProvider — fallback when nothing is configured
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
struct NoopMediaProvider;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
@@ -356,10 +331,6 @@ impl IMediaProvider for NoopMediaProvider {
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// SimpleProviderRegistry — implements IProviderRegistry for N providers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
struct SimpleProviderRegistry {
|
||||
providers: Vec<(String, Arc<dyn IMediaProvider>)>,
|
||||
}
|
||||
@@ -377,7 +348,6 @@ impl SimpleProviderRegistry {
|
||||
self.providers.first().map(|(_, v)| v)
|
||||
}
|
||||
|
||||
/// Extract provider_id from a prefixed item ID (e.g. "jellyfin::abc123" → "jellyfin").
|
||||
fn extract_provider_id(item_id: &str) -> Option<&str> {
|
||||
item_id.find("::").map(|pos| &item_id[..pos])
|
||||
}
|
||||
@@ -411,7 +381,6 @@ impl IProviderRegistry for SimpleProviderRegistry {
|
||||
return provider.fetch_by_id(item_id).await;
|
||||
}
|
||||
}
|
||||
// Fall back to primary
|
||||
if let Some(provider) = self.primary() {
|
||||
provider.fetch_by_id(item_id).await
|
||||
} else {
|
||||
@@ -502,11 +471,6 @@ impl IProviderRegistry for SimpleProviderRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// SimpleSyncAdapter — wraps LibraryCommand for sync operations
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Convert a MediaItem from a provider into a LibraryItem for persistence.
|
||||
fn media_item_to_library_item(item: domain::MediaItem, provider_id: &str) -> domain::LibraryItem {
|
||||
let external_id = item.id().value().to_string();
|
||||
let id = format!("{}::{}", provider_id, external_id);
|
||||
@@ -526,8 +490,8 @@ fn media_item_to_library_item(item: domain::MediaItem, provider_id: &str) -> dom
|
||||
item.genres().to_vec(),
|
||||
item.tags().to_vec(),
|
||||
item.collection_id().map(|s| s.to_string()),
|
||||
None, // collection_name not in MediaItem
|
||||
None, // collection_type not in MediaItem
|
||||
None,
|
||||
None,
|
||||
item.thumbnail_url().map(|s| s.to_string()),
|
||||
now,
|
||||
)
|
||||
@@ -564,7 +528,6 @@ impl domain::ports::LibrarySyncAdapter for SimpleSyncAdapter {
|
||||
}
|
||||
};
|
||||
|
||||
// Fetch all items from provider
|
||||
let filter = domain::MediaFilter::default();
|
||||
let items = match provider.fetch_items(&filter).await {
|
||||
Ok(items) => items,
|
||||
@@ -581,8 +544,6 @@ impl domain::ports::LibrarySyncAdapter for SimpleSyncAdapter {
|
||||
|
||||
let items_found = items.len() as u32;
|
||||
|
||||
// Clear + insert (items are MediaItem; LibrarySyncAdapter implementations
|
||||
// typically handle the conversion. Here we delegate to library_command directly.)
|
||||
if let Err(e) = self.library_command.clear_provider(provider_id).await {
|
||||
let result = domain::LibrarySyncResult::with_error(
|
||||
provider_id,
|
||||
@@ -593,7 +554,6 @@ impl domain::ports::LibrarySyncAdapter for SimpleSyncAdapter {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Convert MediaItems to LibraryItems for storage
|
||||
let library_items: Vec<domain::LibraryItem> = items
|
||||
.into_iter()
|
||||
.map(|item| media_item_to_library_item(item, provider_id))
|
||||
|
||||
Reference in New Issue
Block a user