fix(presentation): extract 12 handler violations to use cases
- TokenService port + JwtTokenService adapter; login/refresh return LoginResult - delete get_token (dup of login); create_tokens helper removed - type UpdateChannelRequest schedule_config/recycle_policy (no serde_json::Value) - update_settings returns updated Vec; handler calls one use case - config use case in application::config; handler maps DTO only - SyncStatusEntry moved to api-types w/ From<LibrarySyncLogEntry> - trigger_sync: Conflict error, drop sync_trigger.send from handler - UpsertProviderCommand.config accepts Value; serialization in use case - get_current_broadcast returns BroadcastWithChannel; one call - get_stream_url resolves broadcast internally; handler single call
This commit is contained in:
@@ -4,6 +4,7 @@ use application::{
|
||||
admin::AdminDeps,
|
||||
auth::AuthDeps,
|
||||
channels::{ChannelCommandDeps, ChannelQueryDeps},
|
||||
config::ConfigDeps,
|
||||
config_snapshots::ConfigSnapshotDeps,
|
||||
iptv::IptvDeps,
|
||||
library::{LibraryCommandDeps, LibraryQueryDeps},
|
||||
@@ -46,12 +47,23 @@ pub async fn build_app_state(config: Config) -> anyhow::Result<AppState> {
|
||||
#[cfg(feature = "auth-jwt")]
|
||||
let jwt_validator = build_jwt_validator(&config)?;
|
||||
|
||||
#[cfg(feature = "auth-jwt")]
|
||||
let token_service: Arc<dyn domain::ports::TokenService> = {
|
||||
let validator = jwt_validator
|
||||
.as_ref()
|
||||
.expect("JWT validator required")
|
||||
.as_ref()
|
||||
.clone();
|
||||
Arc::new(adapter_auth::JwtTokenService::new(validator))
|
||||
};
|
||||
|
||||
let (sync_tx, sync_rx) = tokio::sync::watch::channel(());
|
||||
|
||||
let auth_deps = Arc::new(AuthDeps {
|
||||
user_command: wire_output.user_command.clone(),
|
||||
user_query: wire_output.user_query.clone(),
|
||||
auth_service,
|
||||
token_service,
|
||||
event_publisher: event_publisher.clone(),
|
||||
});
|
||||
|
||||
@@ -105,6 +117,18 @@ pub async fn build_app_state(config: Config) -> anyhow::Result<AppState> {
|
||||
provider_config_query: wire_output.provider_config_query.clone(),
|
||||
});
|
||||
|
||||
let mut available_provider_types = Vec::new();
|
||||
#[cfg(feature = "jellyfin")]
|
||||
available_provider_types.push("jellyfin".to_string());
|
||||
#[cfg(feature = "local-files")]
|
||||
available_provider_types.push("local_files".to_string());
|
||||
|
||||
let config_deps = Arc::new(ConfigDeps {
|
||||
provider_registry: provider_registry.clone(),
|
||||
allow_registration: config.allow_registration,
|
||||
available_provider_types,
|
||||
});
|
||||
|
||||
let config_arc = Arc::new(config);
|
||||
|
||||
let bg_schedule_deps = schedule_deps.clone();
|
||||
@@ -140,6 +164,7 @@ pub async fn build_app_state(config: Config) -> anyhow::Result<AppState> {
|
||||
channel_command_deps,
|
||||
channel_query_deps,
|
||||
config_snapshot_deps,
|
||||
config_deps,
|
||||
schedule_deps,
|
||||
library_command_deps,
|
||||
library_query_deps,
|
||||
@@ -148,12 +173,11 @@ pub async fn build_app_state(config: Config) -> anyhow::Result<AppState> {
|
||||
provider_deps,
|
||||
#[cfg(feature = "auth-jwt")]
|
||||
jwt_validator,
|
||||
provider_registry,
|
||||
_library_sync: library_sync,
|
||||
_settings_repo: wire_output.settings,
|
||||
_event_bus: event_bus,
|
||||
config: config_arc,
|
||||
sync_trigger: sync_tx,
|
||||
_sync_trigger: sync_tx,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ use axum::extract::{Query, State};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use api_types::{ActivityEventResponse, ActivityLogParams, SettingsResponse};
|
||||
use application::admin::{GetActivityLogQuery, GetSettingsQuery, UpdateSettingsCommand};
|
||||
use application::admin::{GetActivityLogQuery, UpdateSettingsCommand};
|
||||
|
||||
use crate::errors::AppError;
|
||||
use crate::extractors::AdminUser;
|
||||
@@ -16,7 +16,7 @@ pub async fn get_settings(
|
||||
AdminUser(_user): AdminUser,
|
||||
) -> Result<Json<SettingsResponse>, AppError> {
|
||||
let pairs =
|
||||
application::admin::get_settings::execute(&state.admin_deps, GetSettingsQuery).await?;
|
||||
application::admin::get_settings::execute(&state.admin_deps, application::admin::GetSettingsQuery).await?;
|
||||
let settings: HashMap<String, String> = pairs.into_iter().collect();
|
||||
Ok(Json(SettingsResponse { settings }))
|
||||
}
|
||||
@@ -30,10 +30,7 @@ pub async fn update_settings(
|
||||
let cmd = UpdateSettingsCommand {
|
||||
settings: settings_vec,
|
||||
};
|
||||
application::admin::update_settings::execute(&state.admin_deps, cmd).await?;
|
||||
|
||||
let pairs =
|
||||
application::admin::get_settings::execute(&state.admin_deps, GetSettingsQuery).await?;
|
||||
let pairs = application::admin::update_settings::execute(&state.admin_deps, cmd).await?;
|
||||
let settings: HashMap<String, String> = pairs.into_iter().collect();
|
||||
Ok(Json(SettingsResponse { settings }))
|
||||
}
|
||||
|
||||
@@ -3,14 +3,12 @@ use axum::extract::State;
|
||||
|
||||
use api_types::{LoginRequest, RefreshRequest, RegisterRequest, TokenResponse, UserResponse};
|
||||
use application::auth::{LoginCommand, RegisterCommand};
|
||||
use domain::DomainError;
|
||||
|
||||
use crate::errors::AppError;
|
||||
use crate::extractors::CurrentUser;
|
||||
use crate::state::AppState;
|
||||
|
||||
const TOKEN_TYPE_BEARER: &str = "Bearer";
|
||||
const SECS_PER_HOUR: u64 = 3600;
|
||||
|
||||
pub async fn register(
|
||||
State(state): State<AppState>,
|
||||
@@ -31,14 +29,14 @@ pub async fn login(
|
||||
let cmd = LoginCommand {
|
||||
email: req.email,
|
||||
password: req.password,
|
||||
remember_me: req.remember_me,
|
||||
};
|
||||
let user = application::auth::login::execute(&state.auth_deps, cmd).await?;
|
||||
let (access_token, refresh_token) = create_tokens(&user, &state, req.remember_me)?;
|
||||
let result = application::auth::login::execute(&state.auth_deps, cmd).await?;
|
||||
Ok(Json(TokenResponse {
|
||||
access_token,
|
||||
access_token: result.access_token,
|
||||
token_type: TOKEN_TYPE_BEARER.to_string(),
|
||||
expires_in: state.config.jwt_expiry_hours * SECS_PER_HOUR,
|
||||
refresh_token,
|
||||
expires_in: result.expires_in,
|
||||
refresh_token: result.refresh_token,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -46,100 +44,21 @@ pub async fn logout() -> Result<Json<serde_json::Value>, AppError> {
|
||||
Ok(Json(serde_json::json!({"message": "logged out"})))
|
||||
}
|
||||
|
||||
pub async fn me(
|
||||
CurrentUser(user): CurrentUser,
|
||||
) -> Result<Json<UserResponse>, AppError> {
|
||||
pub async fn me(CurrentUser(user): CurrentUser) -> Result<Json<UserResponse>, AppError> {
|
||||
Ok(Json(UserResponse::from(user)))
|
||||
}
|
||||
|
||||
#[cfg(feature = "auth-jwt")]
|
||||
pub async fn get_token(
|
||||
State(state): State<AppState>,
|
||||
Json(req): Json<LoginRequest>,
|
||||
) -> Result<Json<TokenResponse>, AppError> {
|
||||
let cmd = LoginCommand {
|
||||
email: req.email,
|
||||
password: req.password,
|
||||
};
|
||||
let user = application::auth::login::execute(&state.auth_deps, cmd).await?;
|
||||
let (access_token, refresh_token) = create_tokens(&user, &state, req.remember_me)?;
|
||||
Ok(Json(TokenResponse {
|
||||
access_token,
|
||||
token_type: TOKEN_TYPE_BEARER.to_string(),
|
||||
expires_in: state.config.jwt_expiry_hours * SECS_PER_HOUR,
|
||||
refresh_token,
|
||||
}))
|
||||
}
|
||||
|
||||
#[cfg(feature = "auth-jwt")]
|
||||
pub async fn refresh_token(
|
||||
State(state): State<AppState>,
|
||||
Json(req): Json<RefreshRequest>,
|
||||
) -> Result<Json<TokenResponse>, AppError> {
|
||||
let validator = state
|
||||
.jwt_validator
|
||||
.as_ref()
|
||||
.ok_or_else(|| AppError(DomainError::InfrastructureError("JWT not configured".to_string())))?;
|
||||
|
||||
let claims = validator.validate_refresh_token(&req.refresh_token).map_err(|e| {
|
||||
tracing::debug!("Refresh token validation failed: {:?}", e);
|
||||
AppError(DomainError::Unauthenticated("Invalid refresh token".to_string()))
|
||||
})?;
|
||||
|
||||
let user_id: uuid::Uuid = claims
|
||||
.sub
|
||||
.parse()
|
||||
.map_err(|_| AppError(DomainError::Unauthenticated("Invalid user ID in token".to_string())))?;
|
||||
|
||||
let user = state
|
||||
.auth_deps
|
||||
.user_query
|
||||
.find_by_id(domain::UserId::from(user_id))
|
||||
.await
|
||||
.map_err(|e| AppError(DomainError::InfrastructureError(format!("Failed to fetch user: {}", e))))?
|
||||
.ok_or_else(|| AppError(DomainError::Unauthenticated("User not found".to_string())))?;
|
||||
|
||||
let (access_token, refresh_token) = create_tokens(&user, &state, true)?;
|
||||
let result =
|
||||
application::auth::refresh::execute(&state.auth_deps, req.refresh_token).await?;
|
||||
Ok(Json(TokenResponse {
|
||||
access_token,
|
||||
access_token: result.access_token,
|
||||
token_type: TOKEN_TYPE_BEARER.to_string(),
|
||||
expires_in: state.config.jwt_expiry_hours * SECS_PER_HOUR,
|
||||
refresh_token,
|
||||
expires_in: result.expires_in,
|
||||
refresh_token: result.refresh_token,
|
||||
}))
|
||||
}
|
||||
|
||||
fn create_tokens(
|
||||
user: &domain::User,
|
||||
state: &AppState,
|
||||
remember_me: bool,
|
||||
) -> Result<(String, Option<String>), AppError> {
|
||||
#[cfg(feature = "auth-jwt")]
|
||||
{
|
||||
let validator = state
|
||||
.jwt_validator
|
||||
.as_ref()
|
||||
.ok_or_else(|| AppError(DomainError::InfrastructureError("JWT not configured".to_string())))?;
|
||||
|
||||
let access = validator
|
||||
.create_token(user)
|
||||
.map_err(|e| AppError(DomainError::InfrastructureError(format!("Failed to create token: {}", e))))?;
|
||||
|
||||
let refresh = if remember_me {
|
||||
Some(
|
||||
validator
|
||||
.create_refresh_token(user)
|
||||
.map_err(|e| AppError(DomainError::InfrastructureError(format!("Failed to create refresh token: {}", e))))?,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok((access, refresh))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "auth-jwt"))]
|
||||
{
|
||||
let _ = (user, state, remember_me);
|
||||
Err(AppError(DomainError::InfrastructureError("JWT feature not enabled".to_string())))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,30 +74,14 @@ pub async fn update_channel(
|
||||
Path(id): Path<uuid::Uuid>,
|
||||
Json(req): Json<UpdateChannelRequest>,
|
||||
) -> Result<Json<ChannelResponse>, AppError> {
|
||||
let schedule_config = req
|
||||
.schedule_config
|
||||
.map(|v| {
|
||||
serde_json::from_value(v)
|
||||
.map_err(|e| AppError(DomainError::ValidationError(format!("Invalid schedule_config: {e}"))))
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let recycle_policy = req
|
||||
.recycle_policy
|
||||
.map(|v| {
|
||||
serde_json::from_value(v)
|
||||
.map_err(|e| AppError(DomainError::ValidationError(format!("Invalid recycle_policy: {e}"))))
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let cmd = UpdateChannelCommand {
|
||||
channel_id: id.into(),
|
||||
owner_id: user.id(),
|
||||
name: req.name,
|
||||
description: req.description.map(Some),
|
||||
timezone: req.timezone,
|
||||
schedule_config,
|
||||
recycle_policy,
|
||||
schedule_config: req.schedule_config.map(Into::into),
|
||||
recycle_policy: req.recycle_policy,
|
||||
auto_schedule: req.auto_schedule,
|
||||
};
|
||||
let channel = application::channels::update::execute(&state.channel_command_deps, cmd).await?;
|
||||
|
||||
@@ -2,54 +2,28 @@ use axum::Json;
|
||||
use axum::extract::State;
|
||||
|
||||
use api_types::{ConfigResponse, ProviderCapabilitiesResponse, ProviderInfo};
|
||||
use application::config::GetConfigQuery;
|
||||
|
||||
use crate::errors::AppError;
|
||||
use crate::state::AppState;
|
||||
|
||||
const FALLBACK_STREAMING_PROTOCOL: &str = "direct_file";
|
||||
|
||||
pub async fn get_config(
|
||||
State(state): State<AppState>,
|
||||
) -> Result<Json<ConfigResponse>, AppError> {
|
||||
let registry = &state.provider_registry;
|
||||
let provider_ids = registry.provider_ids();
|
||||
let primary_id = registry.primary_id().to_string();
|
||||
|
||||
let providers: Vec<ProviderInfo> = provider_ids
|
||||
.iter()
|
||||
.filter_map(|id| {
|
||||
registry.capabilities(id).map(|caps| ProviderInfo {
|
||||
id: id.clone(),
|
||||
capabilities: ProviderCapabilitiesResponse::from(caps),
|
||||
})
|
||||
let sys_config = application::config::get_config::execute(&state.config_deps, GetConfigQuery);
|
||||
let providers: Vec<ProviderInfo> = sys_config
|
||||
.providers
|
||||
.into_iter()
|
||||
.map(|p| ProviderInfo {
|
||||
id: p.id,
|
||||
capabilities: ProviderCapabilitiesResponse::from(p.capabilities),
|
||||
})
|
||||
.collect();
|
||||
|
||||
let primary_caps = registry
|
||||
.capabilities(&primary_id)
|
||||
.map(ProviderCapabilitiesResponse::from)
|
||||
.unwrap_or(ProviderCapabilitiesResponse {
|
||||
collections: false,
|
||||
series: false,
|
||||
genres: false,
|
||||
tags: false,
|
||||
decade: false,
|
||||
search: false,
|
||||
streaming_protocol: FALLBACK_STREAMING_PROTOCOL.to_string(),
|
||||
rescan: false,
|
||||
transcode: false,
|
||||
});
|
||||
|
||||
let mut available_types = Vec::new();
|
||||
#[cfg(feature = "jellyfin")]
|
||||
available_types.push("jellyfin".to_string());
|
||||
#[cfg(feature = "local-files")]
|
||||
available_types.push("local_files".to_string());
|
||||
|
||||
let primary_caps = ProviderCapabilitiesResponse::from(sys_config.primary_capabilities);
|
||||
Ok(Json(ConfigResponse {
|
||||
allow_registration: state.config.allow_registration,
|
||||
allow_registration: sys_config.allow_registration,
|
||||
providers,
|
||||
provider_capabilities: primary_caps,
|
||||
available_provider_types: available_types,
|
||||
available_provider_types: sys_config.available_provider_types,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use axum::Json;
|
||||
use axum::extract::{Path, Query, State};
|
||||
use serde::Serialize;
|
||||
|
||||
use api_types::{
|
||||
CollectionResponse, GenresParams, LibraryItemResponse, LibrarySearchParams, PaginatedResponse,
|
||||
ProviderParam, SeasonResponse, SeasonsParams, ShowResponse, ShowsParams,
|
||||
ProviderParam, SeasonResponse, SeasonsParams, ShowResponse, ShowsParams, SyncStatusEntry,
|
||||
};
|
||||
use application::library::{
|
||||
GetItemQuery, GetSyncStatusQuery, ListCollectionsQuery, ListGenresQuery, ListSeasonsQuery,
|
||||
@@ -116,16 +115,6 @@ pub async fn list_genres(
|
||||
Ok(Json(genres))
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub(crate) struct SyncStatusEntry {
|
||||
provider_id: String,
|
||||
started_at: String,
|
||||
finished_at: String,
|
||||
items_found: u32,
|
||||
status: String,
|
||||
error_msg: String,
|
||||
}
|
||||
|
||||
pub async fn sync_status(
|
||||
State(state): State<AppState>,
|
||||
CurrentUser(_user): CurrentUser,
|
||||
@@ -133,18 +122,7 @@ pub async fn sync_status(
|
||||
let entries =
|
||||
application::library::get_sync_status::execute(&state.library_query_deps, GetSyncStatusQuery)
|
||||
.await?;
|
||||
let result: Vec<SyncStatusEntry> = entries
|
||||
.into_iter()
|
||||
.map(|e| SyncStatusEntry {
|
||||
provider_id: e.provider_id().to_string(),
|
||||
started_at: e.started_at().to_string(),
|
||||
finished_at: e.finished_at().unwrap_or("").to_string(),
|
||||
items_found: e.items_found(),
|
||||
status: e.status().to_string(),
|
||||
error_msg: e.error_msg().unwrap_or("").to_string(),
|
||||
})
|
||||
.collect();
|
||||
Ok(Json(result))
|
||||
Ok(Json(entries.into_iter().map(SyncStatusEntry::from).collect()))
|
||||
}
|
||||
|
||||
pub async fn trigger_sync(
|
||||
@@ -152,17 +130,6 @@ pub async fn trigger_sync(
|
||||
AdminUser(_user): AdminUser,
|
||||
) -> Result<axum::http::StatusCode, AppError> {
|
||||
let cmd = TriggerSyncCommand { provider_id: None };
|
||||
application::library::sync::execute(&state.library_command_deps, cmd)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
if e.to_string().contains("already running") {
|
||||
AppError(DomainError::Conflict(e.to_string()))
|
||||
} else {
|
||||
AppError::from(e)
|
||||
}
|
||||
})?;
|
||||
|
||||
let _ = state.sync_trigger.send(());
|
||||
|
||||
application::library::sync::execute(&state.library_command_deps, cmd).await?;
|
||||
Ok(axum::http::StatusCode::ACCEPTED)
|
||||
}
|
||||
|
||||
@@ -43,13 +43,10 @@ pub async fn upsert_provider(
|
||||
Path(id): Path<String>,
|
||||
Json(req): Json<ProviderConfigRequest>,
|
||||
) -> Result<Json<serde_json::Value>, AppError> {
|
||||
let config_json = serde_json::to_string(&req.config)
|
||||
.map_err(|e| AppError(DomainError::ValidationError(format!("Invalid config JSON: {e}"))))?;
|
||||
|
||||
let cmd = UpsertProviderCommand {
|
||||
id,
|
||||
provider_type: req.provider_type,
|
||||
config_json,
|
||||
config: req.config,
|
||||
enabled: req.enabled,
|
||||
};
|
||||
application::providers::upsert::execute(&state.provider_deps, cmd).await?;
|
||||
|
||||
@@ -44,20 +44,15 @@ pub async fn get_current_broadcast(
|
||||
let query = GetCurrentBroadcastQuery { channel_id: id };
|
||||
match application::schedule::get_current_broadcast::execute(&state.schedule_deps, query).await?
|
||||
{
|
||||
Some(broadcast) => {
|
||||
let channel_query = application::channels::GetChannelQuery { channel_id: id.into() };
|
||||
let channel = application::channels::get::execute(&state.channel_query_deps, channel_query).await?;
|
||||
|
||||
let slot_response = match &channel {
|
||||
Some(ch) => SlotResponse::with_block_access(broadcast.slot().clone(), ch),
|
||||
None => SlotResponse::from(broadcast.slot().clone()),
|
||||
Some(result) => {
|
||||
let slot_response = match &result.channel {
|
||||
Some(ch) => SlotResponse::with_block_access(result.broadcast.slot().clone(), ch),
|
||||
None => SlotResponse::from(result.broadcast.slot().clone()),
|
||||
};
|
||||
|
||||
let block_access_mode = slot_response.block_access_mode.clone();
|
||||
|
||||
Ok(Json(CurrentBroadcastResponse {
|
||||
slot: slot_response,
|
||||
offset_secs: broadcast.offset_secs(),
|
||||
offset_secs: result.broadcast.offset_secs(),
|
||||
block_access_mode,
|
||||
})
|
||||
.into_response())
|
||||
@@ -79,26 +74,13 @@ pub async fn get_stream(
|
||||
State(state): State<AppState>,
|
||||
Path(id): Path<uuid::Uuid>,
|
||||
) -> Result<axum::response::Response, AppError> {
|
||||
let broadcast_query = GetCurrentBroadcastQuery { channel_id: id };
|
||||
let broadcast =
|
||||
application::schedule::get_current_broadcast::execute(&state.schedule_deps, broadcast_query)
|
||||
.await?;
|
||||
|
||||
match broadcast {
|
||||
Some(b) => {
|
||||
let stream_query = GetStreamUrlQuery {
|
||||
channel_id: id,
|
||||
item_id: b.slot().item().id().value().to_string(),
|
||||
};
|
||||
let url =
|
||||
application::schedule::get_stream_url::execute(&state.schedule_deps, stream_query)
|
||||
.await?;
|
||||
Ok((
|
||||
StatusCode::TEMPORARY_REDIRECT,
|
||||
[("Location", url.as_str())],
|
||||
)
|
||||
.into_response())
|
||||
}
|
||||
let query = GetStreamUrlQuery { channel_id: id };
|
||||
match application::schedule::get_stream_url::execute(&state.schedule_deps, query).await? {
|
||||
Some(url) => Ok((
|
||||
StatusCode::TEMPORARY_REDIRECT,
|
||||
[("Location", url.as_str())],
|
||||
)
|
||||
.into_response()),
|
||||
None => Ok(StatusCode::NO_CONTENT.into_response()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ fn auth_router() -> Router<AppState> {
|
||||
|
||||
#[cfg(feature = "auth-jwt")]
|
||||
let r = r
|
||||
.route("/token", post(handlers::auth::get_token))
|
||||
.route("/token", post(handlers::auth::login))
|
||||
.route("/refresh", post(handlers::auth::refresh_token));
|
||||
|
||||
r
|
||||
|
||||
@@ -4,6 +4,7 @@ use application::{
|
||||
admin::AdminDeps,
|
||||
auth::AuthDeps,
|
||||
channels::{ChannelCommandDeps, ChannelQueryDeps},
|
||||
config::ConfigDeps,
|
||||
config_snapshots::ConfigSnapshotDeps,
|
||||
iptv::IptvDeps,
|
||||
library::{LibraryCommandDeps, LibraryQueryDeps},
|
||||
@@ -17,6 +18,7 @@ pub struct AppState {
|
||||
pub channel_command_deps: Arc<ChannelCommandDeps>,
|
||||
pub channel_query_deps: Arc<ChannelQueryDeps>,
|
||||
pub config_snapshot_deps: Arc<ConfigSnapshotDeps>,
|
||||
pub config_deps: Arc<ConfigDeps>,
|
||||
pub schedule_deps: Arc<ScheduleDeps>,
|
||||
pub library_command_deps: Arc<LibraryCommandDeps>,
|
||||
pub library_query_deps: Arc<LibraryQueryDeps>,
|
||||
@@ -27,13 +29,10 @@ pub struct AppState {
|
||||
#[cfg(feature = "auth-jwt")]
|
||||
pub jwt_validator: Option<Arc<adapter_auth::JwtValidator>>,
|
||||
|
||||
pub provider_registry: Arc<dyn domain::ports::IProviderRegistry>,
|
||||
|
||||
// kept alive for background tasks — not read from handlers
|
||||
pub _library_sync: Arc<dyn domain::ports::LibrarySyncAdapter>,
|
||||
pub _settings_repo: Arc<dyn domain::ports::AppSettingsRepository>,
|
||||
pub _event_bus: Arc<adapter_event_publisher::ChannelEventBus>,
|
||||
|
||||
pub config: Arc<infra_wiring::Config>,
|
||||
pub sync_trigger: tokio::sync::watch::Sender<()>,
|
||||
pub _sync_trigger: tokio::sync::watch::Sender<()>,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user